Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 21 additions & 0 deletions dev_tools/qualtran_dev_tools/notebook_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@
import qualtran.bloqs.gf_arithmetic.gf2_inverse
import qualtran.bloqs.gf_arithmetic.gf2_multiplication
import qualtran.bloqs.gf_arithmetic.gf2_square
import qualtran.bloqs.gf_poly_arithmetic.gf2_poly_add_k
import qualtran.bloqs.gf_poly_arithmetic.gf_poly_split_and_join
import qualtran.bloqs.hamiltonian_simulation.hamiltonian_simulation_by_gqsp
import qualtran.bloqs.mcmt.and_bloq
import qualtran.bloqs.mcmt.controlled_via_and
Expand Down Expand Up @@ -604,6 +606,24 @@
),
]

GF_POLY_ARITHMETIC = [
# --------------------------------------------------------------------------
# ----- Polynomials defined over Galois Fields (GF) Arithmetic --------
# --------------------------------------------------------------------------
NotebookSpecV2(
title='Polynomials over GF($p^m$) - Split and Join',
module=qualtran.bloqs.gf_poly_arithmetic.gf_poly_split_and_join,
bloq_specs=[
qualtran.bloqs.gf_poly_arithmetic.gf_poly_split_and_join._GF_POLY_SPLIT_DOC,
qualtran.bloqs.gf_poly_arithmetic.gf_poly_split_and_join._GF_POLY_JOIN_DOC,
],
),
NotebookSpecV2(
title='Polynomials over GF($2^m$) - Add Constant',
module=qualtran.bloqs.gf_poly_arithmetic.gf2_poly_add_k,
bloq_specs=[qualtran.bloqs.gf_poly_arithmetic.gf2_poly_add_k._GF2_POLY_ADD_K_DOC],
),
]

