From 4f990dcaeda381943accd51df8f83cd8020b1327 Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Sun, 8 Oct 2023 22:03:56 +0000 Subject: [PATCH] Add more arithmetic gates. --- dev_tools/autogenerate-bloqs-notebooks.py | 3 + qualtran/bloqs/arithmetic.ipynb | 113 ++++++++++++++++++- qualtran/bloqs/arithmetic.py | 126 ++++++++++++++++++++-- qualtran/bloqs/arithmetic_test.py | 42 ++++++++ 4 files changed, 277 insertions(+), 7 deletions(-) diff --git a/dev_tools/autogenerate-bloqs-notebooks.py b/dev_tools/autogenerate-bloqs-notebooks.py index 2755c558d..50f9f672c 100644 --- a/dev_tools/autogenerate-bloqs-notebooks.py +++ b/dev_tools/autogenerate-bloqs-notebooks.py @@ -112,6 +112,9 @@ BloqNbSpec(qualtran.bloqs.arithmetic_test._make_square), BloqNbSpec(qualtran.bloqs.arithmetic_test._make_sum_of_squares), BloqNbSpec(qualtran.bloqs.arithmetic_test._make_greater_than), + BloqNbSpec(qualtran.bloqs.arithmetic_test._make_greater_than_constant), + BloqNbSpec(qualtran.bloqs.arithmetic_test._make_equals_a_constant), + BloqNbSpec(qualtran.bloqs.arithmetic_test._make_to_contiguous_index), BloqNbSpec(qualtran.bloqs.arithmetic_test._make_scale_int_by_real), BloqNbSpec(qualtran.bloqs.arithmetic_test._make_multiply_two_reals), BloqNbSpec(qualtran.bloqs.arithmetic_test._make_square_real_number), diff --git a/qualtran/bloqs/arithmetic.ipynb b/qualtran/bloqs/arithmetic.ipynb index 0d0dd3f43..36a3c8989 100644 --- a/qualtran/bloqs/arithmetic.ipynb +++ b/qualtran/bloqs/arithmetic.ipynb @@ -198,7 +198,6 @@ "Implements $U|a\\rangle|b\\rangle|0\\rangle \\rightarrow\n", "|a\\rangle|b\\rangle|a > b\\rangle$ using $8n T$ gates.\n", "\n", - "\n", "#### Parameters\n", " - `bitsize`: Number of bits used to represent the two integers a and b. \n", "\n", @@ -361,6 +360,118 @@ "bloq = SquareRealNumber(bitsize=10)\n", "show_bloq(bloq)" ] + }, + { + "cell_type": "markdown", + "id": "c63ef890", + "metadata": { + "cq.autogen": "_make_greater_than_constant.md" + }, + "source": [ + "## `GreaterThanConstant`\n", + "Implements $U_a|x\\rangle = U_a|x\\rangle|z\\rangle = |x\\rangle |z ^ (x > a)\\rangle\"\n", + "\n", + "#### Parameters\n", + " - `bitsize`: bitsize of x register.\n", + " - `val`: integer to compare x against (a above.) \n", + "\n", + "#### Registers\n", + " - `- x`: Register to compare against val.\n", + " - `- result`: Register to hold result of comparison.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e03bf9e1", + "metadata": { + "cq.autogen": "_make_greater_than_constant.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.arithmetic import GreaterThanConstant\n", + "\n", + "bloq = GreaterThanConstant(bitsize=4, val=13)\n", + "show_bloq(bloq)" + ] + }, + { + "cell_type": "markdown", + "id": "c317ee1b", + "metadata": { + "cq.autogen": "_make_equals_a_constant.md" + }, + "source": [ + "## `EqualsAConstant`\n", + "Implements $U_a|x\\rangle = U_a|x\\rangle|z\\rangle = |x\\rangle |z ^ (x == a)\\rangle\"\n", + "\n", + "#### Parameters\n", + " - `bitsize`: bitsize of x register.\n", + " - `val`: integer to compare x against (a above.) \n", + "\n", + "#### Registers\n", + " - `- x`: Register to compare against val.\n", + " - `- result`: Register to hold result of comparison.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ad925b70", + "metadata": { + "cq.autogen": "_make_equals_a_constant.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.arithmetic import EqualsAConstant\n", + "\n", + "bloq = EqualsAConstant(bitsize=4, val=13)\n", + "show_bloq(bloq)" + ] + }, + { + "cell_type": "markdown", + "id": "3aeab187", + "metadata": { + "cq.autogen": "_make_to_contiguous_index.md" + }, + "source": [ + "## `ToContiguousIndex`\n", + "Build a contiguous register s from mu and nu.\n", + "\n", + "$$\n", + " s = \\nu (\\nu + 1) / 2 + \\mu\n", + "$$\n", + "\n", + "Assuming nu is zero indexed (in contrast to the THC paper which assumes 1,\n", + "hence the slightly different formula).\n", + "\n", + "#### Parameters\n", + " - `bitsize`: number of bits for mu and nu registers.\n", + " - `s_bitsize`: Number of bits for contiguous register. \n", + "\n", + "Registers\n", + " - mu, nu: input registers\n", + " - s: output contiguous register\n", + "\n", + "#### References\n", + "(Even more efficient quantum computations of chemistry through tensor hypercontraction)[https://arxiv.org/pdf/2011.03494.pdf] Eq. 29.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6ba10227", + "metadata": { + "cq.autogen": "_make_to_contiguous_index.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.arithmetic import ToContiguousIndex\n", + "\n", + "bloq = ToContiguousIndex(bitsize=4, s_bitsize=8)\n", + "show_bloq(bloq)" + ] } ], "metadata": { diff --git a/qualtran/bloqs/arithmetic.py b/qualtran/bloqs/arithmetic.py index 57ac5810a..0f5bcd7f4 100644 --- a/qualtran/bloqs/arithmetic.py +++ b/qualtran/bloqs/arithmetic.py @@ -12,8 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Set, Tuple, TYPE_CHECKING +from functools import cached_property +from typing import Dict, Optional, Set, Tuple, TYPE_CHECKING, Union +import sympy from attrs import frozen from cirq_ft import TComplexity @@ -421,7 +423,6 @@ class GreaterThan(Bloq): Implements $U|a\rangle|b\rangle|0\rangle \rightarrow |a\rangle|b\rangle|a > b\rangle$ using $8n T$ gates. - Args: bitsize: Number of bits used to represent the two integers a and b. @@ -445,10 +446,123 @@ def pretty_name(self) -> str: return "a gt b" def t_complexity(self): - # TODO Determine precise clifford count and/or ignore. - # See: https://github.com/quantumlib/cirq-qubitization/issues/219 - # See: https://github.com/quantumlib/cirq-qubitization/issues/217 return TComplexity(t=8 * self.bitsize) + def bloq_counts( + self, ssa: Optional['SympySymbolAllocator'] = None + ) -> Set[Tuple[Union[int, sympy.Expr], Bloq]]: + return {(8*self.bitsize, TGate())} + + +@frozen +class GreaterThanConstant(Bloq): + r"""Implements $U_a|x\rangle = U_a|x\rangle|z\rangle = |x\rangle |z ^ (x > a)\rangle" + + Args: + bitsize: bitsize of x register. + val: integer to compare x against (a above.) + + Registers: + - x: Register to compare against val. + - result: Register to hold result of comparison. + """ + + bitsize: int + val: int + + @cached_property + def signature(self) -> Signature: + return Signature.build(x=self.bitsize, result=1) + + def t_complexity(self): + return TComplexity(t=4 * self.bitsize) + + def bloq_counts( + self, ssa: Optional['SympySymbolAllocator'] = None + ) -> Set[Tuple[Union[int, sympy.Expr], Bloq]]: + return {(4*self.bitsize, TGate())} + + +@frozen +class EqualsAConstant(Bloq): + r"""Implements $U_a|x\rangle = U_a|x\rangle|z\rangle = |x\rangle |z ^ (x == a)\rangle" + + Args: + bitsize: bitsize of x register. + val: integer to compare x against (a above.) + + Registers: + - x: Register to compare against val. + - result: Register to hold result of comparison. + """ + + bitsize: int + val: int + + @cached_property + def signature(self) -> Signature: + return Signature.build(x=self.bitsize, result=1) + + def t_complexity(self): + return TComplexity(t=4 * self.bitsize) + + def bloq_counts( + self, ssa: Optional['SympySymbolAllocator'] = None + ) -> Set[Tuple[Union[int, sympy.Expr], Bloq]]: + return {(4*self.bitsize, TGate())} + + +@frozen +class ToContiguousIndex(Bloq): + r"""Build a contiguous register s from mu and nu. + + $$ + s = \nu (\nu + 1) / 2 + \mu + $$ + + Assuming nu is zero indexed (in contrast to the THC paper which assumes 1, + hence the slightly different formula). + + Args: + bitsize: number of bits for mu and nu registers. + s_bitsize: Number of bits for contiguous register. + + Registers + - mu, nu: input registers + - s: output contiguous register + + References: + (Even more efficient quantum computations of chemistry through + tensor hypercontraction)[https://arxiv.org/pdf/2011.03494.pdf] Eq. 29. + """ + + bitsize: int + s_bitsize: int + + @cached_property + def signature(self) -> Signature: + return Signature( + [ + Register("mu", bitsize=self.bitsize), + Register("nu", bitsize=self.bitsize), + Register("s", bitsize=self.s_bitsize), + ] + ) + + def on_classical_vals( + self, mu: 'ClassicalValT', nu: 'ClassicalValT' + ) -> Dict[str, 'ClassicalValT']: + return {'mu': mu, 'nu': nu, 's': nu * (nu + 1) // 2 + mu} + + def bloq_counts( + self, ssa: Optional['SympySymbolAllocator'] = None + ) -> Set[Tuple[Union[int, sympy.Expr], Bloq]]: + return {(4 * (self.bitsize**2 + self.bitsize - 1), TGate())} + + def t_complexity(self) -> 'cirq_ft.TComplexity': + num_toffoli = self.bitsize**2 + self.bitsize - 1 + return TComplexity(t=4 * num_toffoli) + def bloq_counts(self, ssa: Optional['SympySymbolAllocator'] = None) -> Set[Tuple[int, Bloq]]: - return {(8 * self.bitsize, TGate())} + num_toffoli = self.bitsize**2 + self.bitsize - 1 + return {(num_toffoli, TGate())} diff --git a/qualtran/bloqs/arithmetic_test.py b/qualtran/bloqs/arithmetic_test.py index ca3f7476d..955ccbc4a 100644 --- a/qualtran/bloqs/arithmetic_test.py +++ b/qualtran/bloqs/arithmetic_test.py @@ -15,7 +15,9 @@ from qualtran import BloqBuilder, Register from qualtran.bloqs.arithmetic import ( Add, + EqualsAConstant, GreaterThan, + GreaterThanConstant, MultiplyTwoReals, OutOfPlaceAdder, Product, @@ -23,6 +25,7 @@ Square, SquareRealNumber, SumOfSquares, + ToContiguousIndex, ) from qualtran.testing import execute_notebook @@ -56,6 +59,20 @@ def _make_greater_than(): return GreaterThan(bitsize=4) +def _make_greater_than_constant(): + from qualtran.bloqs.arithmetic import GreaterThanConstant + + return GreaterThanConstant(bitsize=4, val=13) + +def _make_equals_a_constant(): + from qualtran.bloqs.arithmetic import EqualsAConstant + + return EqualsAConstant(bitsize=4, val=13) + +def _make_to_contiguous_index(): + from qualtran.bloqs.arithmetic import ToContiguousIndex + + return ToContiguousIndex(bitsize=4, s_bitsize=8) def _make_scale_int_by_real(): from qualtran.bloqs.arithmetic import ScaleIntByReal @@ -155,6 +172,31 @@ def test_greater_than(): q0, q1, anc = bb.add(GreaterThan(bitsize), a=q0, b=q1, result=anc) cbloq = bb.finalize(a=q0, b=q1, result=anc) +def test_greater_than_constant(): + bb = BloqBuilder() + bitsize = 5 + q0 = bb.add_register('x', bitsize) + anc = bb.add_register('result', 1) + q0, anc = bb.add(GreaterThanConstant(bitsize, 17), x=q0, result=anc) + cbloq = bb.finalize(x=q0, result=anc) + +def test_equals_a_constant(): + bb = BloqBuilder() + bitsize = 5 + q0 = bb.add_register('x', bitsize) + anc = bb.add_register('result', 1) + q0, anc = bb.add(EqualsAConstant(bitsize, 17), x=q0, result=anc) + cbloq = bb.finalize(x=q0, result=anc) + +def test_to_contiguous_index(): + bb = BloqBuilder() + bitsize = 5 + q0 = bb.add_register('mu', bitsize) + q1 = bb.add_register('nu', bitsize) + out = bb.add_register('s', 1) + q0, q1, out = bb.add(ToContiguousIndex(bitsize, 2*bitsize), mu=q0, nu=q1, s=out) + cbloq = bb.finalize(mu=q0, nu=q1, s=out) + def test_notebook(): execute_notebook('arithmetic')