From 7344830491f7cbac8e43c9ec106be37a39cb94e8 Mon Sep 17 00:00:00 2001 From: Noureldin Date: Fri, 18 Oct 2024 23:51:10 +0100 Subject: [PATCH] Create linear half comparison bloqs (#1408) This PR creates all versions of half a comparison operation in $n$ toffoli complexity. The uncomputation of these bloqs has zero cost. The half comparason operation is needed in the modular inversion bloq. I also modify the OutOfPlaceAdder to allow it to not compute the extra And when it's not needed --- This PR also shows how we can implement all versions of comparison by implementing just one. for example creating a half greater than bloq that use logarithmic depth is enough to implement all the others. --- .../qualtran_dev_tools/notebook_specs.py | 4 + qualtran/bloqs/arithmetic/__init__.py | 4 + qualtran/bloqs/arithmetic/addition.ipynb | 6 +- qualtran/bloqs/arithmetic/addition.py | 28 +- qualtran/bloqs/arithmetic/comparison.ipynb | 452 +++++++++++++++++ qualtran/bloqs/arithmetic/comparison.py | 470 ++++++++++++++++++ qualtran/bloqs/arithmetic/comparison_test.py | 103 ++++ qualtran/serialization/resolver_dict.py | 4 + 8 files changed, 1063 insertions(+), 8 deletions(-) diff --git a/dev_tools/qualtran_dev_tools/notebook_specs.py b/dev_tools/qualtran_dev_tools/notebook_specs.py index 7c66311fe..6dc00babe 100644 --- a/dev_tools/qualtran_dev_tools/notebook_specs.py +++ b/dev_tools/qualtran_dev_tools/notebook_specs.py @@ -435,6 +435,10 @@ qualtran.bloqs.arithmetic.comparison._SQ_CMP_DOC, qualtran.bloqs.arithmetic.comparison._LEQ_DOC, qualtran.bloqs.arithmetic.comparison._CLinearDepthGreaterThan_DOC, + qualtran.bloqs.arithmetic.comparison._LINEAR_DEPTH_HALF_GREATERTHAN_DOC, + qualtran.bloqs.arithmetic.comparison._LINEAR_DEPTH_HALF_GREATERTHANEQUAL_DOC, + qualtran.bloqs.arithmetic.comparison._LINEAR_DEPTH_HALF_LESSTHAN_DOC, + qualtran.bloqs.arithmetic.comparison._LINEAR_DEPTH_HALF_LESSTHANEQUAL_DOC, ], ), NotebookSpecV2( diff --git a/qualtran/bloqs/arithmetic/__init__.py b/qualtran/bloqs/arithmetic/__init__.py index 533d0ee0c..59ca8a5af 100644 --- a/qualtran/bloqs/arithmetic/__init__.py +++ b/qualtran/bloqs/arithmetic/__init__.py @@ -23,6 +23,10 @@ GreaterThanConstant, LessThanConstant, LessThanEqual, + LinearDepthHalfGreaterThan, + LinearDepthHalfGreaterThanEqual, + LinearDepthHalfLessThan, + LinearDepthHalfLessThanEqual, SingleQubitCompare, ) from qualtran.bloqs.arithmetic.controlled_addition import CAdd diff --git a/qualtran/bloqs/arithmetic/addition.ipynb b/qualtran/bloqs/arithmetic/addition.ipynb index c9d271e14..4cd90a386 100644 --- a/qualtran/bloqs/arithmetic/addition.ipynb +++ b/qualtran/bloqs/arithmetic/addition.ipynb @@ -186,12 +186,14 @@ "using $4n - 4 T$ gates. Uncomputation requires 0 T-gates.\n", "\n", "#### Parameters\n", - " - `bitsize`: Number of bits used to represent each input integer. The allocated output register is of size `bitsize+1` so it has enough space to hold the sum of `a+b`. \n", + " - `bitsize`: Number of bits used to represent each input integer. The allocated output register is of size `bitsize+1` so it has enough space to hold the sum of `a+b`.\n", + " - `is_adjoint`: Whether this is compute or uncompute version.\n", + " - `include_most_significant_bit`: Whether to add an extra most significant (i.e. carry) bit. \n", "\n", "#### Registers\n", " - `a`: A bitsize-sized input register (register a above).\n", " - `b`: A bitsize-sized input register (register b above).\n", - " - `c`: A bitize+1-sized LEFT/RIGHT register depending on whether the gate adjoint or not. \n", + " - `c`: The LEFT/RIGHT register depending on whether the gate adjoint or not. This register size is either bitsize or bitsize+1 depending on the value of `include_most_significant_bit`. \n", "\n", "#### References\n", " - [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648). \n" diff --git a/qualtran/bloqs/arithmetic/addition.py b/qualtran/bloqs/arithmetic/addition.py index f0b8de6b9..7b208bd36 100644 --- a/qualtran/bloqs/arithmetic/addition.py +++ b/qualtran/bloqs/arithmetic/addition.py @@ -260,11 +260,15 @@ class OutOfPlaceAdder(GateWithRegisters, cirq.ArithmeticGate): # type: ignore[m Args: bitsize: Number of bits used to represent each input integer. The allocated output register is of size `bitsize+1` so it has enough space to hold the sum of `a+b`. + is_adjoint: Whether this is compute or uncompute version. + include_most_significant_bit: Whether to add an extra most significant (i.e. carry) bit. Registers: a: A bitsize-sized input register (register a above). b: A bitsize-sized input register (register b above). - c: A bitize+1-sized LEFT/RIGHT register depending on whether the gate adjoint or not. + c: The LEFT/RIGHT register depending on whether the gate adjoint or not. + This register size is either bitsize or bitsize+1 depending on + the value of `include_most_significant_bit`. References: [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648) @@ -272,6 +276,11 @@ class OutOfPlaceAdder(GateWithRegisters, cirq.ArithmeticGate): # type: ignore[m bitsize: 'SymbolicInt' is_adjoint: bool = False + include_most_significant_bit: bool = True + + @property + def out_bitsize(self): + return self.bitsize + (1 if self.include_most_significant_bit else 0) @property def signature(self): @@ -280,14 +289,14 @@ def signature(self): [ Register('a', QUInt(self.bitsize)), Register('b', QUInt(self.bitsize)), - Register('c', QUInt(self.bitsize + 1), side=side), + Register('c', QUInt(self.out_bitsize), side=side), ] ) def registers(self) -> Sequence[Union[int, Sequence[int]]]: if not isinstance(self.bitsize, int): raise ValueError(f'Symbolic bitsize {self.bitsize} not supported') - return [2] * self.bitsize, [2] * self.bitsize, [2] * (self.bitsize + 1) + return [2] * self.bitsize, [2] * self.bitsize, [2] * self.out_bitsize def apply(self, a: int, b: int, c: int) -> Tuple[int, int, int]: return a, b, c + a + b @@ -307,7 +316,7 @@ def on_classical_vals( return { 'a': a, 'b': b, - 'c': add_ints(int(a), int(b), num_bits=self.bitsize + 1, is_signed=False), + 'c': add_ints(int(a), int(b), num_bits=self.out_bitsize, is_signed=False), } def with_registers(self, *new_registers: Union[int, Sequence[int]]): @@ -328,12 +337,19 @@ def decompose_from_registers( cirq.CX(a[i], c[i + 1]), cirq.CX(b[i], c[i]), ] - for i in range(self.bitsize) + for i in range(self.out_bitsize - 1) ] + if not self.include_most_significant_bit: + # Update c[-1] as c[-1] ^= a[-1]^b[-1] + i = self.bitsize - 1 + optree.append([cirq.CX(a[i], c[i]), cirq.CX(b[i], c[i])]) return cirq.inverse(optree) if self.is_adjoint else optree def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': - return {And(uncompute=self.is_adjoint): self.bitsize, CNOT(): 5 * self.bitsize} + return { + And(uncompute=self.is_adjoint): self.out_bitsize - 1, + CNOT(): 5 * (self.bitsize - 1) + 2 + (3 if self.include_most_significant_bit else 0), + } def __pow__(self, power: int): if power == 1: diff --git a/qualtran/bloqs/arithmetic/comparison.ipynb b/qualtran/bloqs/arithmetic/comparison.ipynb index 4a0c8ad6d..5183ede00 100644 --- a/qualtran/bloqs/arithmetic/comparison.ipynb +++ b/qualtran/bloqs/arithmetic/comparison.ipynb @@ -1020,6 +1020,458 @@ "show_call_graph(equals_g)\n", "show_counts_sigma(equals_sigma)" ] + }, + { + "cell_type": "markdown", + "id": "e2db7c5d", + "metadata": { + "cq.autogen": "LinearDepthHalfGreaterThan.bloq_doc.md" + }, + "source": [ + "## `LinearDepthHalfGreaterThan`\n", + "Compare two integers while keeping necessary ancillas for zero cost uncomputation.\n", + "\n", + "Implements $\\ket{a}\\ket{b}\\ket{0}\\ket{0} \\rightarrow \\ket{a}\\ket{b}\\ket{b-a}\\ket{a>b}$ using $n$ And gates.\n", + "\n", + "This comparator relies on the fact that c = (b' + a)' = b - a. If a > b, then b - a < 0. We\n", + "implement it by flipping all the bits in b, computing the first half of the addition circuit,\n", + "copying out the carry, and keeping $c$ for the uncomputation.\n", + "\n", + "#### Parameters\n", + " - `dtype`: dtype of the two integers a and b.\n", + " - `uncompute`: whether this bloq uncomputes or computes the comparison. \n", + "\n", + "#### Registers\n", + " - `a`: first input register.\n", + " - `b`: second input register.\n", + " - `c`: ancilla register that will contain $b-a$ and will be used for uncomputation.\n", + " - `target`: A single bit output register to store the result of a > b. \n", + "\n", + "#### References\n", + " - [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648). \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "758a6e35", + "metadata": { + "cq.autogen": "LinearDepthHalfGreaterThan.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.arithmetic import LinearDepthHalfGreaterThan" + ] + }, + { + "cell_type": "markdown", + "id": "3ea5a7bc", + "metadata": { + "cq.autogen": "LinearDepthHalfGreaterThan.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26e4245f", + "metadata": { + "cq.autogen": "LinearDepthHalfGreaterThan.lineardepthhalfgreaterthan_small" + }, + "outputs": [], + "source": [ + "lineardepthhalfgreaterthan_small = LinearDepthHalfGreaterThan(QUInt(3))" + ] + }, + { + "cell_type": "markdown", + "id": "29abac9f", + "metadata": { + "cq.autogen": "LinearDepthHalfGreaterThan.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d065b007", + "metadata": { + "cq.autogen": "LinearDepthHalfGreaterThan.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([lineardepthhalfgreaterthan_small],\n", + " ['`lineardepthhalfgreaterthan_small`'])" + ] + }, + { + "cell_type": "markdown", + "id": "d67e1888", + "metadata": { + "cq.autogen": "LinearDepthHalfGreaterThan.call_graph.md" + }, + "source": [ + "### Call Graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32952025", + "metadata": { + "cq.autogen": "LinearDepthHalfGreaterThan.call_graph.py" + }, + "outputs": [], + "source": [ + "from qualtran.resource_counting.generalizers import ignore_split_join\n", + "lineardepthhalfgreaterthan_small_g, lineardepthhalfgreaterthan_small_sigma = lineardepthhalfgreaterthan_small.call_graph(max_depth=1, generalizer=ignore_split_join)\n", + "show_call_graph(lineardepthhalfgreaterthan_small_g)\n", + "show_counts_sigma(lineardepthhalfgreaterthan_small_sigma)" + ] + }, + { + "cell_type": "markdown", + "id": "9c39992e", + "metadata": { + "cq.autogen": "LinearDepthHalfGreaterThanEqual.bloq_doc.md" + }, + "source": [ + "## `LinearDepthHalfGreaterThanEqual`\n", + "Compare two integers while keeping necessary ancillas for zero cost uncomputation.\n", + "\n", + "Implements $\\ket{a}\\ket{b}\\ket{0}\\ket{0} \\rightarrow \\ket{a}\\ket{b}\\ket{a-b}\\ket{a \\geq b}$ using $n$ And gates.\n", + "\n", + "This comparator relies on the fact that c = (b' + a)' = b - a. If a > b, then b - a < 0. We\n", + "implement it by flipping all the bits in b, computing the first half of the addition circuit,\n", + "copying out the carry, and keeping $c$ for the uncomputation.\n", + "\n", + "#### Parameters\n", + " - `dtype`: dtype of the two integers a and b.\n", + " - `uncompute`: whether this bloq uncomputes or computes the comparison. \n", + "\n", + "#### Registers\n", + " - `a`: first input register.\n", + " - `b`: second input register.\n", + " - `c`: ancilla register that will contain $b-a$ and will be used for uncomputation.\n", + " - `target`: A single bit output register to store the result of a >= b. \n", + "\n", + "#### References\n", + " - [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648). \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "58e6973f", + "metadata": { + "cq.autogen": "LinearDepthHalfGreaterThanEqual.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.arithmetic import LinearDepthHalfGreaterThanEqual" + ] + }, + { + "cell_type": "markdown", + "id": "32dae58a", + "metadata": { + "cq.autogen": "LinearDepthHalfGreaterThanEqual.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eded8868", + "metadata": { + "cq.autogen": "LinearDepthHalfGreaterThanEqual.lineardepthhalfgreaterthanequal_small" + }, + "outputs": [], + "source": [ + "lineardepthhalfgreaterthanequal_small = LinearDepthHalfGreaterThanEqual(QUInt(3))" + ] + }, + { + "cell_type": "markdown", + "id": "0edbf55b", + "metadata": { + "cq.autogen": "LinearDepthHalfGreaterThanEqual.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5326975b", + "metadata": { + "cq.autogen": "LinearDepthHalfGreaterThanEqual.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([lineardepthhalfgreaterthanequal_small],\n", + " ['`lineardepthhalfgreaterthanequal_small`'])" + ] + }, + { + "cell_type": "markdown", + "id": "eecfbf65", + "metadata": { + "cq.autogen": "LinearDepthHalfGreaterThanEqual.call_graph.md" + }, + "source": [ + "### Call Graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "abfbe19f", + "metadata": { + "cq.autogen": "LinearDepthHalfGreaterThanEqual.call_graph.py" + }, + "outputs": [], + "source": [ + "from qualtran.resource_counting.generalizers import ignore_split_join\n", + "lineardepthhalfgreaterthanequal_small_g, lineardepthhalfgreaterthanequal_small_sigma = lineardepthhalfgreaterthanequal_small.call_graph(max_depth=1, generalizer=ignore_split_join)\n", + "show_call_graph(lineardepthhalfgreaterthanequal_small_g)\n", + "show_counts_sigma(lineardepthhalfgreaterthanequal_small_sigma)" + ] + }, + { + "cell_type": "markdown", + "id": "19744b75", + "metadata": { + "cq.autogen": "LinearDepthHalfLessThan.bloq_doc.md" + }, + "source": [ + "## `LinearDepthHalfLessThan`\n", + "Compare two integers while keeping necessary ancillas for zero cost uncomputation.\n", + "\n", + "Implements $\\ket{a}\\ket{b}\\ket{0}\\ket{0} \\rightarrow \\ket{a}\\ket{b}\\ket{a-b}\\ket{a b, then b - a < 0. We\n", + "implement it by flipping all the bits in b, computing the first half of the addition circuit,\n", + "copying out the carry, and keeping $c$ for the uncomputation.\n", + "\n", + "#### Parameters\n", + " - `dtype`: dtype of the two integers a and b.\n", + " - `uncompute`: whether this bloq uncomputes or computes the comparison. \n", + "\n", + "#### Registers\n", + " - `a`: first input register.\n", + " - `b`: second input register.\n", + " - `c`: ancilla register that will contain $b-a$ and will be used for uncomputation.\n", + " - `target`: A single bit output register to store the result of a < b. \n", + "\n", + "#### References\n", + " - [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648). \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1eec63a1", + "metadata": { + "cq.autogen": "LinearDepthHalfLessThan.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.arithmetic import LinearDepthHalfLessThan" + ] + }, + { + "cell_type": "markdown", + "id": "75142163", + "metadata": { + "cq.autogen": "LinearDepthHalfLessThan.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4759ae6d", + "metadata": { + "cq.autogen": "LinearDepthHalfLessThan.lineardepthhalflessthan_small" + }, + "outputs": [], + "source": [ + "lineardepthhalflessthan_small = LinearDepthHalfLessThan(QUInt(3))" + ] + }, + { + "cell_type": "markdown", + "id": "903efca4", + "metadata": { + "cq.autogen": "LinearDepthHalfLessThan.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bbbefc84", + "metadata": { + "cq.autogen": "LinearDepthHalfLessThan.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([lineardepthhalflessthan_small],\n", + " ['`lineardepthhalflessthan_small`'])" + ] + }, + { + "cell_type": "markdown", + "id": "8862065f", + "metadata": { + "cq.autogen": "LinearDepthHalfLessThan.call_graph.md" + }, + "source": [ + "### Call Graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9be045c4", + "metadata": { + "cq.autogen": "LinearDepthHalfLessThan.call_graph.py" + }, + "outputs": [], + "source": [ + "from qualtran.resource_counting.generalizers import ignore_split_join\n", + "lineardepthhalflessthan_small_g, lineardepthhalflessthan_small_sigma = lineardepthhalflessthan_small.call_graph(max_depth=1, generalizer=ignore_split_join)\n", + "show_call_graph(lineardepthhalflessthan_small_g)\n", + "show_counts_sigma(lineardepthhalflessthan_small_sigma)" + ] + }, + { + "cell_type": "markdown", + "id": "8a868719", + "metadata": { + "cq.autogen": "LinearDepthHalfLessThanEqual.bloq_doc.md" + }, + "source": [ + "## `LinearDepthHalfLessThanEqual`\n", + "Compare two integers while keeping necessary ancillas for zero cost uncomputation.\n", + "\n", + "Implements $\\ket{a}\\ket{b}\\ket{0}\\ket{0} \\rightarrow \\ket{a}\\ket{b}\\ket{b-a}\\ket{a \\leq b}$ using $n$ And gates.\n", + "\n", + "This comparator relies on the fact that c = (b' + a)' = b - a. If a > b, then b - a < 0. We\n", + "implement it by flipping all the bits in b, computing the first half of the addition circuit,\n", + "copying out the carry, and keeping $c$ for the uncomputation.\n", + "\n", + "#### Parameters\n", + " - `dtype`: dtype of the two integers a and b.\n", + " - `uncompute`: whether this bloq uncomputes or computes the comparison. \n", + "\n", + "#### Registers\n", + " - `a`: first input register.\n", + " - `b`: second input register.\n", + " - `c`: ancilla register that will contain $b-a$ and will be used for uncomputation.\n", + " - `target`: A single bit output register to store the result of a <= b. \n", + "\n", + "#### References\n", + " - [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648). \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b4c9b03", + "metadata": { + "cq.autogen": "LinearDepthHalfLessThanEqual.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.arithmetic import LinearDepthHalfLessThanEqual" + ] + }, + { + "cell_type": "markdown", + "id": "bae993de", + "metadata": { + "cq.autogen": "LinearDepthHalfLessThanEqual.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c6610fd4", + "metadata": { + "cq.autogen": "LinearDepthHalfLessThanEqual.lineardepthhalflessthanequal_small" + }, + "outputs": [], + "source": [ + "lineardepthhalflessthanequal_small = LinearDepthHalfLessThanEqual(QUInt(3))" + ] + }, + { + "cell_type": "markdown", + "id": "72de4f8e", + "metadata": { + "cq.autogen": "LinearDepthHalfLessThanEqual.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fdd8d4c4", + "metadata": { + "cq.autogen": "LinearDepthHalfLessThanEqual.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([lineardepthhalflessthanequal_small],\n", + " ['`lineardepthhalflessthanequal_small`'])" + ] + }, + { + "cell_type": "markdown", + "id": "973be9d4", + "metadata": { + "cq.autogen": "LinearDepthHalfLessThanEqual.call_graph.md" + }, + "source": [ + "### Call Graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9efd6db6", + "metadata": { + "cq.autogen": "LinearDepthHalfLessThanEqual.call_graph.py" + }, + "outputs": [], + "source": [ + "from qualtran.resource_counting.generalizers import ignore_split_join\n", + "lineardepthhalflessthanequal_small_g, lineardepthhalflessthanequal_small_sigma = lineardepthhalflessthanequal_small.call_graph(max_depth=1, generalizer=ignore_split_join)\n", + "show_call_graph(lineardepthhalflessthanequal_small_g)\n", + "show_counts_sigma(lineardepthhalflessthanequal_small_sigma)" + ] } ], "metadata": { diff --git a/qualtran/bloqs/arithmetic/comparison.py b/qualtran/bloqs/arithmetic/comparison.py index c546aee77..b28269ae4 100644 --- a/qualtran/bloqs/arithmetic/comparison.py +++ b/qualtran/bloqs/arithmetic/comparison.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import abc from collections import defaultdict from functools import cached_property from typing import Dict, Iterable, Iterator, List, Optional, Sequence, Tuple, TYPE_CHECKING, Union @@ -51,6 +52,7 @@ from qualtran.drawing import WireSymbol from qualtran.drawing.musical_score import Circle, Text, TextBox from qualtran.resource_counting.generalizers import ignore_split_join +from qualtran.simulation.classical_sim import add_ints from qualtran.symbolics import HasLength, is_symbolic, SymbolicInt if TYPE_CHECKING: @@ -1228,3 +1230,471 @@ def _clineardepthgreaterthan_example() -> CLinearDepthGreaterThan: _CLinearDepthGreaterThan_DOC = BloqDocSpec( bloq_cls=CLinearDepthGreaterThan, examples=[_clineardepthgreaterthan_example] ) + + +@frozen +class _HalfLinearDepthGreaterThan(Bloq): + """A concrete implementation of half-circuit for greater than. + + This bloq can be returned by the _HalfComparisonBase._half_greater_than_bloq abstract property. + + Args: + dtype: dtype of the two integers a and b. + uncompute: whether this bloq uncomputes or computes the comparison. + + Registers: + a: first input register. + b: second input register. + c: ancilla register that will contain $b-a$ and will be used for uncomputation. + target: A single bit output register to store the result of a > b. + + References: + [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648). + """ + + dtype: Union[QInt, QUInt, QMontgomeryUInt] + uncompute: bool = False + + @cached_property + def signature(self) -> Signature: + side = Side.LEFT if self.uncompute else Side.RIGHT + return Signature( + [ + Register('a', self.dtype), + Register('b', self.dtype), + Register('c', QUInt(bitsize=self.dtype.bitsize + 1), side=side), + Register('target', QBit(), side=side), + ] + ) + + def adjoint(self) -> '_HalfLinearDepthGreaterThan': + return attrs.evolve(self, uncompute=self.uncompute ^ True) + + def _compute(self, bb: 'BloqBuilder', a: 'Soquet', b: 'Soquet') -> Dict[str, 'SoquetT']: + if isinstance(self.dtype, QInt): + a = bb.add(SignExtend(self.dtype, QInt(self.dtype.bitsize + 1)), x=a) + b = bb.add(SignExtend(self.dtype, QInt(self.dtype.bitsize + 1)), x=b) + else: + a = bb.join(np.concatenate([[bb.allocate(1)], bb.split(a)])) + b = bb.join(np.concatenate([[bb.allocate(1)], bb.split(b)])) + + dtype = attrs.evolve(self.dtype, bitsize=self.dtype.bitsize + 1) + b = bb.add(BitwiseNot(dtype), x=b) # b := -b-1 + a = bb.add(Cast(dtype, QUInt(dtype.bitsize)), reg=a) + b = bb.add(Cast(dtype, QUInt(dtype.bitsize)), reg=b) + a, b, c = bb.add( + OutOfPlaceAdder(self.dtype.bitsize + 1, include_most_significant_bit=False), a=a, b=b + ) # c := a - b - 1 + c = bb.add(BitwiseNot(QUInt(dtype.bitsize)), x=c) # c := b - a + + # Update `target` + c_arr = bb.split(c) + target = bb.allocate(1) + c_arr[0], target = bb.add(CNOT(), ctrl=c_arr[0], target=target) + c = bb.join(c_arr) + + a = bb.add(Cast(dtype, QUInt(dtype.bitsize)).adjoint(), reg=a) + b = bb.add(Cast(dtype, QUInt(dtype.bitsize)).adjoint(), reg=b) + b = bb.add(BitwiseNot(dtype), x=b) + + if isinstance(self.dtype, QInt): + a = bb.add(SignExtend(self.dtype, QInt(self.dtype.bitsize + 1)).adjoint(), x=a) + b = bb.add(SignExtend(self.dtype, QInt(self.dtype.bitsize + 1)).adjoint(), x=b) + else: + a_arr = bb.split(a) + a = bb.join(a_arr[1:]) + b_arr = bb.split(b) + b = bb.join(b_arr[1:]) + bb.free(a_arr[0]) + bb.free(b_arr[0]) + return {'a': a, 'b': b, 'c': c, 'target': target} + + def _uncompute( + self, bb: 'BloqBuilder', a: 'Soquet', b: 'Soquet', c: 'Soquet', target: 'Soquet' + ) -> Dict[str, 'SoquetT']: + if isinstance(self.dtype, QInt): + a = bb.add(SignExtend(self.dtype, QInt(self.dtype.bitsize + 1)), x=a) + b = bb.add(SignExtend(self.dtype, QInt(self.dtype.bitsize + 1)), x=b) + else: + a = bb.join(np.concatenate([[bb.allocate(1)], bb.split(a)])) + b = bb.join(np.concatenate([[bb.allocate(1)], bb.split(b)])) + + dtype = attrs.evolve(self.dtype, bitsize=self.dtype.bitsize + 1) + b = bb.add(BitwiseNot(dtype), x=b) # b := -b-1 + a = bb.add(Cast(dtype, QUInt(dtype.bitsize)), reg=a) + b = bb.add(Cast(dtype, QUInt(dtype.bitsize)), reg=b) + + c_arr = bb.split(c) + c_arr[0], target = bb.add(CNOT(), ctrl=c_arr[0], target=target) + c = bb.join(c_arr) + + c = bb.add(BitwiseNot(QUInt(dtype.bitsize)), x=c) + a, b = bb.add( + OutOfPlaceAdder(self.dtype.bitsize + 1, include_most_significant_bit=False).adjoint(), + a=a, + b=b, + c=c, + ) + a = bb.add(Cast(dtype, QUInt(dtype.bitsize)).adjoint(), reg=a) + b = bb.add(Cast(dtype, QUInt(dtype.bitsize)).adjoint(), reg=b) + b = bb.add(BitwiseNot(dtype), x=b) + + if isinstance(self.dtype, QInt): + a = bb.add(SignExtend(self.dtype, QInt(self.dtype.bitsize + 1)).adjoint(), x=a) + b = bb.add(SignExtend(self.dtype, QInt(self.dtype.bitsize + 1)).adjoint(), x=b) + else: + a_arr = bb.split(a) + a = bb.join(a_arr[1:]) + b_arr = bb.split(b) + b = bb.join(b_arr[1:]) + bb.free(a_arr[0]) + bb.free(b_arr[0]) + bb.free(target) + return {'a': a, 'b': b} + + def build_composite_bloq( + self, + bb: 'BloqBuilder', + a: 'Soquet', + b: 'Soquet', + c: Optional['Soquet'] = None, + target: Optional['Soquet'] = None, + ) -> Dict[str, 'SoquetT']: + if self.uncompute: + # Uncompute + assert c is not None + assert target is not None + return self._uncompute(bb, a, b, c, target) + else: + assert c is None + assert target is None + return self._compute(bb, a, b) + + def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': + dtype = attrs.evolve(self.dtype, bitsize=self.dtype.bitsize + 1) + counts: 'BloqCountDictT' + if isinstance(self.dtype, QUInt): + counts = {BitwiseNot(dtype): 3} + else: + counts = {BitwiseNot(dtype): 2, BitwiseNot(QUInt(dtype.bitsize)): 1} + + counts[CNOT()] = 1 + + adder = OutOfPlaceAdder(self.dtype.bitsize + 1, include_most_significant_bit=False) + if self.uncompute: + adder = adder.adjoint() + counts[adder] = 1 + + return counts + + +@frozen +class _HalfComparisonBase(Bloq): + """Parent class for the 4 comparison operations (>, >=, <, <=). + + The four comparison operations can be implemented by implementing only one of them + and computing the others either by reversing the input order, flipping the result or both. + + The choice made is to build the four opertions around greater than. Namely the greater than + bloq returned by `._half_greater_than_bloq`; By changing this property we can change + change the properties of the constructed circuit (e.g. complexity, depth, ..etc). + + For example _LinearDepthHalfComparisonBase sets the property to a linear depth construction, + other implementations can set the property to a log depth construction. + + Args: + dtype: dtype of the two integers a and b. + _op_symbol: The symbol of the comparison operation. + uncompute: whether this bloq uncomputes or computes the comparison. + + Registers: + a: first input register. + b: second input register. + c: ancilla register that will contain $b-a$ and will be used for uncomputation. + target: A single bit output register to store the result of a > b. + + References: + [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648). + """ + + dtype: Union[QInt, QUInt, QMontgomeryUInt] + _op_symbol: str = attrs.field( + default='>', validator=lambda _, __, s: s in ('>', '<', '>=', '<='), repr=False + ) + uncompute: bool = False + + @cached_property + def signature(self) -> Signature: + side = Side.LEFT if self.uncompute else Side.RIGHT + return Signature( + [ + Register('a', self.dtype), + Register('b', self.dtype), + Register('c', QUInt(bitsize=self.dtype.bitsize + 1), side=side), + Register('target', QBit(), side=side), + ] + ) + + def adjoint(self) -> '_HalfComparisonBase': + return attrs.evolve(self, uncompute=self.uncompute ^ True) + + @cached_property + @abc.abstractmethod + def _half_greater_than_bloq(self) -> Bloq: + raise NotImplementedError() + + def _classical_comparison( + self, a: 'ClassicalValT', b: 'ClassicalValT' + ) -> Union[bool, np.bool_, NDArray[np.bool_]]: + if self._op_symbol == '>': + return a > b + elif self._op_symbol == '<': + return a < b + elif self._op_symbol == '>=': + return a >= b + else: + return a <= b + + def on_classical_vals( + self, + a: 'ClassicalValT', + b: 'ClassicalValT', + c: Optional['ClassicalValT'] = None, + target: Optional['ClassicalValT'] = None, + ) -> Dict[str, 'ClassicalValT']: + if self.uncompute: + assert c == add_ints( + int(a), + int(b), + num_bits=int(self.dtype.bitsize), + is_signed=isinstance(self.dtype, QInt), + ) + assert target == self._classical_comparison(a, b) + return {'a': a, 'b': b} + if self._op_symbol in ('>', '<='): + c = add_ints(-int(a), int(b), num_bits=self.dtype.bitsize + 1, is_signed=False) + else: + c = add_ints(int(a), -int(b), num_bits=self.dtype.bitsize + 1, is_signed=False) + return {'a': a, 'b': b, 'c': c, 'target': int(self._classical_comparison(a, b))} + + def _compute(self, bb: 'BloqBuilder', a: 'Soquet', b: 'Soquet') -> Dict[str, 'SoquetT']: + if self._op_symbol in ('>', '<='): + a, b, c, target = bb.add_from(self._half_greater_than_bloq, a=a, b=b) # type: ignore + else: + b, a, c, target = bb.add_from(self._half_greater_than_bloq, a=b, b=a) # type: ignore + + if self._op_symbol in ('<=', '>='): + target = bb.add(XGate(), q=target) + + return {'a': a, 'b': b, 'c': c, 'target': target} + + def _uncompute( + self, bb: 'BloqBuilder', a: 'Soquet', b: 'Soquet', c: 'Soquet', target: 'Soquet' + ) -> Dict[str, 'SoquetT']: + if self._op_symbol in ('<=', '>='): + target = bb.add(XGate(), q=target) + + if self._op_symbol in ('>', '<='): + a, b = bb.add_from(self._half_greater_than_bloq.adjoint(), a=a, b=b, c=c, target=target) # type: ignore + else: + a, b = bb.add_from(self._half_greater_than_bloq.adjoint(), a=b, b=a, c=c, target=target) # type: ignore + + return {'a': a, 'b': b} + + def build_composite_bloq( + self, + bb: 'BloqBuilder', + a: 'Soquet', + b: 'Soquet', + c: Optional['Soquet'] = None, + target: Optional['Soquet'] = None, + ) -> Dict[str, 'SoquetT']: + if self.uncompute: + assert c is not None + assert target is not None + return self._uncompute(bb, a, b, c, target) + else: + assert c is None + assert target is None + return self._compute(bb, a, b) + + def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': + extra_ops = {} + if isinstance(self.dtype, QInt): + extra_ops = { + SignExtend(self.dtype, QInt(self.dtype.bitsize + 1)): 2, + SignExtend(self.dtype, QInt(self.dtype.bitsize + 1)).adjoint(): 2, + } + if self._op_symbol in ('>=', '<='): + extra_ops[XGate()] = 1 + adder = self._half_greater_than_bloq + if self.uncompute: + adder = adder.adjoint() + adder_call_graph = adder.build_call_graph(ssa) + assert isinstance(adder_call_graph, dict) + counts: defaultdict['Bloq', Union[int, sympy.Expr]] = defaultdict(lambda: 0) + counts.update(adder_call_graph) + for k, v in extra_ops.items(): + counts[k] += v + return counts + + +@frozen +class _LinearDepthHalfComparisonBase(_HalfComparisonBase): + """A wrapper around _HalfComparisonBase that sets ._half_greater_than_bloq property to a construction with linear depth.""" + + @cached_property + def _half_greater_than_bloq(self) -> Bloq: + return _HalfLinearDepthGreaterThan(self.dtype, uncompute=False) + + +@frozen +class LinearDepthHalfGreaterThan(_LinearDepthHalfComparisonBase): + r"""Compare two integers while keeping necessary ancillas for zero cost uncomputation. + + Implements $\ket{a}\ket{b}\ket{0}\ket{0} \rightarrow \ket{a}\ket{b}\ket{b-a}\ket{a>b}$ using $n$ And gates. + + This comparator relies on the fact that c = (b' + a)' = b - a. If a > b, then b - a < 0. We + implement it by flipping all the bits in b, computing the first half of the addition circuit, + copying out the carry, and keeping $c$ for the uncomputation. + + Args: + dtype: dtype of the two integers a and b. + uncompute: whether this bloq uncomputes or computes the comparison. + + Registers: + a: first input register. + b: second input register. + c: ancilla register that will contain $b-a$ and will be used for uncomputation. + target: A single bit output register to store the result of a > b. + + References: + [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648). + """ + + _op_symbol: str = attrs.field(default='>', repr=False, init=False) + + +@frozen +class LinearDepthHalfGreaterThanEqual(_LinearDepthHalfComparisonBase): + r"""Compare two integers while keeping necessary ancillas for zero cost uncomputation. + + Implements $\ket{a}\ket{b}\ket{0}\ket{0} \rightarrow \ket{a}\ket{b}\ket{a-b}\ket{a \geq b}$ using $n$ And gates. + + This comparator relies on the fact that c = (b' + a)' = b - a. If a > b, then b - a < 0. We + implement it by flipping all the bits in b, computing the first half of the addition circuit, + copying out the carry, and keeping $c$ for the uncomputation. + + Args: + dtype: dtype of the two integers a and b. + uncompute: whether this bloq uncomputes or computes the comparison. + + Registers: + a: first input register. + b: second input register. + c: ancilla register that will contain $b-a$ and will be used for uncomputation. + target: A single bit output register to store the result of a >= b. + + References: + [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648). + """ + + _op_symbol: str = attrs.field(default='>=', repr=False, init=False) + + +@frozen +class LinearDepthHalfLessThan(_LinearDepthHalfComparisonBase): + r"""Compare two integers while keeping necessary ancillas for zero cost uncomputation. + + Implements $\ket{a}\ket{b}\ket{0}\ket{0} \rightarrow \ket{a}\ket{b}\ket{a-b}\ket{a b, then b - a < 0. We + implement it by flipping all the bits in b, computing the first half of the addition circuit, + copying out the carry, and keeping $c$ for the uncomputation. + + Args: + dtype: dtype of the two integers a and b. + uncompute: whether this bloq uncomputes or computes the comparison. + + Registers: + a: first input register. + b: second input register. + c: ancilla register that will contain $b-a$ and will be used for uncomputation. + target: A single bit output register to store the result of a < b. + + References: + [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648). + """ + + _op_symbol: str = attrs.field(default='<', repr=False, init=False) + + +@frozen +class LinearDepthHalfLessThanEqual(_LinearDepthHalfComparisonBase): + r"""Compare two integers while keeping necessary ancillas for zero cost uncomputation. + + Implements $\ket{a}\ket{b}\ket{0}\ket{0} \rightarrow \ket{a}\ket{b}\ket{b-a}\ket{a \leq b}$ using $n$ And gates. + + This comparator relies on the fact that c = (b' + a)' = b - a. If a > b, then b - a < 0. We + implement it by flipping all the bits in b, computing the first half of the addition circuit, + copying out the carry, and keeping $c$ for the uncomputation. + + Args: + dtype: dtype of the two integers a and b. + uncompute: whether this bloq uncomputes or computes the comparison. + + Registers: + a: first input register. + b: second input register. + c: ancilla register that will contain $b-a$ and will be used for uncomputation. + target: A single bit output register to store the result of a <= b. + + References: + [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648). + """ + + _op_symbol: str = attrs.field(default='<=', repr=False, init=False) + + +@bloq_example +def _lineardepthhalfgreaterthan_small() -> LinearDepthHalfGreaterThan: + lineardepthhalfgreaterthan_small = LinearDepthHalfGreaterThan(QUInt(3)) + return lineardepthhalfgreaterthan_small + + +@bloq_example +def _lineardepthhalflessthan_small() -> LinearDepthHalfLessThan: + lineardepthhalflessthan_small = LinearDepthHalfLessThan(QUInt(3)) + return lineardepthhalflessthan_small + + +@bloq_example +def _lineardepthhalfgreaterthanequal_small() -> LinearDepthHalfGreaterThanEqual: + lineardepthhalfgreaterthanequal_small = LinearDepthHalfGreaterThanEqual(QUInt(3)) + return lineardepthhalfgreaterthanequal_small + + +@bloq_example +def _lineardepthhalflessthanequal_small() -> LinearDepthHalfLessThanEqual: + lineardepthhalflessthanequal_small = LinearDepthHalfLessThanEqual(QUInt(3)) + return lineardepthhalflessthanequal_small + + +_LINEAR_DEPTH_HALF_GREATERTHAN_DOC = BloqDocSpec( + bloq_cls=LinearDepthHalfGreaterThan, examples=[_lineardepthhalfgreaterthan_small] +) + + +_LINEAR_DEPTH_HALF_LESSTHAN_DOC = BloqDocSpec( + bloq_cls=LinearDepthHalfLessThan, examples=[_lineardepthhalflessthan_small] +) + + +_LINEAR_DEPTH_HALF_GREATERTHANEQUAL_DOC = BloqDocSpec( + bloq_cls=LinearDepthHalfGreaterThanEqual, examples=[_lineardepthhalfgreaterthanequal_small] +) + + +_LINEAR_DEPTH_HALF_LESSTHANEQUAL_DOC = BloqDocSpec( + bloq_cls=LinearDepthHalfLessThanEqual, examples=[_lineardepthhalflessthanequal_small] +) diff --git a/qualtran/bloqs/arithmetic/comparison_test.py b/qualtran/bloqs/arithmetic/comparison_test.py index f755d1eb7..9876537e8 100644 --- a/qualtran/bloqs/arithmetic/comparison_test.py +++ b/qualtran/bloqs/arithmetic/comparison_test.py @@ -28,6 +28,10 @@ _greater_than, _gt_k, _leq_symb, + _lineardepthhalfgreaterthan_small, + _lineardepthhalfgreaterthanequal_small, + _lineardepthhalflessthan_small, + _lineardepthhalflessthanequal_small, _lt_k_symb, BiQubitsMixer, CLinearDepthGreaterThan, @@ -38,10 +42,15 @@ LessThanConstant, LessThanEqual, LinearDepthGreaterThan, + LinearDepthHalfGreaterThan, + LinearDepthHalfGreaterThanEqual, + LinearDepthHalfLessThan, + LinearDepthHalfLessThanEqual, SingleQubitCompare, ) from qualtran.cirq_interop.t_complexity_protocol import t_complexity, TComplexity from qualtran.cirq_interop.testing import assert_circuit_inp_out_cirqsim +from qualtran.resource_counting import get_cost_value, QECGatesCost from qualtran.resource_counting.generalizers import ignore_alloc_free, ignore_split_join @@ -398,3 +407,97 @@ def test_clineardepthgreaterthan_tcomplexity(ctrl, dtype): c = CLinearDepthGreaterThan(dtype(n), ctrl).t_complexity() assert c.t == 4 * (n + 2) assert c.rotations == 0 + + +@pytest.mark.parametrize( + 'comp_cls', + [ + LinearDepthHalfGreaterThan, + LinearDepthHalfGreaterThanEqual, + LinearDepthHalfLessThan, + LinearDepthHalfLessThanEqual, + ], +) +@pytest.mark.parametrize('dtype', [QInt, QUInt, QMontgomeryUInt]) +@pytest.mark.parametrize('bitsize', range(2, 5)) +@pytest.mark.parametrize('uncompute', [True, False]) +def test_linear_half_comparison_decomposition(comp_cls, dtype, bitsize, uncompute): + b = comp_cls(dtype(bitsize), uncompute) + qlt_testing.assert_valid_bloq_decomposition(b) + + +@pytest.mark.parametrize( + 'comp_cls', + [ + LinearDepthHalfGreaterThan, + LinearDepthHalfGreaterThanEqual, + LinearDepthHalfLessThan, + LinearDepthHalfLessThanEqual, + ], +) +@pytest.mark.parametrize('dtype', [QInt, QUInt, QMontgomeryUInt]) +@pytest.mark.parametrize('bitsize', range(2, 5)) +@pytest.mark.parametrize('uncompute', [True, False]) +def test_linear_half_comparison_bloq_counts(comp_cls, dtype, bitsize, uncompute): + b = comp_cls(dtype(bitsize), uncompute) + qlt_testing.assert_equivalent_bloq_counts(b, [ignore_alloc_free, ignore_split_join]) + + +@pytest.mark.parametrize( + 'comp_cls', + [ + LinearDepthHalfGreaterThan, + LinearDepthHalfGreaterThanEqual, + LinearDepthHalfLessThan, + LinearDepthHalfLessThanEqual, + ], +) +@pytest.mark.parametrize('dtype', [QInt, QUInt, QMontgomeryUInt]) +@pytest.mark.parametrize('bitsize', range(2, 5)) +def test_linear_half_comparison_classical_action(comp_cls, dtype, bitsize): + b = comp_cls(dtype(bitsize)) + qlt_testing.assert_consistent_classical_action( + b, a=dtype(bitsize).get_classical_domain(), b=dtype(bitsize).get_classical_domain() + ) + + +@pytest.mark.parametrize( + 'comp_cls', + [ + LinearDepthHalfGreaterThan, + LinearDepthHalfGreaterThanEqual, + LinearDepthHalfLessThan, + LinearDepthHalfLessThanEqual, + ], +) +@pytest.mark.parametrize('dtype', [QInt, QUInt, QMontgomeryUInt]) +def test_linear_half_comparison_symbolic_complexity(comp_cls, dtype): + n = sympy.Symbol('n') + b = comp_cls(dtype(n)) + + cost = get_cost_value(b, QECGatesCost()).total_t_and_ccz_count() + + assert cost['n_t'] == 0 + assert cost['n_ccz'] == n + + # uncomputation has zero cost. + cost = get_cost_value(b.adjoint(), QECGatesCost()).total_t_and_ccz_count() + + assert cost['n_t'] == 0 + assert cost['n_ccz'] == 0 + + +def test_lineardepthhalfgreaterthan_small(bloq_autotester): + bloq_autotester(_lineardepthhalfgreaterthan_small) + + +def test_lineardepthhalflessthan_small(bloq_autotester): + bloq_autotester(_lineardepthhalflessthan_small) + + +def test_lineardepthhalfgreaterthanequal_small(bloq_autotester): + bloq_autotester(_lineardepthhalfgreaterthanequal_small) + + +def test_lineardepthhalflessthanequal_small(bloq_autotester): + bloq_autotester(_lineardepthhalflessthanequal_small) diff --git a/qualtran/serialization/resolver_dict.py b/qualtran/serialization/resolver_dict.py index 4aa73158d..bc3be1a65 100644 --- a/qualtran/serialization/resolver_dict.py +++ b/qualtran/serialization/resolver_dict.py @@ -177,6 +177,10 @@ "qualtran.bloqs.arithmetic.comparison.LinearDepthGreaterThan": qualtran.bloqs.arithmetic.comparison.LinearDepthGreaterThan, "qualtran.bloqs.arithmetic.comparison.SingleQubitCompare": qualtran.bloqs.arithmetic.comparison.SingleQubitCompare, "qualtran.bloqs.arithmetic.comparison.CLinearDepthGreaterThan": qualtran.bloqs.arithmetic.comparison.CLinearDepthGreaterThan, + "qualtran.bloqs.arithmetic.comparison.LinearDepthHalfGreaterThan": qualtran.bloqs.arithmetic.comparison.LinearDepthHalfGreaterThan, + "qualtran.bloqs.arithmetic.comparison.LinearDepthHalfLessThan": qualtran.bloqs.arithmetic.comparison.LinearDepthHalfLessThan, + "qualtran.bloqs.arithmetic.comparison.LinearDepthHalfGreaterThanEqual": qualtran.bloqs.arithmetic.comparison.LinearDepthHalfGreaterThanEqual, + "qualtran.bloqs.arithmetic.comparison.LinearDepthHalfLessThanEqual": qualtran.bloqs.arithmetic.comparison.LinearDepthHalfLessThanEqual, "qualtran.bloqs.arithmetic.controlled_add_or_subtract.ControlledAddOrSubtract": qualtran.bloqs.arithmetic.controlled_add_or_subtract.ControlledAddOrSubtract, "qualtran.bloqs.arithmetic.conversions.contiguous_index.ToContiguousIndex": qualtran.bloqs.arithmetic.conversions.contiguous_index.ToContiguousIndex, "qualtran.bloqs.arithmetic.conversions.ones_complement_to_twos_complement.SignedIntegerToTwosComplement": qualtran.bloqs.arithmetic.conversions.ones_complement_to_twos_complement.SignedIntegerToTwosComplement,