diff --git a/cirq_qubitization/bloq_algos/__init__.py b/cirq_qubitization/bloq_algos/__init__.py index fcb484f52b..91940c50d5 100644 --- a/cirq_qubitization/bloq_algos/__init__.py +++ b/cirq_qubitization/bloq_algos/__init__.py @@ -1 +1 @@ -from . import and_bloq, basic_gates +from . import and_bloq, arithmetic, basic_gates, sorting diff --git a/cirq_qubitization/bloq_algos/arithmetic.ipynb b/cirq_qubitization/bloq_algos/arithmetic.ipynb new file mode 100644 index 0000000000..6d027bd05a --- /dev/null +++ b/cirq_qubitization/bloq_algos/arithmetic.ipynb @@ -0,0 +1,253 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "40368a5b", + "metadata": { + "cq.autogen": "title_cell" + }, + "source": [ + "# Arithmetic" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "410926b9", + "metadata": { + "cq.autogen": "top_imports" + }, + "outputs": [], + "source": [ + "import cirq\n", + "import numpy as np\n", + "import cirq_qubitization\n", + "import cirq_ft\n", + "import cirq_ft.infra.testing as cq_testing\n", + "from cirq_qubitization.jupyter_tools import display_gate_and_compilation, show_bloq\n", + "from typing import *" + ] + }, + { + "cell_type": "markdown", + "id": "b92171ba", + "metadata": { + "cq.autogen": "_make_add.md" + }, + "source": [ + "## `Add`\n", + "An n-bit addition gate.\n", + "\n", + "Implements $U|a\\rangle|b\\rangle \\rightarrow |a\\rangle|a+b\\rangle$ using $4n - 4 T$ gates.\n", + "\n", + "#### Parameters\n", + " - `bitsize`: Number of bits used to represent each integer. Must be large enough to hold the result in the output register of a + b. \n", + "\n", + "Registers:\n", + " - a: A bitsize-sized input register (register a above).\n", + " - b: A bitsize-sized input/output register (register b above).\n", + "\n", + "#### References\n", + "[Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f930d4ff", + "metadata": { + "cq.autogen": "_make_add.py" + }, + "outputs": [], + "source": [ + "from cirq_qubitization.bloq_algos.arithmetic import Add\n", + "\n", + "bloq = Add(bitsize=4)\n", + "show_bloq(bloq)" + ] + }, + { + "cell_type": "markdown", + "id": "3b20c805", + "metadata": { + "cq.autogen": "_make_product.md" + }, + "source": [ + "## `Product`\n", + "Compute the product of an `n` and `m` bit integer.\n", + "\n", + "Implements $U|a\\rangle|b\\rangle|0\\rangle -\\rightarrow\n", + "|a\\rangle|b\\rangle|a\\times b\\rangle$ using $2nm-n$ Toffolis.\n", + "\n", + "#### Parameters\n", + " - `a_bitsize`: Number of bits used to represent the first integer.\n", + " - `b_bitsize`: Number of bits used to represent the second integer. \n", + "\n", + "Registers:\n", + " - a: a_bitsize-sized input register.\n", + " - b: b_bitsize-sized input register.\n", + " - result: A 2*max(a_bitsize, b_bitsize) bit-sized output register to store\n", + " the result a*b.\n", + "\n", + "#### References\n", + "[Fault-Tolerant Quantum Simulations of Chemistry in First Quantization](https://arxiv.org/abs/2105.12767) pg 81 gives a Toffoli complexity for multiplying two numbers.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "877b583b", + "metadata": { + "cq.autogen": "_make_product.py" + }, + "outputs": [], + "source": [ + "from cirq_qubitization.bloq_algos.arithmetic import Product\n", + "\n", + "bloq = Product(a_bitsize=4, b_bitsize=6)\n", + "show_bloq(bloq)" + ] + }, + { + "cell_type": "markdown", + "id": "f50caf16", + "metadata": { + "cq.autogen": "_make_square.md" + }, + "source": [ + "## `Square`\n", + "Square an n-bit number.\n", + "\n", + "Implements $U|a\\rangle|0\\rangle \\rightarrow |a\\rangle|a^2\\rangle$ using $4n - 4 T$ gates.\n", + "\n", + "#### Parameters\n", + " - `bitsize`: Number of bits used to represent the integer to be squared. The result is stored in a register of size 2*bitsize. \n", + "\n", + "Registers:\n", + " - a: A bitsize-sized input register (register a above).\n", + " - result: A 2-bitsize-sized input/ouput register.\n", + "\n", + "#### References\n", + "[Fault-Tolerant Quantum Simulations of Chemistry in First Quantization](https://arxiv.org/abs/2105.12767). pg 76 for Toffoli complexity.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "633eb3ba", + "metadata": { + "cq.autogen": "_make_square.py" + }, + "outputs": [], + "source": [ + "from cirq_qubitization.bloq_algos.arithmetic import Square\n", + "\n", + "bloq = Square(bitsize=8)\n", + "show_bloq(bloq)" + ] + }, + { + "cell_type": "markdown", + "id": "18e2cca7", + "metadata": { + "cq.autogen": "_make_sum_of_squares.md" + }, + "source": [ + "## `SumOfSquares`\n", + "Compute the sum of squares of k n-bit numbers.\n", + "\n", + "Implements $U|a\\rangle|b\\rangle\\dots k\\rangle|0\\rangle \\rightarrow\n", + " |a\\rangle|b\\rangle\\dots|k\\rangle|a^2+b^2+\\dots k^2\\rangle$ using\n", + " $4 k n^2 T$ gates.\n", + "\n", + "#### Parameters\n", + " - `bitsize`: Number of bits used to represent each of the k integers.\n", + " - `k`: The number of integers we want to square. \n", + "\n", + "Registers:\n", + " - input: k n-bit registers.\n", + " - result: 2 * bitsize + 1 sized output register.\n", + "\n", + "#### References\n", + "[Fault-Tolerant Quantum Simulations of Chemistry in First Quantization](https://arxiv.org/abs/2105.12767) pg 80 give a Toffoli complexity for squaring.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "95d0069d", + "metadata": { + "cq.autogen": "_make_sum_of_squares.py" + }, + "outputs": [], + "source": [ + "from cirq_qubitization.bloq_algos.arithmetic import SumOfSquares\n", + "\n", + "bloq = SumOfSquares(bitsize=8, k=4)\n", + "show_bloq(bloq)" + ] + }, + { + "cell_type": "markdown", + "id": "20639a7a", + "metadata": { + "cq.autogen": "_make_greater_than.md" + }, + "source": [ + "## `GreaterThan`\n", + "Compare two n-bit integers.\n", + "\n", + "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", + "Registers:\n", + " - a: n-bit-sized input registers.\n", + " - b: n-bit-sized input registers.\n", + " - result: A single bit output register to store the result of A > B.\n", + "\n", + "#### References\n", + "[Improved techniques for preparing eigenstates of fermionic Hamiltonians](https://www.nature.com/articles/s41534-018-0071-5#additional-information), Comparison Oracle from SI: Appendix 2B (pg 3)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "af66690c", + "metadata": { + "cq.autogen": "_make_greater_than.py" + }, + "outputs": [], + "source": [ + "from cirq_qubitization.bloq_algos.arithmetic import GreaterThan\n", + "\n", + "bloq = GreaterThan(bitsize=4)\n", + "show_bloq(bloq)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/cirq_qubitization/bloq_algos/arithmetic.py b/cirq_qubitization/bloq_algos/arithmetic.py new file mode 100644 index 0000000000..6b37fcb1f2 --- /dev/null +++ b/cirq_qubitization/bloq_algos/arithmetic.py @@ -0,0 +1,201 @@ +from attrs import frozen +from cirq_ft import TComplexity + +from cirq_qubitization.quantum_graph.bloq import Bloq +from cirq_qubitization.quantum_graph.fancy_registers import FancyRegister, FancyRegisters + + +@frozen +class Add(Bloq): + r"""An n-bit addition gate. + + Implements $U|a\rangle|b\rangle \rightarrow |a\rangle|a+b\rangle$ using $4n - 4 T$ gates. + + Args: + bitsize: Number of bits used to represent each integer. Must be large + enough to hold the result in the output register of a + b. + + Registers: + - a: A bitsize-sized input register (register a above). + - b: A bitsize-sized input/output register (register b above). + + References: + [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648) + """ + + bitsize: int + + @property + def registers(self): + return FancyRegisters.build(a=self.bitsize, b=self.bitsize) + + def pretty_name(self) -> str: + return "a + b" + + def t_complexity(self): + num_clifford = (self.bitsize - 2) * 19 + 16 + num_t_gates = 4 * self.bitsize - 4 + return TComplexity(t=num_t_gates, clifford=num_clifford) + + +@frozen +class Square(Bloq): + r"""Square an n-bit number. + + Implements $U|a\rangle|0\rangle \rightarrow |a\rangle|a^2\rangle$ using $4n - 4 T$ gates. + + Args: + bitsize: Number of bits used to represent the integer to be squared. The + result is stored in a register of size 2*bitsize. + + Registers: + - a: A bitsize-sized input register (register a above). + - result: A 2-bitsize-sized input/ouput register. + + References: + [Fault-Tolerant Quantum Simulations of Chemistry in First + Quantization](https://arxiv.org/abs/2105.12767). pg 76 for Toffoli complexity. + """ + + bitsize: int + + @property + def registers(self): + return FancyRegisters.build(a=self.bitsize, result=2 * self.bitsize) + + def pretty_name(self) -> str: + return "a^2" + + 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 + num_toff = self.bitsize * (self.bitsize - 1) + return TComplexity(t=4 * num_toff) + + +@frozen +class SumOfSquares(Bloq): + r"""Compute the sum of squares of k n-bit numbers. + + Implements $U|a\rangle|b\rangle\dots k\rangle|0\rangle \rightarrow + |a\rangle|b\rangle\dots|k\rangle|a^2+b^2+\dots k^2\rangle$ using + $4 k n^2 T$ gates. + + Args: + bitsize: Number of bits used to represent each of the k integers. + k: The number of integers we want to square. + + Registers: + - input: k n-bit registers. + - result: 2 * bitsize + 1 sized output register. + + References: + [Fault-Tolerant Quantum Simulations of Chemistry in First + Quantization](https://arxiv.org/abs/2105.12767) pg 80 give a Toffoli + complexity for squaring. + """ + + bitsize: int + k: int + + @property + def registers(self): + return FancyRegisters( + [ + FancyRegister("input", bitsize=self.bitsize, wireshape=(self.k,)), + FancyRegister("result", bitsize=2 * self.bitsize + 1), + ] + ) + + def short_name(self) -> str: + return "SOS" + + 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 + num_toff = self.k * self.bitsize**2 - self.bitsize + if self.k % 3 == 0: + num_toff -= 1 + return TComplexity(t=4 * num_toff) + + +@frozen +class Product(Bloq): + r"""Compute the product of an `n` and `m` bit integer. + + Implements $U|a\rangle|b\rangle|0\rangle -\rightarrow + |a\rangle|b\rangle|a\times b\rangle$ using $2nm-n$ Toffolis. + + Args: + a_bitsize: Number of bits used to represent the first integer. + b_bitsize: Number of bits used to represent the second integer. + + Registers: + - a: a_bitsize-sized input register. + - b: b_bitsize-sized input register. + - result: A 2*max(a_bitsize, b_bitsize) bit-sized output register to store + the result a*b. + + References: + [Fault-Tolerant Quantum Simulations of Chemistry in First + Quantization](https://arxiv.org/abs/2105.12767) pg 81 gives a Toffoli + complexity for multiplying two numbers. + """ + + a_bitsize: int + b_bitsize: int + + @property + def registers(self): + return FancyRegisters.build( + a=self.a_bitsize, b=self.b_bitsize, result=2 * max(self.a_bitsize, self.b_bitsize) + ) + + def pretty_name(self) -> str: + return "a*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 + num_toff = 2 * self.a_bitsize * self.b_bitsize - max(self.a_bitsize, self.b_bitsize) + return TComplexity(t=4 * num_toff) + + +@frozen +class GreaterThan(Bloq): + r"""Compare two n-bit integers. + + 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. + + Registers: + - a: n-bit-sized input registers. + - b: n-bit-sized input registers. + - result: A single bit output register to store the result of A > B. + + References: + [Improved techniques for preparing eigenstates of fermionic + Hamiltonians](https://www.nature.com/articles/s41534-018-0071-5#additional-information), + Comparison Oracle from SI: Appendix 2B (pg 3) + """ + bitsize: int + + @property + def registers(self): + return FancyRegisters.build(a=self.bitsize, b=self.bitsize, result=1) + + 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) diff --git a/cirq_qubitization/bloq_algos/arithmetic_test.py b/cirq_qubitization/bloq_algos/arithmetic_test.py new file mode 100644 index 0000000000..138e1511c0 --- /dev/null +++ b/cirq_qubitization/bloq_algos/arithmetic_test.py @@ -0,0 +1,87 @@ +from cirq_qubitization.bloq_algos.arithmetic import Add, GreaterThan, Product, Square, SumOfSquares +from cirq_qubitization.jupyter_tools import execute_notebook +from cirq_qubitization.quantum_graph.composite_bloq import CompositeBloqBuilder +from cirq_qubitization.quantum_graph.fancy_registers import FancyRegister + + +def _make_add(): + from cirq_qubitization.bloq_algos.arithmetic import Add + + return Add(bitsize=4) + + +def _make_square(): + from cirq_qubitization.bloq_algos.arithmetic import Square + + return Square(bitsize=8) + + +def _make_sum_of_squares(): + from cirq_qubitization.bloq_algos.arithmetic import SumOfSquares + + return SumOfSquares(bitsize=8, k=4) + + +def _make_product(): + from cirq_qubitization.bloq_algos.arithmetic import Product + + return Product(a_bitsize=4, b_bitsize=6) + + +def _make_greater_than(): + from cirq_qubitization.bloq_algos.arithmetic import GreaterThan + + return GreaterThan(bitsize=4) + + +def test_add(): + bb = CompositeBloqBuilder() + bitsize = 4 + q0 = bb.add_register('a', bitsize) + q1 = bb.add_register('b', bitsize) + a, b = bb.add(Add(bitsize), a=q0, b=q1) + cbloq = bb.finalize(a=a, b=b) + + +def test_square(): + bb = CompositeBloqBuilder() + bitsize = 4 + q0 = bb.add_register('a', bitsize) + q1 = bb.add_register('result', 2 * bitsize) + q0, q1 = bb.add(Square(bitsize), a=q0, result=q1) + cbloq = bb.finalize(a=q0, result=q1) + + +def test_sum_of_squares(): + bb = CompositeBloqBuilder() + bitsize = 4 + k = 3 + inp = bb.add_register(FancyRegister("input", bitsize=bitsize, wireshape=(k,))) + out = bb.add_register(FancyRegister("result", bitsize=2 * bitsize + 1)) + inp, out = bb.add(SumOfSquares(bitsize, k), input=inp, result=out) + cbloq = bb.finalize(input=inp, result=out) + + +def test_product(): + bb = CompositeBloqBuilder() + bitsize = 5 + mbits = 3 + q0 = bb.add_register('a', bitsize) + q1 = bb.add_register('b', mbits) + q2 = bb.add_register('result', 2 * max(bitsize, mbits)) + q0, q1, q2 = bb.add(Product(bitsize, mbits), a=q0, b=q1, result=q2) + cbloq = bb.finalize(a=q0, b=q1, result=q2) + + +def test_greater_than(): + bb = CompositeBloqBuilder() + bitsize = 5 + q0 = bb.add_register('a', bitsize) + q1 = bb.add_register('b', bitsize) + anc = bb.add_register('result', 1) + q0, q1, anc = bb.add(GreaterThan(bitsize), a=q0, b=q1, result=anc) + cbloq = bb.finalize(a=q0, b=q1, result=anc) + + +def test_notebook(): + execute_notebook('arithmetic') diff --git a/cirq_qubitization/bloq_algos/basic_gates.ipynb b/cirq_qubitization/bloq_algos/basic_gates.ipynb index c6ea73a821..aeb68774cf 100644 --- a/cirq_qubitization/bloq_algos/basic_gates.ipynb +++ b/cirq_qubitization/bloq_algos/basic_gates.ipynb @@ -200,6 +200,42 @@ "\n", "tn.draw(color=['COPY', 'XOR', '0', '+'], show_tags=False, initial_layout='spectral')" ] + }, + { + "cell_type": "markdown", + "id": "8846fb21", + "metadata": { + "cq.autogen": "_make_Rz.md" + }, + "source": [ + "## `Rz`\n", + "Single-qubit Rz gate.\n", + "\n", + "#### Parameters\n", + " - `angle`: Rotation angle.\n", + " - `eps`: precision for implementation of rotation. \n", + "\n", + "Registers:\n", + " - q: One-bit register.\n", + "\n", + "#### References\n", + "[Efficient synthesis of universal Repeat-Until-Success circuits](https://arxiv.org/abs/1404.5320), which offers a small improvement [Optimal ancilla-free Clifford+T approximation of z-rotations](https://arxiv.org/pdf/1403.2975.pdf).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82f2d005", + "metadata": { + "cq.autogen": "_make_Rz.py" + }, + "outputs": [], + "source": [ + "from cirq_qubitization.bloq_algos.basic_gates import Rz\n", + "\n", + "bloq = Rz(angle=np.pi / 4.0)\n", + "show_bloq(bloq)" + ] } ], "metadata": { diff --git a/cirq_qubitization/bloq_algos/basic_gates/__init__.py b/cirq_qubitization/bloq_algos/basic_gates/__init__.py index 882133ef76..919f6867f9 100644 --- a/cirq_qubitization/bloq_algos/basic_gates/__init__.py +++ b/cirq_qubitization/bloq_algos/basic_gates/__init__.py @@ -1,4 +1,5 @@ from .cnot import CNOT +from .rotation import Rx, Ry, Rz from .swap import CSwap, TwoBitCSwap, TwoBitSwap from .x_basis import MinusEffect, MinusState, PlusEffect, PlusState, XGate from .z_basis import IntEffect, IntState, OneEffect, OneState, ZeroEffect, ZeroState, ZGate diff --git a/cirq_qubitization/bloq_algos/basic_gates/rotation.py b/cirq_qubitization/bloq_algos/basic_gates/rotation.py new file mode 100644 index 0000000000..d3ef071101 --- /dev/null +++ b/cirq_qubitization/bloq_algos/basic_gates/rotation.py @@ -0,0 +1,68 @@ +from functools import cached_property +from typing import Dict, Tuple + +import numpy as np +from attrs import frozen +from cirq_ft import TComplexity + +from cirq_qubitization.quantum_graph.bloq import Bloq +from cirq_qubitization.quantum_graph.fancy_registers import FancyRegisters + + +@frozen +class Rz(Bloq): + """Single-qubit Rz gate. + + Args: + angle: Rotation angle. + eps: precision for implementation of rotation. + + Registers: + - q: One-bit register. + + References: + [Efficient synthesis of universal Repeat-Until-Success + circuits](https://arxiv.org/abs/1404.5320), which offers a small improvement + [Optimal ancilla-free Clifford+T approximation + of z-rotations](https://arxiv.org/pdf/1403.2975.pdf). + """ + + angle: float + eps: float = 1e-11 + + @cached_property + def registers(self) -> 'FancyRegisters': + return FancyRegisters.build(q=1) + + def t_complexity(self): + # TODO Determine precise clifford count and/or ignore. + # This is an improvement over Ref. 2 from the docstring which provides + # a bound of 3 log(1/eps). + # See: https://github.com/quantumlib/cirq-qubitization/issues/219 + # See: https://github.com/quantumlib/cirq-qubitization/issues/217 + num_t = int(np.ceil(1.149 * np.log2(1.0 / self.eps) + 9.2)) + return TComplexity(t=num_t) + + def as_cirq_op(self, q: 'CirqQuregT') -> Tuple['cirq.Operation', Dict[str, 'CirqQuregT']]: + import cirq + + (q,) = q + return cirq.rz(self.angle).on(q), {'q': [q]} + + +@frozen +class Rx(Rz): + def as_cirq_op(self, q: 'CirqQuregT') -> Tuple['cirq.Operation', Dict[str, 'CirqQuregT']]: + import cirq + + (q,) = q + return cirq.rx(self.angle).on(q), {'q': [q]} + + +@frozen +class Ry(Rz): + def as_cirq_op(self, q: 'CirqQuregT') -> Tuple['cirq.Operation', Dict[str, 'CirqQuregT']]: + import cirq + + (q,) = q + return cirq.ry(self.angle).on(q), {'q': [q]} diff --git a/cirq_qubitization/bloq_algos/basic_gates/rotation_test.py b/cirq_qubitization/bloq_algos/basic_gates/rotation_test.py new file mode 100644 index 0000000000..577a1093d9 --- /dev/null +++ b/cirq_qubitization/bloq_algos/basic_gates/rotation_test.py @@ -0,0 +1,47 @@ +import cirq +import numpy as np +import pytest + +from cirq_qubitization.bloq_algos.basic_gates import Rx, Ry, Rz + + +def _make_Rx(): + from cirq_qubitization.bloq_algos.basic_gates import Rx + + return Rx(angle=np.pi / 4.0) + + +def _make_Ry(): + from cirq_qubitization.bloq_algos.basic_gates import Ry + + return Ry(angle=np.pi / 4.0) + + +def _make_Rz(): + from cirq_qubitization.bloq_algos.basic_gates import Rz + + return Rz(angle=np.pi / 4.0) + + +@pytest.mark.parametrize("rot_gate,tcount", [(Rx, 52), (Ry, 52), (Rz, 52)]) +def test_rotation_gates(rot_gate, tcount): + binst = rot_gate(angle=np.pi / 4.0) + assert binst.t_complexity().t == tcount + + +def test_as_cirq_op(): + bloq = Rx(angle=np.pi / 4.0, eps=1e-8) + quregs = bloq.registers.get_cirq_quregs() + op, _ = bloq.as_cirq_op(**quregs) + circuit = cirq.Circuit(op) + assert circuit == cirq.Circuit(cirq.Rx(rads=bloq.angle).on(cirq.NamedQubit("q"))) + bloq = Ry(angle=np.pi / 4.0, eps=1e-8) + quregs = bloq.registers.get_cirq_quregs() + op, _ = bloq.as_cirq_op(**quregs) + circuit = cirq.Circuit(op) + assert circuit == cirq.Circuit(cirq.Ry(rads=bloq.angle).on(cirq.NamedQubit("q"))) + bloq = Rz(angle=np.pi / 4.0, eps=1e-8) + quregs = bloq.registers.get_cirq_quregs() + op, _ = bloq.as_cirq_op(**quregs) + circuit = cirq.Circuit(op) + assert circuit == cirq.Circuit(cirq.Rz(rads=bloq.angle).on(cirq.NamedQubit("q"))) diff --git a/cirq_qubitization/bloq_algos/sorting.ipynb b/cirq_qubitization/bloq_algos/sorting.ipynb new file mode 100644 index 0000000000..d076e98a15 --- /dev/null +++ b/cirq_qubitization/bloq_algos/sorting.ipynb @@ -0,0 +1,135 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "60a1531e", + "metadata": { + "cq.autogen": "title_cell" + }, + "source": [ + "# Sorting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf61ba01", + "metadata": { + "cq.autogen": "top_imports" + }, + "outputs": [], + "source": [ + "import cirq\n", + "import numpy as np\n", + "import cirq_qubitization\n", + "import cirq_ft\n", + "import cirq_ft.infra.testing as cq_testing\n", + "from cirq_qubitization.jupyter_tools import display_gate_and_compilation, show_bloq\n", + "from typing import *" + ] + }, + { + "cell_type": "markdown", + "id": "6a182ade", + "metadata": { + "cq.autogen": "_make_comparator.md" + }, + "source": [ + "## `Comparator`\n", + "Compare and potentially swaps two n-bit numbers.\n", + "\n", + "Implements $U|a\\rangle|b\\rangle|0\\rangle \\rightarrow |\\min(a,b)\\rangle|\\max(a,b)\\rangle|a>b\\rangle$,\n", + "\n", + "where $a$ and $b$ are n-qubit quantum registers. On output a and b are\n", + "swapped if a > b. Forms the base primitive for sorting.\n", + "\n", + "#### Parameters\n", + " - `bitsize`: Number of bits used to represent each integer. \n", + "\n", + "Registers:\n", + " - a: A nbit-sized input register (register a above).\n", + " - b: A nbit-sized input register (register b above).\n", + " - out: A single bit output register which will store the result of the comparator.\n", + "\n", + "#### References\n", + "[Improved techniques for preparing eigenstates of fermionic Hamiltonians](https://www.nature.com/articles/s41534-018-0071-5), Fig. 1. in main text.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e191ccfe", + "metadata": { + "cq.autogen": "_make_comparator.py" + }, + "outputs": [], + "source": [ + "from cirq_qubitization.bloq_algos.sorting import Comparator\n", + "\n", + "bloq = Comparator(bitsize=4)\n", + "show_bloq(bloq)" + ] + }, + { + "cell_type": "markdown", + "id": "1e4ba443", + "metadata": { + "cq.autogen": "_make_bitonic_sort.md" + }, + "source": [ + "## `BitonicSort`\n", + "Sort k n-bit numbers.\n", + "\n", + "TODO: actually implement the algorithm using comparitor. Hiding ancilla cost\n", + " for the moment. Issue #219\n", + "\n", + "#### Parameters\n", + " - `bitsize`: Number of bits used to represent each integer.\n", + " - `k`: Number of integers to sort. \n", + "\n", + "Registers:\n", + " - input: A k-nbit-sized input register (register a above). List of integers\n", + " we want to sort.\n", + "\n", + "#### References\n", + "[Improved techniques for preparing eigenstates of fermionic Hamiltonians](https://www.nature.com/articles/s41534-018-0071-5), Supporting Information Sec. II.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8b54ab12", + "metadata": { + "cq.autogen": "_make_bitonic_sort.py" + }, + "outputs": [], + "source": [ + "from cirq_qubitization.bloq_algos.sorting import BitonicSort\n", + "\n", + "bloq = BitonicSort(bitsize=8, k=8)\n", + "show_bloq(bloq)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/cirq_qubitization/bloq_algos/sorting.py b/cirq_qubitization/bloq_algos/sorting.py new file mode 100644 index 0000000000..8ed3d25352 --- /dev/null +++ b/cirq_qubitization/bloq_algos/sorting.py @@ -0,0 +1,99 @@ +import numpy as np +from attrs import frozen +from cirq_ft import TComplexity + +from cirq_qubitization.bloq_algos.arithmetic import GreaterThan +from cirq_qubitization.quantum_graph.bloq import Bloq +from cirq_qubitization.quantum_graph.fancy_registers import FancyRegister, FancyRegisters, Side + + +@frozen +class Comparator(Bloq): + r"""Compare and potentially swaps two n-bit numbers. + + Implements $U|a\rangle|b\rangle|0\rangle \rightarrow |\min(a,b)\rangle|\max(a,b)\rangle|a>b\rangle$, + + where $a$ and $b$ are n-qubit quantum registers. On output a and b are + swapped if a > b. Forms the base primitive for sorting. + + Args: + bitsize: Number of bits used to represent each integer. + + Registers: + - a: A nbit-sized input register (register a above). + - b: A nbit-sized input register (register b above). + - out: A single bit output register which will store the result of the comparator. + + References: + [Improved techniques for preparing eigenstates of fermionic + Hamiltonians](https://www.nature.com/articles/s41534-018-0071-5), + Fig. 1. in main text. + """ + + bitsize: int + + @property + def registers(self): + return FancyRegisters( + [ + FancyRegister('a', 1, wireshape=(self.bitsize,)), + FancyRegister('b', 1, wireshape=(self.bitsize,)), + FancyRegister('out', 1, side=Side.RIGHT), + ] + ) + + def short_name(self) -> str: + return "Cmprtr" + + def t_complexity(self): + # complexity is from less than on two n qubit numbers + controlled swap + # Hard code for now until CSwap-Bloq is merged. + # See: https://github.com/quantumlib/cirq-qubitization/issues/219 + t_complexity = GreaterThan(self.bitsize).t_complexity() + t_complexity += TComplexity(t=14 * self.bitsize) + return t_complexity + + +@frozen +class BitonicSort(Bloq): + r"""Sort k n-bit numbers. + + TODO: actually implement the algorithm using comparitor. Hiding ancilla cost + for the moment. Issue #219 + + Args: + bitsize: Number of bits used to represent each integer. + k: Number of integers to sort. + + Registers: + - input: A k-nbit-sized input register (register a above). List of integers + we want to sort. + + References: + [Improved techniques for preparing eigenstates of fermionic + Hamiltonians](https://www.nature.com/articles/s41534-018-0071-5), + Supporting Information Sec. II. + """ + + bitsize: int + k: int + + @property + def registers(self): + return FancyRegisters( + [FancyRegister("input", bitsize=self.bitsize, wireshape=(self.bitsize,))] + ) + + def short_name(self) -> str: + return "BSort" + + def t_complexity(self): + # Need O(k * log^2(k)) comparisons. + # TODO: This is Big-O complexity. + # Should work out constant factors or + # See: https://github.com/quantumlib/cirq-qubitization/issues/219 + return ( + self.k + * int(np.ceil(max(np.log2(self.k) ** 2.0, 1))) + * Comparator(self.bitsize).t_complexity() + ) diff --git a/cirq_qubitization/bloq_algos/sorting_test.py b/cirq_qubitization/bloq_algos/sorting_test.py new file mode 100644 index 0000000000..3aa234976a --- /dev/null +++ b/cirq_qubitization/bloq_algos/sorting_test.py @@ -0,0 +1,36 @@ +import pytest + +from cirq_qubitization.bloq_algos.sorting import BitonicSort, Comparator +from cirq_qubitization.jupyter_tools import execute_notebook + + +def _make_comparator(): + from cirq_qubitization.bloq_algos.sorting import Comparator + + return Comparator(bitsize=4) + + +def _make_bitonic_sort(): + from cirq_qubitization.bloq_algos.sorting import BitonicSort + + return BitonicSort(bitsize=8, k=8) + + +def test_comparator(): + bloq = Comparator(4) + assert bloq.t_complexity().t == 88 + with pytest.raises(NotImplementedError): + bloq.decompose_bloq() + + +def test_bitonic_sort(): + bitsize = 4 + k = 8 + bloq = BitonicSort(bitsize, k) + assert bloq.t_complexity().t == 8 * 9 * 88 + with pytest.raises(NotImplementedError): + bloq.decompose_bloq() + + +def test_notebook(): + execute_notebook('sorting') diff --git a/cirq_qubitization/jupyter_autogen.py b/cirq_qubitization/jupyter_autogen.py index 85896dcb2b..18789799ff 100644 --- a/cirq_qubitization/jupyter_autogen.py +++ b/cirq_qubitization/jupyter_autogen.py @@ -40,9 +40,12 @@ from sphinx.ext.napoleon import Config, GoogleDocstring import cirq_qubitization.bloq_algos.and_bloq_test +import cirq_qubitization.bloq_algos.arithmetic_test import cirq_qubitization.bloq_algos.basic_gates.cnot_test +import cirq_qubitization.bloq_algos.basic_gates.rotation_test import cirq_qubitization.bloq_algos.basic_gates.swap_test import cirq_qubitization.bloq_algos.basic_gates.x_basis_test +import cirq_qubitization.bloq_algos.sorting_test import cirq_qubitization.bloq_algos.swap_network import cirq_qubitization.bloq_algos.swap_network_test import cirq_qubitization.quantum_graph @@ -134,6 +137,7 @@ def basename(self): gate_specs=[ BloqNbSpec(cirq_qubitization.bloq_algos.basic_gates.cnot_test._make_CNOT), BloqNbSpec(cirq_qubitization.bloq_algos.basic_gates.x_basis_test._make_plus_state), + BloqNbSpec(cirq_qubitization.bloq_algos.basic_gates.rotation_test._make_Rz), ], directory='./bloq_algos', ), @@ -146,6 +150,27 @@ def basename(self): ], directory='./bloq_algos', ), + NotebookSpec( + title='Arithmetic', + module=cirq_qubitization.bloq_algos.arithmetic, + gate_specs=[ + BloqNbSpec(cirq_qubitization.bloq_algos.arithmetic_test._make_add), + BloqNbSpec(cirq_qubitization.bloq_algos.arithmetic_test._make_product), + BloqNbSpec(cirq_qubitization.bloq_algos.arithmetic_test._make_square), + BloqNbSpec(cirq_qubitization.bloq_algos.arithmetic_test._make_sum_of_squares), + BloqNbSpec(cirq_qubitization.bloq_algos.arithmetic_test._make_greater_than), + ], + directory='./bloq_algos', + ), + NotebookSpec( + title='Sorting', + module=cirq_qubitization.bloq_algos.sorting, + gate_specs=[ + BloqNbSpec(cirq_qubitization.bloq_algos.sorting_test._make_comparator), + BloqNbSpec(cirq_qubitization.bloq_algos.sorting_test._make_bitonic_sort), + ], + directory='./bloq_algos', + ), ]