From 354eb5c4955e4ad3e5abcf853bd3693ec6b275a8 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 24 Mar 2025 18:25:13 -0700 Subject: [PATCH 1/3] Create a Bloq that represents initialization in the Coset representation --- .../qualtran_dev_tools/notebook_specs.py | 7 + docs/bloqs/index.rst | 1 + qualtran/bloqs/mod_arithmetic/__init__.py | 1 + .../mod_arithmetic/coset_representation.ipynb | 159 ++++++++++++++++++ .../mod_arithmetic/coset_representation.py | 86 ++++++++++ .../coset_representation_test.py | 43 +++++ qualtran/serialization/resolver_dict.py | 1 + 7 files changed, 298 insertions(+) create mode 100644 qualtran/bloqs/mod_arithmetic/coset_representation.ipynb create mode 100644 qualtran/bloqs/mod_arithmetic/coset_representation.py create mode 100644 qualtran/bloqs/mod_arithmetic/coset_representation_test.py diff --git a/dev_tools/qualtran_dev_tools/notebook_specs.py b/dev_tools/qualtran_dev_tools/notebook_specs.py index 46734ef48..2695792a0 100644 --- a/dev_tools/qualtran_dev_tools/notebook_specs.py +++ b/dev_tools/qualtran_dev_tools/notebook_specs.py @@ -564,6 +564,13 @@ qualtran.bloqs.cryptography.ecc.ec_add_r._EC_WINDOW_ADD_BLOQ_DOC, ], ), + NotebookSpecV2( + title='Coset Representation', + module=qualtran.bloqs.mod_arithmetic.coset_representation, + bloq_specs=[ + qualtran.bloqs.mod_arithmetic.coset_representation._INIT_COST_REPRESENTATION_DOC + ], + ), ] GF_ARITHMETIC = [ diff --git a/docs/bloqs/index.rst b/docs/bloqs/index.rst index 3f0dbbac0..cbc1e1c4b 100644 --- a/docs/bloqs/index.rst +++ b/docs/bloqs/index.rst @@ -94,6 +94,7 @@ Bloqs Library cryptography/rsa/rsa.ipynb cryptography/ecc/ec_add.ipynb cryptography/ecc/ecc.ipynb + mod_arithmetic/coset_representation.ipynb .. toctree:: :maxdepth: 2 diff --git a/qualtran/bloqs/mod_arithmetic/__init__.py b/qualtran/bloqs/mod_arithmetic/__init__.py index 774aa44d6..fd4f53f97 100644 --- a/qualtran/bloqs/mod_arithmetic/__init__.py +++ b/qualtran/bloqs/mod_arithmetic/__init__.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from .coset_representation import InitCosetRepresntation from .mod_addition import CModAdd, CModAddK, CtrlScaleModAdd, ModAdd, ModAddK from .mod_division import KaliskiModInverse from .mod_multiplication import CModMulK, DirtyOutOfPlaceMontgomeryModMul, ModDbl diff --git a/qualtran/bloqs/mod_arithmetic/coset_representation.ipynb b/qualtran/bloqs/mod_arithmetic/coset_representation.ipynb new file mode 100644 index 000000000..428963158 --- /dev/null +++ b/qualtran/bloqs/mod_arithmetic/coset_representation.ipynb @@ -0,0 +1,159 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2feee875", + "metadata": { + "cq.autogen": "title_cell" + }, + "source": [ + "# Coset Representation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4e5b47c0", + "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": "521a3742", + "metadata": { + "cq.autogen": "InitCosetRepresntation.bloq_doc.md" + }, + "source": [ + "## `InitCosetRepresntation`\n", + "A state initialization of an integer in the coset representation.\n", + "\n", + "The cost representation of an integer $k$ modulo $N$ with $c_{pad}$ bits is defined as\n", + "$$\n", + " \\frac{1}{\\sqrt{2^{c_{pad}}}}\\sum_{j=0}^{2^{c_{pad}}} \\ket{jN + k}\n", + "$$\n", + "\n", + "This bloq can be built of only clifford gates ... namely $c_{pad}$ `H` gates on the padding\n", + "qubitsfollowed by `CNOT` gates implementing the reversible operation $jN+k$.\n", + "\n", + "#### Parameters\n", + " - `c_pad`: The number of padding bits.\n", + " - `k_bitsize`: The number of bits used to represent $k$ ($\\geq$ the number of bits of $k$ and $N$).\n", + " - `k`: The value of $k$.\n", + " - `mod`: The value of $N$. \n", + "\n", + "#### Registers\n", + " - `x`: A k_bitsize+c_pad register output register containing the initialized state. \n", + "\n", + "#### References\n", + " - - [Shor's algorithm with fewer (pure) qubits](https://arxiv.org/abs/quant-ph/0601097) section 4. - [How to factor 2048 bit RSA integers in 8 hours using 20 million noisy qubits](https://arxiv.org/abs/1905.09749) section 2.4\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecf59588", + "metadata": { + "cq.autogen": "InitCosetRepresntation.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.mod_arithmetic import InitCosetRepresntation" + ] + }, + { + "cell_type": "markdown", + "id": "e8233189", + "metadata": { + "cq.autogen": "InitCosetRepresntation.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fcbe6508", + "metadata": { + "cq.autogen": "InitCosetRepresntation.init_coset_representation" + }, + "outputs": [], + "source": [ + "c_pad, k_bitsize = sympy.symbols('c k')\n", + "init_coset_representation = InitCosetRepresntation(c_pad, k_bitsize, k=1, mod=19)" + ] + }, + { + "cell_type": "markdown", + "id": "76acaa4c", + "metadata": { + "cq.autogen": "InitCosetRepresntation.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9859906c", + "metadata": { + "cq.autogen": "InitCosetRepresntation.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([init_coset_representation],\n", + " ['`init_coset_representation`'])" + ] + }, + { + "cell_type": "markdown", + "id": "b7764566", + "metadata": { + "cq.autogen": "InitCosetRepresntation.call_graph.md" + }, + "source": [ + "### Call Graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bce29566", + "metadata": { + "cq.autogen": "InitCosetRepresntation.call_graph.py" + }, + "outputs": [], + "source": [ + "from qualtran.resource_counting.generalizers import ignore_split_join\n", + "init_coset_representation_g, init_coset_representation_sigma = init_coset_representation.call_graph(max_depth=1, generalizer=ignore_split_join)\n", + "show_call_graph(init_coset_representation_g)\n", + "show_counts_sigma(init_coset_representation_sigma)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/qualtran/bloqs/mod_arithmetic/coset_representation.py b/qualtran/bloqs/mod_arithmetic/coset_representation.py new file mode 100644 index 000000000..7651c282b --- /dev/null +++ b/qualtran/bloqs/mod_arithmetic/coset_representation.py @@ -0,0 +1,86 @@ +# 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 TYPE_CHECKING + +import attrs +import sympy + +from qualtran import Bloq, bloq_example, BloqDocSpec, QUInt, Register, Side, Signature +from qualtran.bloqs.basic_gates import CNOT, Hadamard +from qualtran.resource_counting import BloqCountDictT, SympySymbolAllocator + +if TYPE_CHECKING: + from qualtran.symbolics import SymbolicInt + + +@attrs.frozen +class InitCosetRepresntation(Bloq): + r"""A state initialization of an integer in the coset representation. + + The cost representation of an integer $k$ modulo $N$ with $c_{pad}$ bits is defined as + $$ + \frac{1}{\sqrt{2^{c_{pad}}}}\sum_{j=0}^{2^{c_{pad}}} \ket{jN + k} + $$ + + This bloq can be built of only clifford gates ... namely $c_{pad}$ `H` gates on the padding + qubitsfollowed by `CNOT` gates implementing the reversible operation $jN+k$. + + Args: + c_pad: The number of padding bits. + k_bitsize: The number of bits used to represent $k$ ($\geq$ the number of bits of $k$ and $N$). + k: The value of $k$. + mod: The value of $N$. + + Registers: + x: A k_bitsize+c_pad register output register containing the initialized state. + + References: + - [Shor's algorithm with fewer (pure) qubits](https://arxiv.org/abs/quant-ph/0601097) + section 4. + - [How to factor 2048 bit RSA integers in 8 hours using 20 million noisy qubits](https://arxiv.org/abs/1905.09749) + section 2.4 + """ + + c_pad: 'SymbolicInt' + k_bitsize: 'SymbolicInt' + k: 'SymbolicInt' + mod: 'SymbolicInt' + + @cached_property + def signature(self) -> 'Signature': + return Signature([Register('x', QUInt(self.c_pad + self.k_bitsize), side=Side.RIGHT)]) + + def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': + bitsize = self.k_bitsize + self.c_pad + return { + Hadamard(): self.c_pad, + # The matrix representing the reversible operation $jN+x$ consists of 0s and 1s. + # Thus it can be implemented using CNOTs using LUP decomposition or Gaussian elemenation + # using at most $n(n-1)$ CNOTs. + CNOT(): bitsize * (bitsize - 1), + } + + +@bloq_example +def _init_coset_representation() -> InitCosetRepresntation: + c_pad, k_bitsize = sympy.symbols('c k') + init_coset_representation = InitCosetRepresntation(c_pad, k_bitsize, k=1, mod=19) + return init_coset_representation + + +_INIT_COST_REPRESENTATION_DOC = BloqDocSpec( + bloq_cls=InitCosetRepresntation, examples=[_init_coset_representation] +) diff --git a/qualtran/bloqs/mod_arithmetic/coset_representation_test.py b/qualtran/bloqs/mod_arithmetic/coset_representation_test.py new file mode 100644 index 000000000..692224cf0 --- /dev/null +++ b/qualtran/bloqs/mod_arithmetic/coset_representation_test.py @@ -0,0 +1,43 @@ +# 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. + +import pytest +import sympy + +import qualtran.testing as qlt_testing +from qualtran.bloqs.mod_arithmetic.coset_representation import ( + _init_coset_representation, + InitCosetRepresntation, +) +from qualtran.resource_counting import get_cost_value, QECGatesCost + + +def test_init_cost_representation_cost(): + sym_vars = sympy.symbols('c n k N') + c, n, k, N = sym_vars + blq = InitCosetRepresntation(c, n, k, N) + cost = get_cost_value(blq, QECGatesCost()) + assert cost.total_toffoli_only() == 0 + resolver = {v: 10**4 for v in sym_vars} + upper_bound = (c + n) * (c + n - 1) + c + assert cost.clifford.subs(resolver) <= upper_bound.subs(resolver) + + +def test_init_coset_representation(bloq_autotester): + bloq_autotester(_init_coset_representation) + + +@pytest.mark.notebook +def test_notebook(): + qlt_testing.execute_notebook('coset_representation') diff --git a/qualtran/serialization/resolver_dict.py b/qualtran/serialization/resolver_dict.py index e455324b7..310691a1d 100644 --- a/qualtran/serialization/resolver_dict.py +++ b/qualtran/serialization/resolver_dict.py @@ -342,6 +342,7 @@ "qualtran.bloqs.data_loading.qroam_clean.QROAMCleanAdjoint": qualtran.bloqs.data_loading.qroam_clean.QROAMCleanAdjoint, "qualtran.bloqs.data_loading.qroam_clean.QROAMCleanAdjointWrapper": qualtran.bloqs.data_loading.qroam_clean.QROAMCleanAdjointWrapper, "qualtran.bloqs.data_loading.select_swap_qrom.SelectSwapQROM": qualtran.bloqs.data_loading.select_swap_qrom.SelectSwapQROM, + "qualtran.bloqs.mod_arithmetic.coset_representation.InitCosetRepresntation": qualtran.bloqs.mod_arithmetic.coset_representation.InitCosetRepresntation, "qualtran.bloqs.mod_arithmetic.CModAddK": qualtran.bloqs.mod_arithmetic.CModAddK, "qualtran.bloqs.mod_arithmetic.mod_addition.ModAdd": qualtran.bloqs.mod_arithmetic.mod_addition.ModAdd, "qualtran.bloqs.mod_arithmetic.mod_addition.CModAdd": qualtran.bloqs.mod_arithmetic.mod_addition.CModAdd, From 1d99c3eea78bf14d5888852b45447e7d6a656bca Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 24 Mar 2025 18:29:59 -0700 Subject: [PATCH 2/3] mypy --- qualtran/bloqs/mod_arithmetic/coset_representation_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qualtran/bloqs/mod_arithmetic/coset_representation_test.py b/qualtran/bloqs/mod_arithmetic/coset_representation_test.py index 692224cf0..c2def4f01 100644 --- a/qualtran/bloqs/mod_arithmetic/coset_representation_test.py +++ b/qualtran/bloqs/mod_arithmetic/coset_representation_test.py @@ -31,7 +31,7 @@ def test_init_cost_representation_cost(): assert cost.total_toffoli_only() == 0 resolver = {v: 10**4 for v in sym_vars} upper_bound = (c + n) * (c + n - 1) + c - assert cost.clifford.subs(resolver) <= upper_bound.subs(resolver) + assert cost.clifford.subs(resolver) <= upper_bound.subs(resolver) # type: ignore[union-attr] def test_init_coset_representation(bloq_autotester): From 3bdf5617afa3b96a852bdd428027de3a67d134be Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 24 Mar 2025 18:31:17 -0700 Subject: [PATCH 3/3] nit --- qualtran/bloqs/mod_arithmetic/coset_representation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qualtran/bloqs/mod_arithmetic/coset_representation.py b/qualtran/bloqs/mod_arithmetic/coset_representation.py index 7651c282b..b7bfe50bc 100644 --- a/qualtran/bloqs/mod_arithmetic/coset_representation.py +++ b/qualtran/bloqs/mod_arithmetic/coset_representation.py @@ -69,7 +69,7 @@ def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': Hadamard(): self.c_pad, # The matrix representing the reversible operation $jN+x$ consists of 0s and 1s. # Thus it can be implemented using CNOTs using LUP decomposition or Gaussian elemenation - # using at most $n(n-1)$ CNOTs. + # utilizing at most $n(n-1)$ CNOTs. CNOT(): bitsize * (bitsize - 1), }