ROT_QFT_PE = [
# --------------------------------------------------------------------------
Expand Down Expand Up @@ -935,6 +955,7 @@
('Arithmetic', ARITHMETIC),
('Modular Arithmetic', MOD_ARITHMETIC),
('GF Arithmetic', GF_ARITHMETIC),
('Polynomials over Galois Fields', GF_POLY_ARITHMETIC),
('Rotations', ROT_QFT_PE),
('Block Encoding', BLOCK_ENCODING),
('Optimization', OPTIMIZATION),
Expand Down
7 changes: 7 additions & 0 deletions docs/bloqs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@ Bloqs Library
gf_arithmetic/gf2_square.ipynb
gf_arithmetic/gf2_inverse.ipynb

.. toctree::
:maxdepth: 2
:caption: Polynomials over Galois Fields:

gf_poly_arithmetic/gf_poly_split_and_join.ipynb
gf_poly_arithmetic/gf2_poly_add_k.ipynb

.. toctree::
:maxdepth: 2
:caption: Rotations:
Expand Down
17 changes: 17 additions & 0 deletions qualtran/bloqs/gf_poly_arithmetic/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


from qualtran.bloqs.gf_poly_arithmetic.gf2_poly_add_k import GF2PolyAddK
from qualtran.bloqs.gf_poly_arithmetic.gf_poly_split_and_join import GFPolyJoin, GFPolySplit
179 changes: 179 additions & 0 deletions qualtran/bloqs/gf_poly_arithmetic/gf2_poly_add_k.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "71e0dbd8",
"metadata": {
"cq.autogen": "title_cell"
},
"source": [
"# Polynomials over GF($2^m$) - Add Constant"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c02448cd",
"metadata": {
"cq.autogen": "top_imports"
},
"outputs": [],
"source": [
"from qualtran import Bloq, CompositeBloq, BloqBuilder, Signature, Register\n",
"from qualtran import QBit, QInt, QUInt, QAny\n",
"from qualtran.drawing import show_bloq, show_call_graph, show_counts_sigma\n",
"from typing import *\n",
"import numpy as np\n",
"import sympy\n",
"import cirq"
]
},
{
"cell_type": "markdown",
"id": "a5f23a08",
"metadata": {
"cq.autogen": "GF2PolyAddK.bloq_doc.md"
},
"source": [
"## `GF2PolyAddK`\n",
"In place addition of a constant polynomial defined over GF($2^m$).\n",
"\n",
"The bloq implements in place addition of a classical constant polynomial $g(x)$ and\n",
"a quantum register $|f(x)\\rangle$ storing coefficients of a degree-n polynomial defined\n",
"over GF($2^m$). Addition in GF($2^m$) simply reduces to a component wise XOR, which can\n",
"be implemented via X gates.\n",
"\n",
"$$\n",
" |f(x)\\rangle \\rightarrow |f(x) + g(x)\\rangle\n",
"$$\n",
"\n",
"#### Parameters\n",
" - `qgf_poly`: An instance of `QGFPoly` type that defines the data type for quantum register $|f(x)\\rangle$ storing coefficients of a degree-n polynomial defined over GF($2^m$).\n",
" - `g_x`: An instance of `galois.Poly` that specifies that constant polynomial g(x) defined over GF($2^m$) that should be added to the input register f(x). \n",
"\n",
"#### Registers\n",
" - `f_x`: Input THRU register that stores coefficients of polynomial defined over $GF(2^m)$.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d40e23be",
"metadata": {
"cq.autogen": "GF2PolyAddK.bloq_doc.py"
},
"outputs": [],
"source": [
"from qualtran.bloqs.gf_poly_arithmetic import GF2PolyAddK"
]
},
{
"cell_type": "markdown",
"id": "101615d7",
"metadata": {
"cq.autogen": "GF2PolyAddK.example_instances.md"
},
"source": [
"### Example Instances"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2cc9165f",
"metadata": {
"cq.autogen": "GF2PolyAddK.gf2_poly_4_8_add_k"
},
"outputs": [],
"source": [
"from galois import Poly\n",
"\n",
"from qualtran import QGF, QGFPoly\n",
"\n",
"qgf_poly = QGFPoly(4, QGF(2, 3))\n",
"g_x = Poly(qgf_poly.qgf.gf_type([1, 2, 3, 4, 5]))\n",
"gf2_poly_4_8_add_k = GF2PolyAddK(qgf_poly, g_x)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9081edee",
"metadata": {
"cq.autogen": "GF2PolyAddK.gf2_poly_add_k_symbolic"
},
"outputs": [],
"source": [
"import sympy\n",
"from galois import Poly\n",
"\n",
"from qualtran import QGF, QGFPoly\n",
"\n",
"n, m = sympy.symbols('n, m', positive=True, integers=True)\n",
"qgf_poly = QGFPoly(n, QGF(2, m))\n",
"gf2_poly_add_k_symbolic = GF2PolyAddK(qgf_poly, Poly([0, 0, 0, 0]))"
]
},
{
"cell_type": "markdown",
"id": "11a4d71f",
"metadata": {
"cq.autogen": "GF2PolyAddK.graphical_signature.md"
},
"source": [
"#### Graphical Signature"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "66b4a211",
"metadata": {
"cq.autogen": "GF2PolyAddK.graphical_signature.py"
},
"outputs": [],
"source": [
"from qualtran.drawing import show_bloqs\n",
"show_bloqs([gf2_poly_4_8_add_k, gf2_poly_add_k_symbolic],\n",
" ['`gf2_poly_4_8_add_k`', '`gf2_poly_add_k_symbolic`'])"
]
},
{
"cell_type": "markdown",
"id": "5940114d",
"metadata": {
"cq.autogen": "GF2PolyAddK.call_graph.md"
},
"source": [
"### Call Graph"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "dffd364c",
"metadata": {
"cq.autogen": "GF2PolyAddK.call_graph.py"
},
"outputs": [],
"source": [
"from qualtran.resource_counting.generalizers import ignore_split_join\n",
"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",
"show_call_graph(gf2_poly_4_8_add_k_g)\n",
"show_counts_sigma(gf2_poly_4_8_add_k_sigma)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
134 changes: 134 additions & 0 deletions qualtran/bloqs/gf_poly_arithmetic/gf2_poly_add_k.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from functools import cached_property
from typing import Dict, Set, TYPE_CHECKING, Union

import attrs
import galois

from qualtran import (
Bloq,
bloq_example,
BloqDocSpec,
DecomposeTypeError,
QGFPoly,
Register,
Signature,
)
from qualtran.bloqs.gf_arithmetic import GF2AddK
from qualtran.bloqs.gf_poly_arithmetic.gf_poly_split_and_join import GFPolyJoin, GFPolySplit
from qualtran.symbolics import is_symbolic

if TYPE_CHECKING:
from qualtran import BloqBuilder, Soquet
from qualtran.resource_counting import BloqCountDictT, BloqCountT, SympySymbolAllocator
from qualtran.simulation.classical_sim import ClassicalValT


@attrs.frozen
class GF2PolyAddK(Bloq):
r"""In place addition of a constant polynomial defined over GF($2^m$).

The bloq implements in place addition of a classical constant polynomial $g(x)$ and
a quantum register $|f(x)\rangle$ storing coefficients of a degree-n polynomial defined
over GF($2^m$). Addition in GF($2^m$) simply reduces to a component wise XOR, which can
be implemented via X gates.

$$
|f(x)\rangle \rightarrow |f(x) + g(x)\rangle
$$

Args:
qgf_poly: An instance of `QGFPoly` type that defines the data type for quantum
register $|f(x)\rangle$ storing coefficients of a degree-n polynomial defined
over GF($2^m$).
g_x: An instance of `galois.Poly` that specifies that constant polynomial g(x)
defined over GF($2^m$) that should be added to the input register f(x).

Registers:
f_x: Input THRU register that stores coefficients of polynomial defined over $GF(2^m)$.
"""

qgf_poly: QGFPoly
g_x: galois.Poly = attrs.field()

@cached_property
def signature(self) -> 'Signature':
return Signature([Register('f_x', dtype=self.qgf_poly)])

@g_x.validator
def _validate_g_x(self, attribute, value):
if not is_symbolic(self.qgf_poly.degree):
if value.degree > self.qgf_poly.degree:
raise ValueError(f"Degree of constant polynomial must be <= {self.qgf_poly.degree}")
if not is_symbolic(self.qgf_poly.degree, self.qgf_poly.qgf):
if not value.field is self.qgf_poly.qgf.gf_type:
raise ValueError(
f"Constant polynomial must be defined over galois field {self.qgf_poly.qgf.gf_type}"
)

def is_symbolic(self):
return is_symbolic(self.qgf_poly.degree)

def build_composite_bloq(self, bb: 'BloqBuilder', *, f_x: 'Soquet') -> Dict[str, 'Soquet']:
if self.is_symbolic():
raise DecomposeTypeError(f"Cannot decompose symbolic {self}")
f_x = bb.add(GFPolySplit(self.qgf_poly), reg=f_x)
g_x = self.qgf_poly.to_gf_coefficients(self.g_x)
for i in range(self.qgf_poly.degree + 1):
f_x[i] = bb.add(GF2AddK(self.qgf_poly.qgf.bitsize, int(g_x[i])), x=f_x[i])

f_x = bb.add(GFPolyJoin(self.qgf_poly), reg=f_x)
return {'f_x': f_x}

def build_call_graph(
self, ssa: 'SympySymbolAllocator'
) -> Union['BloqCountDictT', Set['BloqCountT']]:
if self.is_symbolic():
k = ssa.new_symbol('g_x')
return {GF2AddK(self.qgf_poly.qgf.bitsize, k): self.qgf_poly.degree + 1}
return super().build_call_graph(ssa)

def on_classical_vals(self, *, f_x) -> Dict[str, 'ClassicalValT']:
return {'f_x': f_x + self.g_x}


@bloq_example
def _gf2_poly_4_8_add_k() -> GF2PolyAddK:
from galois import Poly

from qualtran import QGF, QGFPoly

qgf_poly = QGFPoly(4, QGF(2, 3))
g_x = Poly(qgf_poly.qgf.gf_type([1, 2, 3, 4, 5]))
gf2_poly_4_8_add_k = GF2PolyAddK(qgf_poly, g_x)
return gf2_poly_4_8_add_k


@bloq_example
def _gf2_poly_add_k_symbolic() -> GF2PolyAddK:
import sympy
from galois import Poly

from qualtran import QGF, QGFPoly

n, m = sympy.symbols('n, m', positive=True, integers=True)
qgf_poly = QGFPoly(n, QGF(2, m))
gf2_poly_add_k_symbolic = GF2PolyAddK(qgf_poly, Poly([0, 0, 0, 0]))
return gf2_poly_add_k_symbolic


_GF2_POLY_ADD_K_DOC = BloqDocSpec(
bloq_cls=GF2PolyAddK, examples=(_gf2_poly_4_8_add_k, _gf2_poly_add_k_symbolic)
)
Loading