Skip to content

Commit af5e022

Browse files
Add bloq for constant polynomial multiplication modulu in GF(2)
1 parent c84bca1 commit af5e022

File tree

4 files changed

+286
-13
lines changed

4 files changed

+286
-13
lines changed

dev_tools/qualtran_dev_tools/notebook_specs.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,10 @@
558558
NotebookSpecV2(
559559
title='GF($2^m$) Multiplication',
560560
module=qualtran.bloqs.gf_arithmetic.gf2_multiplication,
561-
bloq_specs=[qualtran.bloqs.gf_arithmetic.gf2_multiplication._GF2_MULTIPLICATION_DOC],
561+
bloq_specs=[
562+
qualtran.bloqs.gf_arithmetic.gf2_multiplication._GF2_MULTIPLICATION_DOC,
563+
qualtran.bloqs.gf_arithmetic.gf2_multiplication._MULTIPLY_BY_CONSTANT_MOD_DOC,
564+
],
562565
),
563566
NotebookSpecV2(
564567
title='GF($2^m$) Addition',

qualtran/bloqs/gf_arithmetic/gf2_multiplication.ipynb

+118-11
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"cells": [
33
{
44
"cell_type": "markdown",
5-
"id": "87c95c4a",
5+
"id": "acbb10a4",
66
"metadata": {
77
"cq.autogen": "title_cell"
88
},
@@ -13,7 +13,7 @@
1313
{
1414
"cell_type": "code",
1515
"execution_count": null,
16-
"id": "31c1f087",
16+
"id": "fd0e976a",
1717
"metadata": {
1818
"cq.autogen": "top_imports"
1919
},
@@ -30,7 +30,7 @@
3030
},
3131
{
3232
"cell_type": "markdown",
33-
"id": "307679ec",
33+
"id": "8bfc1e7d",
3434
"metadata": {
3535
"cq.autogen": "GF2Multiplication.bloq_doc.md"
3636
},
@@ -72,7 +72,7 @@
7272
{
7373
"cell_type": "code",
7474
"execution_count": null,
75-
"id": "872a44d1",
75+
"id": "d1193813",
7676
"metadata": {
7777
"cq.autogen": "GF2Multiplication.bloq_doc.py"
7878
},
@@ -83,7 +83,7 @@
8383
},
8484
{
8585
"cell_type": "markdown",
86-
"id": "d0f0db7d",
86+
"id": "4b13e0a3",
8787
"metadata": {
8888
"cq.autogen": "GF2Multiplication.example_instances.md"
8989
},
@@ -94,7 +94,7 @@
9494
{
9595
"cell_type": "code",
9696
"execution_count": null,
97-
"id": "131bc962",
97+
"id": "20cab892",
9898
"metadata": {
9999
"cq.autogen": "GF2Multiplication.gf16_multiplication"
100100
},
@@ -106,7 +106,7 @@
106106
{
107107
"cell_type": "code",
108108
"execution_count": null,
109-
"id": "69f564d8",
109+
"id": "06f44b6e",
110110
"metadata": {
111111
"cq.autogen": "GF2Multiplication.gf2_multiplication_symbolic"
112112
},
@@ -120,7 +120,7 @@
120120
},
121121
{
122122
"cell_type": "markdown",
123-
"id": "2a62c2b8",
123+
"id": "9b96d200",
124124
"metadata": {
125125
"cq.autogen": "GF2Multiplication.graphical_signature.md"
126126
},
@@ -131,7 +131,7 @@
131131
{
132132
"cell_type": "code",
133133
"execution_count": null,
134-
"id": "cf003e98",
134+
"id": "3c5ef2f6",
135135
"metadata": {
136136
"cq.autogen": "GF2Multiplication.graphical_signature.py"
137137
},
@@ -144,7 +144,7 @@
144144
},
145145
{
146146
"cell_type": "markdown",
147-
"id": "f14ef0c5",
147+
"id": "e6a3dc52",
148148
"metadata": {
149149
"cq.autogen": "GF2Multiplication.call_graph.md"
150150
},
@@ -155,7 +155,7 @@
155155
{
156156
"cell_type": "code",
157157
"execution_count": null,
158-
"id": "f4b7bf2c",
158+
"id": "e3b95e04",
159159
"metadata": {
160160
"cq.autogen": "GF2Multiplication.call_graph.py"
161161
},
@@ -166,6 +166,113 @@
166166
"show_call_graph(gf16_multiplication_g)\n",
167167
"show_counts_sigma(gf16_multiplication_sigma)"
168168
]
169+
},
170+
{
171+
"cell_type": "markdown",
172+
"id": "8e1b2599",
173+
"metadata": {
174+
"cq.autogen": "MultiplyPolyByConstantMod.bloq_doc.md"
175+
},
176+
"source": [
177+
"## `MultiplyPolyByConstantMod`\n",
178+
"Multiply a polynomial by $f(x)$ modulu $m(x)$. Both $f(x)$ and $m(x)$ are constants.\n",
179+
"\n",
180+
"#### Parameters\n",
181+
" - `f_x`: The polynomial to mulitply with, given either a galois.Poly or as a sequence degrees.\n",
182+
" - `m_x`: The modulus polynomial, given either a galois.Poly or as a sequence degrees. \n",
183+
"\n",
184+
"#### Registers\n",
185+
" - `g`: The polynomial coefficients (in GF(2)). \n",
186+
"\n",
187+
"Regerences:\n",
188+
" - [Space-efficient quantum multiplication of polynomials for binary finite fields with\n",
189+
" sub-quadratic Toffoli gate count](https://arxiv.org/abs/1910.02849v2) Algorithm 1"
190+
]
191+
},
192+
{
193+
"cell_type": "code",
194+
"execution_count": null,
195+
"id": "f48b16b4",
196+
"metadata": {
197+
"cq.autogen": "MultiplyPolyByConstantMod.bloq_doc.py"
198+
},
199+
"outputs": [],
200+
"source": [
201+
"from qualtran.bloqs.gf_arithmetic import MultiplyPolyByConstantMod"
202+
]
203+
},
204+
{
205+
"cell_type": "markdown",
206+
"id": "f71cbb55",
207+
"metadata": {
208+
"cq.autogen": "MultiplyPolyByConstantMod.example_instances.md"
209+
},
210+
"source": [
211+
"### Example Instances"
212+
]
213+
},
214+
{
215+
"cell_type": "code",
216+
"execution_count": null,
217+
"id": "cb34d39c",
218+
"metadata": {
219+
"cq.autogen": "MultiplyPolyByConstantMod.gf2_multiply_by_constant_modulu"
220+
},
221+
"outputs": [],
222+
"source": [
223+
"fx = [2, 0] # x^2 + 1\n",
224+
"mx = [0, 1, 3] # x^3 + x + 1\n",
225+
"gf2_multiply_by_constant_modulu = MultiplyPolyByConstantMod(fx, mx)"
226+
]
227+
},
228+
{
229+
"cell_type": "markdown",
230+
"id": "413907e4",
231+
"metadata": {
232+
"cq.autogen": "MultiplyPolyByConstantMod.graphical_signature.md"
233+
},
234+
"source": [
235+
"#### Graphical Signature"
236+
]
237+
},
238+
{
239+
"cell_type": "code",
240+
"execution_count": null,
241+
"id": "ffce6a92",
242+
"metadata": {
243+
"cq.autogen": "MultiplyPolyByConstantMod.graphical_signature.py"
244+
},
245+
"outputs": [],
246+
"source": [
247+
"from qualtran.drawing import show_bloqs\n",
248+
"show_bloqs([gf2_multiply_by_constant_modulu],\n",
249+
" ['`gf2_multiply_by_constant_modulu`'])"
250+
]
251+
},
252+
{
253+
"cell_type": "markdown",
254+
"id": "703fa7bf",
255+
"metadata": {
256+
"cq.autogen": "MultiplyPolyByConstantMod.call_graph.md"
257+
},
258+
"source": [
259+
"### Call Graph"
260+
]
261+
},
262+
{
263+
"cell_type": "code",
264+
"execution_count": null,
265+
"id": "2d8b870f",
266+
"metadata": {
267+
"cq.autogen": "MultiplyPolyByConstantMod.call_graph.py"
268+
},
269+
"outputs": [],
270+
"source": [
271+
"from qualtran.resource_counting.generalizers import ignore_split_join\n",
272+
"gf2_multiply_by_constant_modulu_g, gf2_multiply_by_constant_modulu_sigma = gf2_multiply_by_constant_modulu.call_graph(max_depth=1, generalizer=ignore_split_join)\n",
273+
"show_call_graph(gf2_multiply_by_constant_modulu_g)\n",
274+
"show_counts_sigma(gf2_multiply_by_constant_modulu_sigma)"
275+
]
169276
}
170277
],
171278
"metadata": {

qualtran/bloqs/gf_arithmetic/gf2_multiplication.py

+103
Original file line numberDiff line numberDiff line change
@@ -247,3 +247,106 @@ def _gf2_multiplication_symbolic() -> GF2Multiplication:
247247
_GF2_MULTIPLICATION_DOC = BloqDocSpec(
248248
bloq_cls=GF2Multiplication, examples=(_gf16_multiplication, _gf2_multiplication_symbolic)
249249
)
250+
251+
252+
@attrs.frozen
253+
class MultiplyPolyByConstantMod(Bloq):
254+
r"""Multiply a polynomial by $f(x)$ modulu $m(x)$. Both $f(x)$ and $m(x)$ are constants.
255+
256+
Args:
257+
f_x: The polynomial to mulitply with, given either a galois.Poly or as
258+
a sequence degrees.
259+
m_x: The modulus polynomial, given either a galois.Poly or as
260+
a sequence degrees.
261+
262+
Registers:
263+
g: The polynomial coefficients (in GF(2)).
264+
265+
Regerences:
266+
- [Space-efficient quantum multiplication of polynomials for binary finite fields with
267+
sub-quadratic Toffoli gate count](https://arxiv.org/abs/1910.02849v2) Algorithm 1
268+
"""
269+
270+
f_x: Poly = attrs.field(converter=lambda x: x if isinstance(x, Poly) else Poly.Degrees(x))
271+
m_x: Poly = attrs.field(converter=lambda x: x if isinstance(x, Poly) else Poly.Degrees(x))
272+
273+
def __attrs_post_init__(self):
274+
assert self.m_x.is_irreducible()
275+
assert self.f_x.degrees.max() < self.m_x.degrees.max()
276+
277+
@cached_property
278+
def n(self):
279+
return self.m_x.degrees.max()
280+
281+
@cached_property
282+
def lup(self) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
283+
"""Returns the LUP decomposition of the matrix representing the operation.
284+
285+
If m_x is irreducible, then the operation y := (y*f_x)%m_x can be represented
286+
by a full rank matrix that can be decomposed into PLU where L and U are lower
287+
and upper traingular matricies and P is a permutation matrix.
288+
"""
289+
n = self.n
290+
matrix = np.zeros((n, n), dtype=int)
291+
for i in range(n):
292+
p = (self.f_x * Poly.Degrees([i])) % self.m_x
293+
for j in p.nonzero_degrees:
294+
matrix[j, i] = 1
295+
P, L, U = GF(2)(matrix).plu_decompose()
296+
return np.asarray(L, dtype=int), np.asarray(U, dtype=int), np.asarray(P, dtype=int)
297+
298+
@cached_property
299+
def signature(self) -> 'Signature':
300+
return Signature([Register('g', QBit(), shape=(self.n,))])
301+
302+
def on_classical_vals(self, g) -> Dict[str, 'ClassicalValT']:
303+
p = (Poly(g[::-1], GF(2)) * self.f_x) % self.m_x
304+
res = p.coefficients().tolist()
305+
res = [0 for _ in range(self.n - len(res))] + res
306+
res = res[::-1]
307+
return {'g': res}
308+
309+
def build_composite_bloq(self, bb: 'BloqBuilder', g: 'Soquet') -> Dict[str, 'Soquet']:
310+
L, U, P = self.lup
311+
if is_symbolic(self.n):
312+
raise DecomposeTypeError(f"Symbolic decomposition isn't supported for {self}")
313+
for i in range(self.n):
314+
for j in range(i + 1, self.n):
315+
if U[i, j]:
316+
g[j], g[i] = bb.add(CNOT(), ctrl=g[j], target=g[i])
317+
318+
for i in reversed(range(self.n)):
319+
for j in reversed(range(i)):
320+
if L[i, j]:
321+
g[j], g[i] = bb.add(CNOT(), ctrl=g[j], target=g[i])
322+
323+
column = [*range(self.n)]
324+
for i in range(self.n):
325+
for j in range(i + 1, self.n):
326+
if P[i, column[j]]:
327+
g[i], g[j] = g[j], g[i]
328+
column[i], column[j] = column[j], column[i]
329+
return {'g': g}
330+
331+
def build_call_graph(
332+
self, ssa: 'SympySymbolAllocator'
333+
) -> Union['BloqCountDictT', Set['BloqCountT']]:
334+
L, U, _ = self.lup
335+
# The number of cnots is the number of non zero off-diagnoal entries in L and U.
336+
cnots = np.sum(L) + np.sum(U) - 2 * self.n
337+
if cnots:
338+
return {CNOT(): cnots}
339+
return {}
340+
341+
342+
@bloq_example
343+
def _gf2_multiply_by_constant_modulu() -> MultiplyPolyByConstantMod:
344+
fx = [2, 0] # x^2 + 1
345+
mx = [0, 1, 3] # x^3 + x + 1
346+
gf2_multiply_by_constant_modulu = MultiplyPolyByConstantMod(fx, mx)
347+
return gf2_multiply_by_constant_modulu
348+
349+
350+
_MULTIPLY_BY_CONSTANT_MOD_DOC = BloqDocSpec(
351+
bloq_cls=MultiplyPolyByConstantMod, examples=(_gf2_multiply_by_constant_modulu,)
352+
)

0 commit comments

Comments
 (0)