Skip to content

Commit 9d3f6b2

Browse files
authored
Add GF2AddK for in place addition of a constant over GF($2^m$) (#1447)
* Add GF2Add bloq for addition over GF(2^m) * Add import * Add GF2Square bloq for squaring over GF(2^m) * Docstring and more tests * Fix pytest typo * Add GF2Inverse bloq to compute inverse over GF(2^m) * Update docstring, notebook and add test for symbolic costs * Nits and rename tests * Add GF2AddK for in place addition of a constant over GF(2^m) * Add GF2AddK to init * Address nits and rename tests
1 parent bdad716 commit 9d3f6b2

File tree

7 files changed

+328
-0
lines changed

7 files changed

+328
-0
lines changed

dev_tools/autogenerate-bloqs-notebooks-v2.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
import qualtran.bloqs.data_loading.select_swap_qrom
106106
import qualtran.bloqs.factoring.ecc
107107
import qualtran.bloqs.factoring.mod_exp
108+
import qualtran.bloqs.gf_arithmetic.gf2_add_k
108109
import qualtran.bloqs.gf_arithmetic.gf2_addition
109110
import qualtran.bloqs.gf_arithmetic.gf2_inverse
110111
import qualtran.bloqs.gf_arithmetic.gf2_multiplication
@@ -569,6 +570,11 @@
569570
module=qualtran.bloqs.gf_arithmetic.gf2_addition,
570571
bloq_specs=[qualtran.bloqs.gf_arithmetic.gf2_addition._GF2_ADDITION_DOC],
571572
),
573+
NotebookSpecV2(
574+
title='GF($2^m$) Add Constant',
575+
module=qualtran.bloqs.gf_arithmetic.gf2_add_k,
576+
bloq_specs=[qualtran.bloqs.gf_arithmetic.gf2_add_k._GF2_ADD_K_DOC],
577+
),
572578
NotebookSpecV2(
573579
title='GF($2^m$) Square',
574580
module=qualtran.bloqs.gf_arithmetic.gf2_square,

docs/bloqs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ Bloqs Library
9393

9494
gf_arithmetic/gf2_multiplication.ipynb
9595
gf_arithmetic/gf2_addition.ipynb
96+
gf_arithmetic/gf2_add_k.ipynb
9697
gf_arithmetic/gf2_square.ipynb
9798
gf_arithmetic/gf2_inverse.ipynb
9899

qualtran/bloqs/gf_arithmetic/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from qualtran.bloqs.gf_arithmetic.gf2_add_k import GF2AddK
1516
from qualtran.bloqs.gf_arithmetic.gf2_addition import GF2Addition
1617
from qualtran.bloqs.gf_arithmetic.gf2_inverse import GF2Inverse
1718
from qualtran.bloqs.gf_arithmetic.gf2_multiplication import GF2Multiplication
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "b02daa35",
6+
"metadata": {
7+
"cq.autogen": "title_cell"
8+
},
9+
"source": [
10+
"# GF($2^m$) Add Constant"
11+
]
12+
},
13+
{
14+
"cell_type": "code",
15+
"execution_count": null,
16+
"id": "c57ce930",
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": "e4620511",
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": "04dc8f2b",
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": "a0d774d4",
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": "0588ca5b",
83+
"metadata": {
84+
"cq.autogen": "GF2AddK.gf16_add_k"
85+
},
86+
"outputs": [],
87+
"source": [
88+
"gf16_add_k = GF2AddK(4, 1)"
89+
]
90+
},
91+
{
92+
"cell_type": "code",
93+
"execution_count": null,
94+
"id": "8250af55",
95+
"metadata": {
96+
"cq.autogen": "GF2AddK.gf2_add_k_symbolic"
97+
},
98+
"outputs": [],
99+
"source": [
100+
"import sympy\n",
101+
"\n",
102+
"m, k = sympy.symbols('m, k', positive=True, integers=True)\n",
103+
"gf2_add_k_symbolic = GF2AddK(m, k)"
104+
]
105+
},
106+
{
107+
"cell_type": "markdown",
108+
"id": "fe4c4550",
109+
"metadata": {
110+
"cq.autogen": "GF2AddK.graphical_signature.md"
111+
},
112+
"source": [
113+
"#### Graphical Signature"
114+
]
115+
},
116+
{
117+
"cell_type": "code",
118+
"execution_count": null,
119+
"id": "2374ad42",
120+
"metadata": {
121+
"cq.autogen": "GF2AddK.graphical_signature.py"
122+
},
123+
"outputs": [],
124+
"source": [
125+
"from qualtran.drawing import show_bloqs\n",
126+
"show_bloqs([gf16_add_k, gf2_add_k_symbolic],\n",
127+
" ['`gf16_add_k`', '`gf2_add_k_symbolic`'])"
128+
]
129+
},
130+
{
131+
"cell_type": "markdown",
132+
"id": "f64d8798",
133+
"metadata": {
134+
"cq.autogen": "GF2AddK.call_graph.md"
135+
},
136+
"source": [
137+
"### Call Graph"
138+
]
139+
},
140+
{
141+
"cell_type": "code",
142+
"execution_count": null,
143+
"id": "578f0f39",
144+
"metadata": {
145+
"cq.autogen": "GF2AddK.call_graph.py"
146+
},
147+
"outputs": [],
148+
"source": [
149+
"from qualtran.resource_counting.generalizers import ignore_split_join\n",
150+
"gf16_add_k_g, gf16_add_k_sigma = gf16_add_k.call_graph(max_depth=1, generalizer=ignore_split_join)\n",
151+
"show_call_graph(gf16_add_k_g)\n",
152+
"show_counts_sigma(gf16_add_k_sigma)"
153+
]
154+
}
155+
],
156+
"metadata": {
157+
"kernelspec": {
158+
"display_name": "Python 3",
159+
"language": "python",
160+
"name": "python3"
161+
},
162+
"language_info": {
163+
"name": "python"
164+
}
165+
},
166+
"nbformat": 4,
167+
"nbformat_minor": 5
168+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Copyright 2024 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, Sequence, TYPE_CHECKING
16+
17+
import attrs
18+
19+
from qualtran import Bloq, bloq_example, BloqDocSpec, DecomposeTypeError, QGF, Register, Signature
20+
from qualtran.bloqs.basic_gates import XGate
21+
from qualtran.symbolics import is_symbolic, SymbolicInt
22+
23+
if TYPE_CHECKING:
24+
from qualtran import BloqBuilder, Soquet
25+
from qualtran.resource_counting import BloqCountDictT, SympySymbolAllocator
26+
from qualtran.simulation.classical_sim import ClassicalValT
27+
28+
29+
@attrs.frozen
30+
class GF2AddK(Bloq):
31+
r"""In place addition of a constant $k$ for elements in GF($2^m$).
32+
33+
The bloq implements in place addition of a classical constant $k$ and a quantum register
34+
$|x\rangle$ storing elements from GF($2^m$). Addition in GF($2^m$) simply reduces to a component
35+
wise XOR, which can be implemented via X gates.
36+
37+
$$
38+
|x\rangle \rightarrow |x + k\rangle
39+
$$
40+
41+
Args:
42+
bitsize: The degree $m$ of the galois field GF($2^m$). Also corresponds to the number of
43+
qubits in the input register x.
44+
k: Integer representation of constant over GF($2^m$) that should be added to the input
45+
register x.
46+
47+
Registers:
48+
x: Input THRU register of size $m$ that stores elements from $GF(2^m)$.
49+
"""
50+
51+
bitsize: SymbolicInt
52+
k: SymbolicInt
53+
54+
@cached_property
55+
def signature(self) -> 'Signature':
56+
return Signature([Register('x', dtype=self.qgf)])
57+
58+
@cached_property
59+
def qgf(self) -> QGF:
60+
return QGF(characteristic=2, degree=self.bitsize)
61+
62+
@cached_property
63+
def _bits_k(self) -> Sequence[int]:
64+
return self.qgf.to_bits(self.qgf.gf_type(self.k))
65+
66+
def is_symbolic(self):
67+
return is_symbolic(self.k, self.bitsize)
68+
69+
def build_composite_bloq(self, bb: 'BloqBuilder', *, x: 'Soquet') -> Dict[str, 'Soquet']:
70+
if self.is_symbolic():
71+
raise DecomposeTypeError(f"Cannot decompose symbolic {self}")
72+
xs = bb.split(x)
73+
74+
for i, bit in enumerate(self._bits_k):
75+
if bit == 1:
76+
xs[i] = bb.add(XGate(), q=xs[i])
77+
78+
x = bb.join(xs, dtype=self.qgf)
79+
80+
return {'x': x}
81+
82+
def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT':
83+
num_flips = self.bitsize if self.is_symbolic() else sum(self._bits_k)
84+
return {XGate(): num_flips}
85+
86+
def on_classical_vals(self, *, x) -> Dict[str, 'ClassicalValT']:
87+
assert isinstance(x, self.qgf.gf_type)
88+
return {'x': x + self.qgf.gf_type(self.k)}
89+
90+
91+
@bloq_example
92+
def _gf16_add_k() -> GF2AddK:
93+
gf16_add_k = GF2AddK(4, 1)
94+
return gf16_add_k
95+
96+
97+
@bloq_example
98+
def _gf2_add_k_symbolic() -> GF2AddK:
99+
import sympy
100+
101+
m, k = sympy.symbols('m, k', positive=True, integers=True)
102+
gf2_add_k_symbolic = GF2AddK(m, k)
103+
return gf2_add_k_symbolic
104+
105+
106+
_GF2_ADD_K_DOC = BloqDocSpec(bloq_cls=GF2AddK, examples=(_gf16_add_k, _gf2_add_k_symbolic))
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright 2024 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+
15+
import pytest
16+
from galois import GF
17+
18+
from qualtran.bloqs.gf_arithmetic.gf2_add_k import _gf2_add_k_symbolic, _gf16_add_k, GF2AddK
19+
from qualtran.testing import assert_consistent_classical_action
20+
21+
22+
def test_gf16_add_k(bloq_autotester):
23+
bloq_autotester(_gf16_add_k)
24+
25+
26+
def test_gf2_add_k_symbolic(bloq_autotester):
27+
bloq_autotester(_gf2_add_k_symbolic)
28+
29+
30+
def test_gf2_add_k_classical_sim_quick():
31+
m = 2
32+
GFM = GF(2**m)
33+
for k in GFM.elements:
34+
bloq = GF2AddK(m, int(k))
35+
assert_consistent_classical_action(bloq, x=GFM.elements)
36+
37+
38+
@pytest.mark.slow
39+
@pytest.mark.parametrize('m', [3, 4, 5])
40+
def test_gf2_add_k_classical_sim(m):
41+
GFM = GF(2**m)
42+
for k in GFM.elements:
43+
bloq = GF2AddK(m, int(k))
44+
assert_consistent_classical_action(bloq, x=GFM.elements)

qualtran/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ def assert_bloq_example_serializes_for_pytest(bloq_ex: BloqExample):
9292
'symbolic_hamsim_by_gqsp',
9393
'gf16_addition', # cannot serialize QGF
9494
'gf2_addition_symbolic', # cannot serialize QGF
95+
'gf16_add_k', # cannot serialize QGF
96+
'gf2_add_k_symbolic', # cannot serialize QGF
9597
'gf16_multiplication', # cannot serialize QGF
9698
'gf2_multiplication_symbolic', # cannot serialize QGF
9799
'gf16_square', # cannot serialize QGF

0 commit comments

Comments
 (0)