Skip to content

Commit 54d8d0b

Browse files
committed
Add GF2PolyAddK, GFPolySplit and GFPolyJoin bloqs
1 parent c88c7bb commit 54d8d0b

10 files changed

Lines changed: 912 additions & 0 deletions

File tree

dev_tools/qualtran_dev_tools/notebook_specs.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@
9292
import qualtran.bloqs.gf_arithmetic.gf2_inverse
9393
import qualtran.bloqs.gf_arithmetic.gf2_multiplication
9494
import qualtran.bloqs.gf_arithmetic.gf2_square
95+
import qualtran.bloqs.gf_poly_arithmetic.gf2_poly_add_k
96+
import qualtran.bloqs.gf_poly_arithmetic.gf_poly_split_and_join
9597
import qualtran.bloqs.hamiltonian_simulation.hamiltonian_simulation_by_gqsp
9698
import qualtran.bloqs.mcmt.and_bloq
9799
import qualtran.bloqs.mcmt.controlled_via_and
@@ -604,6 +606,24 @@
604606
),
605607
]
606608

609+
GF_POLY_ARITHMETIC = [
610+
# --------------------------------------------------------------------------
611+
# ----- Polynomials defined over Galois Fields (GF) Arithmetic --------
612+
# --------------------------------------------------------------------------
613+
NotebookSpecV2(
614+
title='GF Polynomials Split and Join',
615+
module=qualtran.bloqs.gf_poly_arithmetic.gf_poly_split_and_join,
616+
bloq_specs=[
617+
qualtran.bloqs.gf_poly_arithmetic.gf_poly_split_and_join._GF_POLY_SPLIT_DOC,
618+
qualtran.bloqs.gf_poly_arithmetic.gf_poly_split_and_join._GF_POLY_JOIN_DOC,
619+
],
620+
),
621+
NotebookSpecV2(
622+
title='GF($2^m$) Polynomials Add Constant',
623+
module=qualtran.bloqs.gf_poly_arithmetic.gf2_poly_add_k,
624+
bloq_specs=[qualtran.bloqs.gf_poly_arithmetic.gf2_poly_add_k._GF2_ADD_K_DOC],
625+
),
626+
]
607627

