Skip to content

Commit

Permalink
Add bloq for constant polynomial multiplication modulu in GF(2)
Browse files Browse the repository at this point in the history
  • Loading branch information
NoureldinYosri committed Jan 6, 2025
1 parent c84bca1 commit af5e022
Show file tree
Hide file tree
Showing 4 changed files with 286 additions and 13 deletions.
5 changes: 4 additions & 1 deletion dev_tools/qualtran_dev_tools/notebook_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,10 @@
NotebookSpecV2(
title='GF($2^m$) Multiplication',
module=qualtran.bloqs.gf_arithmetic.gf2_multiplication,
bloq_specs=[qualtran.bloqs.gf_arithmetic.gf2_multiplication._GF2_MULTIPLICATION_DOC],
bloq_specs=[
qualtran.bloqs.gf_arithmetic.gf2_multiplication._GF2_MULTIPLICATION_DOC,
qualtran.bloqs.gf_arithmetic.gf2_multiplication._MULTIPLY_BY_CONSTANT_MOD_DOC,
],
),
NotebookSpecV2(
title='GF($2^m$) Addition',
Expand Down
129 changes: 118 additions & 11 deletions qualtran/bloqs/gf_arithmetic/gf2_multiplication.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "markdown",
"id": "87c95c4a",
"id": "acbb10a4",
"metadata": {
"cq.autogen": "title_cell"
},
Expand All @@ -13,7 +13,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "31c1f087",
"id": "fd0e976a",
"metadata": {
"cq.autogen": "top_imports"
},
Expand All @@ -30,7 +30,7 @@
},
{
"cell_type": "markdown",
"id": "307679ec",
"id": "8bfc1e7d",
"metadata": {
"cq.autogen": "GF2Multiplication.bloq_doc.md"
},
Expand Down Expand Up @@ -72,7 +72,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "872a44d1",
"id": "d1193813",
"metadata": {
"cq.autogen": "GF2Multiplication.bloq_doc.py"
},
Expand All @@ -83,7 +83,7 @@
},
{
"cell_type": "markdown",
"id": "d0f0db7d",
"id": "4b13e0a3",
"metadata": {
"cq.autogen": "GF2Multiplication.example_instances.md"
},
Expand All @@ -94,7 +94,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "131bc962",
"id": "20cab892",
"metadata": {
"cq.autogen": "GF2Multiplication.gf16_multiplication"
},
Expand All @@ -106,7 +106,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "69f564d8",
"id": "06f44b6e",
"metadata": {
"cq.autogen": "GF2Multiplication.gf2_multiplication_symbolic"
},
Expand All @@ -120,7 +120,7 @@
},
{
"cell_type": "markdown",
"id": "2a62c2b8",
"id": "9b96d200",
"metadata": {
"cq.autogen": "GF2Multiplication.graphical_signature.md"
},
Expand All @@ -131,7 +131,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "cf003e98",
"id": "3c5ef2f6",
"metadata": {
"cq.autogen": "GF2Multiplication.graphical_signature.py"
},
Expand All @@ -144,7 +144,7 @@
},
{
"cell_type": "markdown",
"id": "f14ef0c5",
"id": "e6a3dc52",
"metadata": {
"cq.autogen": "GF2Multiplication.call_graph.md"
},
Expand All @@ -155,7 +155,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "f4b7bf2c",
"id": "e3b95e04",
"metadata": {
"cq.autogen": "GF2Multiplication.call_graph.py"
},
Expand All @@ -166,6 +166,113 @@
"show_call_graph(gf16_multiplication_g)\n",
"show_counts_sigma(gf16_multiplication_sigma)"
]
},
{
"cell_type": "markdown",
"id": "8e1b2599",
"metadata": {
"cq.autogen": "MultiplyPolyByConstantMod.bloq_doc.md"
},
"source": [
"## `MultiplyPolyByConstantMod`\n",
"Multiply a polynomial by $f(x)$ modulu $m(x)$. Both $f(x)$ and $m(x)$ are constants.\n",
"\n",
"#### Parameters\n",
" - `f_x`: The polynomial to mulitply with, given either a galois.Poly or as a sequence degrees.\n",
" - `m_x`: The modulus polynomial, given either a galois.Poly or as a sequence degrees. \n",
"\n",
"#### Registers\n",
" - `g`: The polynomial coefficients (in GF(2)). \n",
"\n",
"Regerences:\n",
" - [Space-efficient quantum multiplication of polynomials for binary finite fields with\n",
" sub-quadratic Toffoli gate count](https://arxiv.org/abs/1910.02849v2) Algorithm 1"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f48b16b4",
"metadata": {
"cq.autogen": "MultiplyPolyByConstantMod.bloq_doc.py"
},
"outputs": [],
"source": [
"from qualtran.bloqs.gf_arithmetic import MultiplyPolyByConstantMod"
]
},
{
"cell_type": "markdown",
"id": "f71cbb55",
"metadata": {
"cq.autogen": "MultiplyPolyByConstantMod.example_instances.md"
},
"source": [
"### Example Instances"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cb34d39c",
"metadata": {
"cq.autogen": "MultiplyPolyByConstantMod.gf2_multiply_by_constant_modulu"
},
"outputs": [],
"source": [
"fx = [2, 0] # x^2 + 1\n",
"mx = [0, 1, 3] # x^3 + x + 1\n",
"gf2_multiply_by_constant_modulu = MultiplyPolyByConstantMod(fx, mx)"
]
},
{
"cell_type": "markdown",
"id": "413907e4",
"metadata": {
"cq.autogen": "MultiplyPolyByConstantMod.graphical_signature.md"
},
"source": [
"#### Graphical Signature"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ffce6a92",
"metadata": {
"cq.autogen": "MultiplyPolyByConstantMod.graphical_signature.py"
},
"outputs": [],
"source": [
"from qualtran.drawing import show_bloqs\n",
"show_bloqs([gf2_multiply_by_constant_modulu],\n",
" ['`gf2_multiply_by_constant_modulu`'])"
]
},
{
"cell_type": "markdown",
"id": "703fa7bf",
"metadata": {
"cq.autogen": "MultiplyPolyByConstantMod.call_graph.md"
},
"source": [
"### Call Graph"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2d8b870f",
"metadata": {
"cq.autogen": "MultiplyPolyByConstantMod.call_graph.py"
},
"outputs": [],
"source": [
"from qualtran.resource_counting.generalizers import ignore_split_join\n",
"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",
"show_call_graph(gf2_multiply_by_constant_modulu_g)\n",
"show_counts_sigma(gf2_multiply_by_constant_modulu_sigma)"
]
}
],
"metadata": {
Expand Down
103 changes: 103 additions & 0 deletions qualtran/bloqs/gf_arithmetic/gf2_multiplication.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,3 +247,106 @@ def _gf2_multiplication_symbolic() -> GF2Multiplication:
_GF2_MULTIPLICATION_DOC = BloqDocSpec(
bloq_cls=GF2Multiplication, examples=(_gf16_multiplication, _gf2_multiplication_symbolic)
)


@attrs.frozen
class MultiplyPolyByConstantMod(Bloq):
r"""Multiply a polynomial by $f(x)$ modulu $m(x)$. Both $f(x)$ and $m(x)$ are constants.
Args:
f_x: The polynomial to mulitply with, given either a galois.Poly or as
a sequence degrees.
m_x: The modulus polynomial, given either a galois.Poly or as
a sequence degrees.
Registers:
g: The polynomial coefficients (in GF(2)).
Regerences:
- [Space-efficient quantum multiplication of polynomials for binary finite fields with
sub-quadratic Toffoli gate count](https://arxiv.org/abs/1910.02849v2) Algorithm 1
"""

f_x: Poly = attrs.field(converter=lambda x: x if isinstance(x, Poly) else Poly.Degrees(x))
m_x: Poly = attrs.field(converter=lambda x: x if isinstance(x, Poly) else Poly.Degrees(x))

def __attrs_post_init__(self):
assert self.m_x.is_irreducible()
assert self.f_x.degrees.max() < self.m_x.degrees.max()

@cached_property
def n(self):
return self.m_x.degrees.max()

@cached_property
def lup(self) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
"""Returns the LUP decomposition of the matrix representing the operation.
If m_x is irreducible, then the operation y := (y*f_x)%m_x can be represented
by a full rank matrix that can be decomposed into PLU where L and U are lower
and upper traingular matricies and P is a permutation matrix.
"""
n = self.n
matrix = np.zeros((n, n), dtype=int)
for i in range(n):
p = (self.f_x * Poly.Degrees([i])) % self.m_x
for j in p.nonzero_degrees:
matrix[j, i] = 1
P, L, U = GF(2)(matrix).plu_decompose()
return np.asarray(L, dtype=int), np.asarray(U, dtype=int), np.asarray(P, dtype=int)

@cached_property
def signature(self) -> 'Signature':
return Signature([Register('g', QBit(), shape=(self.n,))])

def on_classical_vals(self, g) -> Dict[str, 'ClassicalValT']:
p = (Poly(g[::-1], GF(2)) * self.f_x) % self.m_x
res = p.coefficients().tolist()
res = [0 for _ in range(self.n - len(res))] + res
res = res[::-1]
return {'g': res}

def build_composite_bloq(self, bb: 'BloqBuilder', g: 'Soquet') -> Dict[str, 'Soquet']:
L, U, P = self.lup
if is_symbolic(self.n):
raise DecomposeTypeError(f"Symbolic decomposition isn't supported for {self}")
for i in range(self.n):
for j in range(i + 1, self.n):
if U[i, j]:
g[j], g[i] = bb.add(CNOT(), ctrl=g[j], target=g[i])

for i in reversed(range(self.n)):
for j in reversed(range(i)):
if L[i, j]:
g[j], g[i] = bb.add(CNOT(), ctrl=g[j], target=g[i])

column = [*range(self.n)]
for i in range(self.n):
for j in range(i + 1, self.n):
if P[i, column[j]]:
g[i], g[j] = g[j], g[i]
column[i], column[j] = column[j], column[i]
return {'g': g}

def build_call_graph(
self, ssa: 'SympySymbolAllocator'
) -> Union['BloqCountDictT', Set['BloqCountT']]:
L, U, _ = self.lup
# The number of cnots is the number of non zero off-diagnoal entries in L and U.
cnots = np.sum(L) + np.sum(U) - 2 * self.n
if cnots:
return {CNOT(): cnots}
return {}


@bloq_example
def _gf2_multiply_by_constant_modulu() -> MultiplyPolyByConstantMod:
fx = [2, 0] # x^2 + 1
mx = [0, 1, 3] # x^3 + x + 1
gf2_multiply_by_constant_modulu = MultiplyPolyByConstantMod(fx, mx)
return gf2_multiply_by_constant_modulu


_MULTIPLY_BY_CONSTANT_MOD_DOC = BloqDocSpec(
bloq_cls=MultiplyPolyByConstantMod, examples=(_gf2_multiply_by_constant_modulu,)
)
Loading

0 comments on commit af5e022

Please sign in to comment.