608628
ROT_QFT_PE = [
609629
# --------------------------------------------------------------------------
@@ -935,6 +955,7 @@
935955
('Arithmetic', ARITHMETIC),
936956
('Modular Arithmetic', MOD_ARITHMETIC),
937957
('GF Arithmetic', GF_ARITHMETIC),
958+
('Polynomials over Galois Fields', GF_POLY_ARITHMETIC),
938959
('Rotations', ROT_QFT_PE),
939960
('Block Encoding', BLOCK_ENCODING),
940961
('Optimization', OPTIMIZATION),

docs/bloqs/index.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,13 @@ Bloqs Library
105105
gf_arithmetic/gf2_square.ipynb
106106
gf_arithmetic/gf2_inverse.ipynb
107107

108+
.. toctree::
109+
:maxdepth: 2
110+
:caption: Polynomials over Galois Fields:
111+
112+
gf_poly_arithmetic/gf_poly_split_and_join.ipynb
113+
gf_poly_arithmetic/gf2_poly_add_k.ipynb
114+
108115
.. toctree::
109116
:maxdepth: 2
110117
:caption: Rotations:

qualtran/bloqs/gf_poly_arithmetic/__init__.py

Whitespace-only changes.
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "3d480a1d",
6+
"metadata": {
7+
"cq.autogen": "title_cell"
8+
},
9+
"source": [
10+
"# GF($2^m$) Polynomials Add Constant"
11+
]
12+
},
13+
{
14+
"cell_type": "code",
15+
"execution_count": null,
16+
"id": "218a8310",
17+
"metadata": {
18+
"cq.autogen": "top_imports"
19+
},
20+
"outputs": [],
21+
"source": [
22+
"from qualtran import Bloq, CompositeBloq, BloqBuilder, Signature, Register\n",
23+
"from qualtran import QBit, QInt, QUInt, QAny\n",
24+
"from qualtran.drawing import show_bloq, show_call_graph, show_counts_sigma\n",
25+
"from typing import *\n",
26+
"import numpy as np\n",
27+
"import sympy\n",
28+
"import cirq"
29+
]
30+
},
31+
{
32+
"cell_type": "markdown",
33+
"id": "203cfaf0",
34+
"metadata": {
35+
"cq.autogen": "GF2AddK.bloq_doc.md"
36+
},
37+
"source": [
38+
"## `GF2AddK`\n",
39+
"In place addition of a constant $k$ for elements in GF($2^m$).\n",
40+
"\n",
41+
"The bloq implements in place addition of a classical constant $k$ and a quantum register\n",
42+
"$|x\\rangle$ storing elements from GF($2^m$). Addition in GF($2^m$) simply reduces to a component\n",
43+
"wise XOR, which can be implemented via X gates.\n",
44+
"\n",
45+
"$$\n",
46+
"|x\\rangle \\rightarrow |x + k\\rangle\n",
47+
"$$\n",
48+
"\n",
49+
"#### Parameters\n",
50+
" - `bitsize`: The degree $m$ of the galois field GF($2^m$). Also corresponds to the number of qubits in the input register x.\n",
51+
" - `k`: Integer representation of constant over GF($2^m$) that should be added to the input register x. \n",
52+
"\n",
53+
"#### Registers\n",
54+
" - `x`: Input THRU register of size $m$ that stores elements from $GF(2^m)$.\n"
55+
]
56+
},
57+
{
58+
"cell_type": "code",
59+
"execution_count": null,
60+
"id": "d52372c6",
61+
"metadata": {
62+
"cq.autogen": "GF2AddK.bloq_doc.py"
63+
},
64+
"outputs": [],
65+
"source": [
66+
"from qualtran.bloqs.gf_arithmetic import GF2AddK"
67+
]
68+
},
69+
{
70+
"cell_type": "markdown",
71+
"id": "1b73fd35",
72+
"metadata": {
73+
"cq.autogen": "GF2AddK.example_instances.md"
74+
},
75+
"source": [
76+
"### Example Instances"
77+
]
78+
},
79+
{
80+
"cell_type": "code",
81+
"execution_count": null,
82+
"id": "ab076857",
83+
"metadata": {
84+
"cq.autogen": "GF2AddK.gf2_poly_4_8_add_k"
85+
},
86+
"outputs": [],
87+
"source": [
88+
"from galois import Poly\n",
89+
"from qualtran import QGF, QGFPoly\n",
90+
"\n",
91+
"qgf_poly = QGFPoly(4, QGF(2, 3))\n",
92+
"g_x = Poly(qgf_poly.qgf.gf_type([1, 2, 3, 4, 5]))\n",
93+
"gf2_poly_4_8_add_k = GF2PolyAddK(qgf_poly, g_x)"
94+
]
95+
},
96+
{
97+
"cell_type": "code",
98+
"execution_count": null,
99+
"id": "991e90de",
100+
"metadata": {
101+
"cq.autogen": "GF2AddK.gf2_poly_add_k_symbolic"
102+
},
103+
"outputs": [],
104+
"source": [
105+
"import sympy\n",
106+
"from galois import Poly\n",
107+
"from qualtran import QGF, QGFPoly\n",
108+
"\n",
109+
"n, m = sympy.symbols('n, m', positive=True, integers=True)\n",
110+
"qgf_poly = QGFPoly(n, QGF(2, m))\n",
111+
"gf2_poly_symbolic_add_k = GF2PolyAddK(qgf_poly, Poly([0, 0, 0, 0]))"
112+
]
113+
},
114+
{
115+
"cell_type": "markdown",
116+
"id": "6a3ecb1e",
117+
"metadata": {
118+
"cq.autogen": "GF2AddK.graphical_signature.md"
119+
},
120+
"source": [
121+
"#### Graphical Signature"
122+
]
123+
},
124+
{
125+
"cell_type": "code",
126+
"execution_count": null,
127+
"id": "32a100a5",
128+
"metadata": {
129+
"cq.autogen": "GF2AddK.graphical_signature.py"
130+
},
131+
"outputs": [],
132+
"source": [
133+
"from qualtran.drawing import show_bloqs\n",
134+
"show_bloqs([gf2_poly_4_8_add_k, gf2_poly_add_k_symbolic],\n",
135+
" ['`gf2_poly_4_8_add_k`', '`gf2_poly_add_k_symbolic`'])"
136+
]
137+
},
138+
{
139+
"cell_type": "markdown",
140+
"id": "3cd8496b",
141+
"metadata": {
142+
"cq.autogen": "GF2AddK.call_graph.md"
143+
},
144+
"source": [
145+
"### Call Graph"
146+
]
147+
},
148+
{
149+
"cell_type": "code",
150+
"execution_count": null,
151+
"id": "605f81e0",
152+
"metadata": {
153+
"cq.autogen": "GF2AddK.call_graph.py"
154+
},
155+
"outputs": [],
156+
"source": [
157+
"from qualtran.resource_counting.generalizers import ignore_split_join\n",
158+
"gf2_poly_4_8_add_k_g, gf2_poly_4_8_add_k_sigma = gf2_poly_4_8_add_k.call_graph(max_depth=1, generalizer=ignore_split_join)\n",
159+
"show_call_graph(gf2_poly_4_8_add_k_g)\n",
160+
"show_counts_sigma(gf2_poly_4_8_add_k_sigma)"
161+
]
162+
}
163+
],
164+
"metadata": {
165+
"kernelspec": {
166+
"display_name": "Python 3",
167+
"language": "python",
168+
"name": "python3"
169+
},
170+
"language_info": {
171+
"name": "python"
172+
}
173+
},
174+
"nbformat": 4,
175+
"nbformat_minor": 5
176+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Copyright 2025 Google LLC
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+
# https://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+
from functools import cached_property
15+
from typing import Dict, TYPE_CHECKING
16+
17+
import attrs
18+
import galois
19+
20+
from qualtran import (
21+
Bloq,
22+
bloq_example,
23+
BloqDocSpec,
24+
DecomposeTypeError,
25+
QGFPoly,
26+
Register,
27+
Signature,
28+
)
29+
from qualtran.bloqs.gf_arithmetic import GF2AddK
30+
from qualtran.bloqs.gf_poly_arithmetic.gf_poly_split_and_join import GFPolyJoin, GFPolySplit
31+
from qualtran.symbolics import is_symbolic
32+
33+
if TYPE_CHECKING:
34+
from qualtran import BloqBuilder, Soquet
35+
from qualtran.resource_counting import BloqCountDictT, SympySymbolAllocator
36+
from qualtran.simulation.classical_sim import ClassicalValT
37+
38+
39+
@attrs.frozen
40+
class GF2PolyAddK(Bloq):
41+
r"""In place addition of a constant polynomial defined over GF($2^m$).
42+
43+
The bloq implements in place addition of a classical constant polynomial $g(x)$ and
44+
a quantum register $|f(x)\rangle$ storing coefficients of a degree-n polynomial defined
45+
over GF($2^m$). Addition in GF($2^m$) simply reduces to a component wise XOR, which can
46+
be implemented via X gates.
47+
48+
$$
49+
|f(x)\rangle \rightarrow |f(x) + g(x)\rangle
50+
$$
51+
52+
Args:
53+
qgf_poly: An instance of `QGFPoly` type that defines the data type for quantum
54+
register $|f(x)\rangle$ storing coefficients of a degree-n polynomial defined
55+
over GF($2^m$).
56+
g_x: An instance of `galois.Poly` that specifies that constant polynomial g(x)
57+
defined over GF($2^m$) that should be added to the input register f(x).
58+
59+
Registers:
60+
f_x: Input THRU register that stores coefficients of polynomial defined over $GF(2^m)$.
61+
"""
62+
63+
qgf_poly: QGFPoly
64+
g_x: galois.Poly = attrs.field()
65+
66+
@cached_property
67+
def signature(self) -> 'Signature':
68+
return Signature([Register('f_x', dtype=self.qgf_poly)])
69+
70+
@g_x.validator
71+
def _validate_g_x(self, attribute, value):
72+
if not is_symbolic(self.qgf_poly.degree):
73+
if value.degree > self.qgf_poly.degree:
74+
raise ValueError(f"Degree of constant polynomial must be <= {self.qgf_poly.degree}")
75+
if not is_symbolic(self.qgf_poly.degree, self.qgf_poly.qgf):
76+
if not value.field is self.qgf_poly.qgf.gf_type:
77+
raise ValueError(
78+
f"Constant polynomial must be defined over galois field {self.qgf_poly.qgf.gf_type}"
79+
)
80+
81+
def is_symbolic(self):
82+
return is_symbolic(self.qgf_poly)
83+
84+
def build_composite_bloq(self, bb: 'BloqBuilder', *, f_x: 'Soquet') -> Dict[str, 'Soquet']:
85+
if self.is_symbolic():
86+
raise DecomposeTypeError(f"Cannot decompose symbolic {self}")
87+
f_x = bb.add(GFPolySplit(self.qgf_poly), reg=f_x)
88+
g_x = self.qgf_poly.to_gf_coefficients(self.g_x)
89+
for i in range(self.qgf_poly.degree + 1):
90+
f_x[i] = bb.add(GF2AddK(self.qgf_poly.qgf.bitsize, int(g_x[i])), x=f_x[i])
91+
92+
f_x = bb.add(GFPolyJoin(self.qgf_poly), reg=f_x)
93+
return {'f_x': f_x}
94+
95+
def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT':
96+
if self.is_symbolic():
97+
k = ssa.new_symbol('g_x')
98+
return {GF2AddK(self.qgf_poly.qgf.bitsize, k): self.qgf_poly.degree + 1}
99+
return super().build_call_graph(ssa)
100+
101+
def on_classical_vals(self, *, f_x) -> Dict[str, 'ClassicalValT']:
102+
return {'f_x': f_x + self.g_x}
103+
104+
105+
@bloq_example
106+
def _gf2_poly_4_8_add_k() -> GF2PolyAddK:
107+
from galois import Poly
108+
109+
from qualtran import QGF, QGFPoly
110+
111+
qgf_poly = QGFPoly(4, QGF(2, 3))
112+
g_x = Poly(qgf_poly.qgf.gf_type([1, 2, 3, 4, 5]))
113+
gf2_poly_4_8_add_k = GF2PolyAddK(qgf_poly, g_x)
114+
return gf2_poly_4_8_add_k
115+
116+
117+
@bloq_example
118+
def _gf2_poly_add_k_symbolic() -> GF2PolyAddK:
119+
import sympy
120+
from galois import Poly
121+
122+
from qualtran import QGF, QGFPoly
123+
124+
n, m = sympy.symbols('n, m', positive=True, integers=True)
125+
qgf_poly = QGFPoly(n, QGF(2, m))
126+
gf2_poly_symbolic_add_k = GF2PolyAddK(qgf_poly, Poly([0, 0, 0, 0]))
127+
return gf2_poly_symbolic_add_k
128+
129+
130+
_GF2_ADD_K_DOC = BloqDocSpec(
131+
bloq_cls=GF2AddK, examples=(_gf2_poly_4_8_add_k, _gf2_poly_add_k_symbolic)
132+
)

0 commit comments

Comments
 (0)