diff --git a/qualtran/bloqs/arithmetic/addition.ipynb b/qualtran/bloqs/arithmetic/addition.ipynb index 4cd90a386..d379603de 100644 --- a/qualtran/bloqs/arithmetic/addition.ipynb +++ b/qualtran/bloqs/arithmetic/addition.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "c231998b", + "id": "c9483021", "metadata": { "cq.autogen": "title_cell" }, @@ -13,7 +13,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b572d769", + "id": "2dd760e6", "metadata": { "cq.autogen": "top_imports" }, @@ -30,7 +30,7 @@ }, { "cell_type": "markdown", - "id": "d3f4ce95", + "id": "95d8998c", "metadata": { "cq.autogen": "Add.bloq_doc.md" }, @@ -55,7 +55,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6426bb53", + "id": "4cb5b704", "metadata": { "cq.autogen": "Add.bloq_doc.py" }, @@ -66,7 +66,7 @@ }, { "cell_type": "markdown", - "id": "3c8caad1", + "id": "69f415a2", "metadata": { "cq.autogen": "Add.example_instances.md" }, @@ -77,46 +77,46 @@ { "cell_type": "code", "execution_count": null, - "id": "73929b65", + "id": "b0c9815d", "metadata": { - "cq.autogen": "Add.add_small" + "cq.autogen": "Add.add_symb" }, "outputs": [], "source": [ - "add_small = Add(QUInt(bitsize=4))" + "n = sympy.Symbol('n')\n", + "add_symb = Add(QInt(bitsize=n))" ] }, { "cell_type": "code", "execution_count": null, - "id": "fbd4dbe9", + "id": "b481bd7d", "metadata": { - "cq.autogen": "Add.add_large" + "cq.autogen": "Add.add_small" }, "outputs": [], "source": [ - "add_large = Add(QUInt(bitsize=64))" + "add_small = Add(QUInt(bitsize=4))" ] }, { "cell_type": "code", "execution_count": null, - "id": "e749abdb", + "id": "90bc9e29", "metadata": { - "cq.autogen": "Add.add_symb" + "cq.autogen": "Add.add_large" }, "outputs": [], "source": [ - "n = sympy.Symbol('n')\n", - "add_symb = Add(QInt(bitsize=n))" + "add_large = Add(QUInt(bitsize=64))" ] }, { "cell_type": "code", "execution_count": null, - "id": "e5b746c1", + "id": "2fbbb318", "metadata": { - "collapsed": false + "cq.autogen": "Add.add_diff_size_regs" }, "outputs": [], "source": [ @@ -125,7 +125,7 @@ }, { "cell_type": "markdown", - "id": "c3c1a7e4", + "id": "253212ec", "metadata": { "cq.autogen": "Add.graphical_signature.md" }, @@ -136,7 +136,7 @@ { "cell_type": "code", "execution_count": null, - "id": "0f92be09", + "id": "ed24479f", "metadata": { "cq.autogen": "Add.graphical_signature.py" }, @@ -149,7 +149,7 @@ }, { "cell_type": "markdown", - "id": "350cb374", + "id": "1c55412d", "metadata": { "cq.autogen": "Add.call_graph.md" }, @@ -160,7 +160,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c36a3fc4", + "id": "90569a0c", "metadata": { "cq.autogen": "Add.call_graph.py" }, @@ -174,7 +174,7 @@ }, { "cell_type": "markdown", - "id": "ffc76cc5", + "id": "8ae20541", "metadata": { "cq.autogen": "OutOfPlaceAdder.bloq_doc.md" }, @@ -202,7 +202,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c2450d62", + "id": "7c51ccda", "metadata": { "cq.autogen": "OutOfPlaceAdder.bloq_doc.py" }, @@ -213,7 +213,7 @@ }, { "cell_type": "markdown", - "id": "b5c79e25", + "id": "6e0af527", "metadata": { "cq.autogen": "OutOfPlaceAdder.example_instances.md" }, @@ -224,7 +224,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e31acd2b", + "id": "f062b497", "metadata": { "cq.autogen": "OutOfPlaceAdder.add_oop_symb" }, @@ -237,7 +237,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ef8871a6", + "id": "0bde421f", "metadata": { "cq.autogen": "OutOfPlaceAdder.add_oop_small" }, @@ -249,7 +249,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e57b8c8d", + "id": "ce284cd8", "metadata": { "cq.autogen": "OutOfPlaceAdder.add_oop_large" }, @@ -260,7 +260,7 @@ }, { "cell_type": "markdown", - "id": "01915f46", + "id": "052cf86a", "metadata": { "cq.autogen": "OutOfPlaceAdder.graphical_signature.md" }, @@ -271,7 +271,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d117e345", + "id": "672073d0", "metadata": { "cq.autogen": "OutOfPlaceAdder.graphical_signature.py" }, @@ -284,7 +284,7 @@ }, { "cell_type": "markdown", - "id": "3b6469e0", + "id": "ab488ede", "metadata": { "cq.autogen": "OutOfPlaceAdder.call_graph.md" }, @@ -295,7 +295,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ff84301f", + "id": "311c752e", "metadata": { "cq.autogen": "OutOfPlaceAdder.call_graph.py" }, @@ -309,7 +309,7 @@ }, { "cell_type": "markdown", - "id": "2813f173", + "id": "62b014b7", "metadata": { "cq.autogen": "AddK.bloq_doc.md" }, @@ -324,13 +324,12 @@ "only clifford operations.\n", "\n", "#### Parameters\n", - " - `bitsize`: Number of bits used to represent each integer.\n", + " - `dtype`: data type of the input register `x`\n", " - `k`: The classical integer value to be added to x.\n", - " - `cvs`: A tuple of control values. Each entry specifies whether that control line is a \"positive\" control (`cv[i]=1`) or a \"negative\" control (`cv[i]=0`).\n", - " - `signed`: A boolean condition which controls whether the x register holds a value represented in 2's Complement or Unsigned. This affects the ability to add a negative constant. \n", + " - `is_controlled`: if True, construct a singly-controlled bloq. \n", "\n", "#### Registers\n", - " - `x`: A bitsize-sized input register (register x above). \n", + " - `x`: register of type `self.dtype` \n", "\n", "#### References\n", " - [Improved quantum circuits for elliptic curve discrete logarithms](https://arxiv.org/abs/2001.09580). Haner et al. 2020. Section 3: Components. \"Integer addition\" and Fig 2a.\n" @@ -339,7 +338,7 @@ { "cell_type": "code", "execution_count": null, - "id": "cd255bf9", + "id": "3155864a", "metadata": { "cq.autogen": "AddK.bloq_doc.py" }, @@ -350,7 +349,7 @@ }, { "cell_type": "markdown", - "id": "7538f9a5", + "id": "9009b701", "metadata": { "cq.autogen": "AddK.example_instances.md" }, @@ -361,43 +360,43 @@ { "cell_type": "code", "execution_count": null, - "id": "4305289f", + "id": "7e2a9fdf", "metadata": { "cq.autogen": "AddK.add_k" }, "outputs": [], "source": [ "n, k = sympy.symbols('n k')\n", - "add_k = AddK(bitsize=n, k=k)" + "add_k = AddK(QUInt(n), k=k)" ] }, { "cell_type": "code", "execution_count": null, - "id": "f6048819", + "id": "f4e218da", "metadata": { "cq.autogen": "AddK.add_k_small" }, "outputs": [], "source": [ - "add_k_small = AddK(bitsize=4, k=2, signed=False)" + "add_k_small = AddK(QUInt(4), k=2)" ] }, { "cell_type": "code", "execution_count": null, - "id": "b67fd469", + "id": "97073a76", "metadata": { "cq.autogen": "AddK.add_k_large" }, "outputs": [], "source": [ - "add_k_large = AddK(bitsize=64, k=-23, signed=True)" + "add_k_large = AddK(QInt(64), k=-23)" ] }, { "cell_type": "markdown", - "id": "b8b04228", + "id": "57f1032d", "metadata": { "cq.autogen": "AddK.graphical_signature.md" }, @@ -408,7 +407,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e93e7f2e", + "id": "d5d7dad4", "metadata": { "cq.autogen": "AddK.graphical_signature.py" }, @@ -421,7 +420,7 @@ }, { "cell_type": "markdown", - "id": "13552795", + "id": "f1a51ffb", "metadata": { "cq.autogen": "AddK.call_graph.md" }, @@ -432,7 +431,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d8d6584e", + "id": "cbfc2d39", "metadata": { "cq.autogen": "AddK.call_graph.py" }, @@ -443,37 +442,16 @@ "show_call_graph(add_k_g)\n", "show_counts_sigma(add_k_sigma)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8853ae5d", - "metadata": { - "cq.autogen": "Add.add_diff_size_regs" - }, - "outputs": [], - "source": [ - "add_diff_size_regs = Add(QUInt(bitsize=4), QUInt(bitsize=16))" - ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "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.11.7" + "name": "python" } }, "nbformat": 4, diff --git a/qualtran/bloqs/arithmetic/addition.py b/qualtran/bloqs/arithmetic/addition.py index 7b208bd36..fa50046d8 100644 --- a/qualtran/bloqs/arithmetic/addition.py +++ b/qualtran/bloqs/arithmetic/addition.py @@ -11,19 +11,9 @@ # 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 collections import Counter from functools import cached_property -from typing import ( - Dict, - Iterable, - Iterator, - List, - Optional, - Sequence, - Set, - Tuple, - TYPE_CHECKING, - Union, -) +from typing import Dict, Iterator, List, Optional, Sequence, Tuple, TYPE_CHECKING, Union import attrs import cirq @@ -42,7 +32,7 @@ CtrlSpec, DecomposeTypeError, GateWithRegisters, - QBit, + QAny, QInt, QMontgomeryUInt, QUInt, @@ -52,24 +42,18 @@ Soquet, SoquetT, ) -from qualtran.bloqs.basic_gates import CNOT, XGate -from qualtran.bloqs.mcmt import MultiControlX +from qualtran.bloqs.basic_gates import CNOT from qualtran.bloqs.mcmt.and_bloq import And from qualtran.cirq_interop import decompose_from_cirq_style_method from qualtran.drawing import directional_text_box, Text from qualtran.resource_counting.generalizers import ignore_split_join from qualtran.simulation.classical_sim import add_ints +from qualtran.symbolics import is_symbolic, SymbolicInt if TYPE_CHECKING: from qualtran.drawing import WireSymbol - from qualtran.resource_counting import ( - BloqCountDictT, - BloqCountT, - MutableBloqCountDictT, - SympySymbolAllocator, - ) + from qualtran.resource_counting import BloqCountDictT, SympySymbolAllocator from qualtran.simulation.classical_sim import ClassicalValT - from qualtran.symbolics import SymbolicInt @frozen @@ -405,180 +389,118 @@ class AddK(Bloq): only clifford operations. Args: - bitsize: Number of bits used to represent each integer. + dtype: data type of the input register `x` k: The classical integer value to be added to x. - cvs: A tuple of control values. Each entry specifies whether that control line is a - "positive" control (`cv[i]=1`) or a "negative" control (`cv[i]=0`). - signed: A boolean condition which controls whether the x register holds a value represented - in 2's Complement or Unsigned. This affects the ability to add a negative constant. + is_controlled: if True, construct a singly-controlled bloq. Registers: - x: A bitsize-sized input register (register x above). + x: register of type `self.dtype` References: [Improved quantum circuits for elliptic curve discrete logarithms](https://arxiv.org/abs/2001.09580). Haner et al. 2020. Section 3: Components. "Integer addition" and Fig 2a. """ - bitsize: 'SymbolicInt' + dtype: Union[QInt, QUInt, QMontgomeryUInt] k: 'SymbolicInt' - cvs: Tuple[int, ...] = field(converter=_cvs_converter, default=()) - signed: bool = False + is_controlled: bool = False + + def __attrs_post_init__(self): + if not isinstance(self.dtype, (QInt, QUInt, QMontgomeryUInt)): + raise NotImplementedError( + "Only QInt, QUInt and QMontgomeryUInt types are supported for composite addition." + ) @cached_property def signature(self) -> 'Signature': - if len(self.cvs) > 0: - return Signature( - [ - Register('ctrls', QBit(), shape=(len(self.cvs),)), - Register('x', QInt(self.bitsize) if self.signed else QUInt(self.bitsize)), - ] - ) - else: - return Signature( - [Register('x', QInt(bitsize=self.bitsize) if self.signed else QUInt(self.bitsize))] - ) + return Signature.build_from_dtypes(ctrl=QAny(1 if self.is_controlled else 0), x=self.dtype) def on_classical_vals( self, x: 'ClassicalValT', **vals: 'ClassicalValT' ) -> Dict[str, 'ClassicalValT']: - if isinstance(self.k, sympy.Expr) or isinstance(self.bitsize, sympy.Expr): + if is_symbolic(self.k) or is_symbolic(self.dtype): raise ValueError(f"Classical simulation isn't supported for symbolic block {self}") - N = 2**self.bitsize - if len(self.cvs) > 0: - ctrls = vals['ctrls'] - else: - return { - 'x': add_ints(int(x), int(self.k), num_bits=self.bitsize, is_signed=self.signed) - } - if np.all(self.cvs == ctrls): - x = add_ints(int(x), int(self.k), num_bits=self.bitsize, is_signed=self.signed) + if not self.is_controlled or vals['ctrl']: + is_signed = isinstance(self.dtype, QInt) + x = add_ints(int(x), int(self.k), num_bits=self.dtype.num_qubits, is_signed=is_signed) + + return vals | {'x': x} - return {'ctrls': ctrls, 'x': x} + @cached_property + def _load_k_bloq(self) -> Bloq: + from qualtran.bloqs.arithmetic.bitwise import XorK + + xork = XorK(self.dtype, self.k) + return xork.controlled() if self.is_controlled else xork def build_composite_bloq( self, bb: 'BloqBuilder', x: Soquet, **regs: SoquetT ) -> Dict[str, 'SoquetT']: - if isinstance(self.k, sympy.Expr) or isinstance(self.bitsize, sympy.Expr): + if is_symbolic(self.k) or is_symbolic(self.dtype): raise DecomposeTypeError(f"Cannot decompose symbolic {self}.") - # Assign registers to variables and allocate ancilla bits for classical integer k. - if len(self.cvs) > 0: - ctrls = regs['ctrls'] - else: - ctrls = None - k = bb.allocate(dtype=x.reg.dtype) + ctrl = regs.pop('ctrl', None) - # Get binary representation of k and split k into separate wires. - k_split = bb.split(k) - if self.signed: - binary_rep = QInt(self.bitsize).to_bits(self.k) + # load `k` (conditional on ctrl if present) + k = bb.allocate(dtype=self.dtype) + if self.is_controlled: + ctrl, k = bb.add(self._load_k_bloq, ctrl=ctrl, x=k) else: - val = self.k - if val < 0: - # Since this is unsigned addition adding -v is equivalent to - # adding 2^bitsize - v - val %= 2**self.bitsize - binary_rep = QUInt(self.bitsize).to_bits(val) - - # Apply XGates to qubits in k where the bitstring has value 1. Apply CNOTs when the gate is - # controlled. - for i in range(self.bitsize): - if binary_rep[i] == 1: - if len(self.cvs) > 0 and ctrls is not None: - ctrls, k_split[i] = bb.add( - MultiControlX(cvs=self.cvs), controls=ctrls, target=k_split[i] - ) - else: - k_split[i] = bb.add(XGate(), q=k_split[i]) - - # Rejoin the qubits representing k for in-place addition. - k = bb.join(k_split, dtype=x.reg.dtype) - if not isinstance(x.reg.dtype, (QInt, QUInt, QMontgomeryUInt)): - raise ValueError( - "Only QInt, QUInt and QMontgomerUInt types are supported for composite addition." - ) - k, x = bb.add(Add(x.reg.dtype, x.reg.dtype), a=k, b=x) - - # Resplit the k qubits in order to undo the original bit flips to go from the binary - # representation back to the zero state. - k_split = bb.split(k) - for i in range(self.bitsize): - if binary_rep[i] == 1: - if len(self.cvs) > 0 and ctrls is not None: - ctrls, k_split[i] = bb.add( - MultiControlX(cvs=self.cvs), controls=ctrls, target=k_split[i] - ) - else: - k_split[i] = bb.add(XGate(), q=k_split[i]) - - # Free the ancilla qubits. - k = bb.join(k_split, dtype=x.reg.dtype) - bb.free(k) + k = bb.add(self._load_k_bloq, x=k) - # Return the output registers. - if len(self.cvs) > 0 and ctrls is not None: - return {'ctrls': ctrls, 'x': x} - else: - return {'x': x} - - def build_call_graph( - self, ssa: 'SympySymbolAllocator' - ) -> Union['BloqCountDictT', Set['BloqCountT']]: - loading_cost: MutableBloqCountDictT - if len(self.cvs) == 0: - loading_cost = {XGate(): self.bitsize} # upper bound; depends on the data. - elif len(self.cvs) == 1: - loading_cost = {CNOT(): self.bitsize} # upper bound; depends on the data. - else: - # Otherwise, use the decomposition - return super().build_call_graph(ssa=ssa) - loading_cost[Add(QUInt(self.bitsize))] = 1 - return loading_cost + # quantum-quantum addition + k, x = bb.add(Add(self.dtype, self.dtype), a=k, b=x) - def get_ctrl_system(self, ctrl_spec: 'CtrlSpec') -> Tuple['Bloq', 'AddControlledT']: - if self.cvs: - # We're already controlled, use default fallback - return super().get_ctrl_system(ctrl_spec) + # unload `k` + if self.is_controlled: + ctrl, k = bb.add(self._load_k_bloq.adjoint(), ctrl=ctrl, x=k) + else: + k = bb.add(self._load_k_bloq.adjoint(), x=k) + bb.free(k) - if ctrl_spec.num_ctrl_reg != 1: - # Multiple control registers, use default fallback - return super().get_ctrl_system(ctrl_spec) + soqs = {'x': x} + if ctrl: + soqs |= {'ctrl': ctrl} + return soqs - ((qdtype, cv_shape),) = ctrl_spec.activation_function_dtypes() - if qdtype != QBit(): - # Control values aren't bits, use default fallback - return super().get_ctrl_system(ctrl_spec) + def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': + counts = Counter[Bloq]() - # Supported via this class's custom `cvs` attribute. - bloq = attrs.evolve(self, cvs=ctrl_spec.cvs) + counts[self._load_k_bloq] += 1 + counts[Add(self.dtype, self.dtype)] += 1 + counts[self._load_k_bloq.adjoint()] += 1 - def _add_ctrled( - bb: 'BloqBuilder', ctrl_soqs: Sequence['SoquetT'], in_soqs: Dict[str, 'SoquetT'] - ) -> Tuple[Iterable['SoquetT'], Iterable['SoquetT']]: - ctrls, x = bb.add_t(bloq, ctrls=np.asarray(ctrl_soqs), **in_soqs) - return np.asarray(ctrls).tolist(), (x,) + return counts - return bloq, _add_ctrled + def get_ctrl_system(self, ctrl_spec: 'CtrlSpec') -> Tuple['Bloq', 'AddControlledT']: + from qualtran.bloqs.mcmt.specialized_ctrl import get_ctrl_system_1bit_cv_from_bloqs + + return get_ctrl_system_1bit_cv_from_bloqs( + self, + ctrl_spec, + current_ctrl_bit=1 if self.is_controlled else None, + bloq_with_ctrl=attrs.evolve(self, is_controlled=True), + ctrl_reg_name='ctrl', + ) @bloq_example(generalizer=ignore_split_join) def _add_k() -> AddK: n, k = sympy.symbols('n k') - add_k = AddK(bitsize=n, k=k) + add_k = AddK(QUInt(n), k=k) return add_k @bloq_example(generalizer=ignore_split_join) def _add_k_small() -> AddK: - add_k_small = AddK(bitsize=4, k=2, signed=False) + add_k_small = AddK(QUInt(4), k=2) return add_k_small @bloq_example(generalizer=ignore_split_join) def _add_k_large() -> AddK: - add_k_large = AddK(bitsize=64, k=-23, signed=True) + add_k_large = AddK(QInt(64), k=-23) return add_k_large diff --git a/qualtran/bloqs/arithmetic/addition_test.py b/qualtran/bloqs/arithmetic/addition_test.py index 7f3576862..60c64e6d0 100644 --- a/qualtran/bloqs/arithmetic/addition_test.py +++ b/qualtran/bloqs/arithmetic/addition_test.py @@ -36,6 +36,7 @@ AddK, OutOfPlaceAdder, ) +from qualtran.bloqs.basic_gates import XGate from qualtran.bloqs.mcmt.and_bloq import And from qualtran.cirq_interop.t_complexity_protocol import TComplexity from qualtran.cirq_interop.testing import assert_circuit_inp_out_cirqsim, GateHelper @@ -315,16 +316,19 @@ def test_out_of_place_adder(): def test_controlled_add_k(): n, k = sympy.symbols('n k') - addk = AddK(n, k) - assert addk.controlled() == AddK(n, k, cvs=(1,)) - assert addk.controlled(CtrlSpec(cvs=0)) == AddK(n, k, cvs=(0,)) + addk = AddK(QUInt(n), k) + assert addk.controlled() == AddK(QUInt(n), k, is_controlled=True) + _, sigma = addk.controlled(CtrlSpec(cvs=0)).call_graph(max_depth=1) + assert sigma == {addk.controlled(): 1, XGate(): 2} @pytest.mark.parametrize('bitsize', [5]) @pytest.mark.parametrize('k', [5, 8]) @pytest.mark.parametrize('cvs', [[], [0, 1], [1, 0], [1, 1]]) def test_add_k_decomp_unsigned(bitsize, k, cvs): - bloq = AddK(bitsize=bitsize, k=k, cvs=cvs, signed=False) + bloq = AddK(QUInt(bitsize), k=k) + if cvs: + bloq = bloq.controlled(CtrlSpec(cvs=cvs)) qlt_testing.assert_valid_bloq_decomposition(bloq) @@ -332,7 +336,9 @@ def test_add_k_decomp_unsigned(bitsize, k, cvs): @pytest.mark.parametrize('k', [-5, 8]) @pytest.mark.parametrize('cvs', [[], [0, 1], [1, 0], [1, 1]]) def test_add_k_decomp_signed(bitsize, k, cvs): - bloq = AddK(bitsize=bitsize, k=k, cvs=cvs, signed=True) + bloq = AddK(QInt(bitsize), k=k) + if cvs: + bloq = bloq.controlled(CtrlSpec(cvs=cvs)) qlt_testing.assert_valid_bloq_decomposition(bloq) @@ -340,16 +346,18 @@ def test_add_k_decomp_signed(bitsize, k, cvs): 'bitsize,k,x,cvs,ctrls,result', [ (5, 1, 2, (), (), 3), - (5, 3, 2, (1,), (1,), 5), + (5, 3, 2, (1,), 1, 5), (5, 2, 0, (1, 0), (1, 0), 2), (5, 1, 2, (1, 0, 1), (0, 0, 0), 2), ], ) def test_classical_add_k_unsigned(bitsize, k, x, cvs, ctrls, result): - bloq = AddK(bitsize=bitsize, k=k, cvs=cvs, signed=False) + bloq = AddK(QUInt(bitsize), k=k) + if cvs: + bloq = bloq.controlled(CtrlSpec(cvs=cvs)) cbloq = bloq.decompose_bloq() - bloq_classical = bloq.call_classically(ctrls=ctrls, x=x) - cbloq_classical = cbloq.call_classically(ctrls=ctrls, x=x) + bloq_classical = bloq.call_classically(ctrl=ctrls, x=x) + cbloq_classical = cbloq.call_classically(ctrl=ctrls, x=x) assert len(bloq_classical) == len(cbloq_classical) for i in range(len(bloq_classical)): @@ -369,10 +377,12 @@ def test_classical_add_signed_overflow(bitsize): 'bitsize,k,x,cvs,ctrls,result', [(5, 2, 0, (1, 0), (1, 0), 2), (6, -3, 2, (), (), -1)] ) def test_classical_add_k_signed(bitsize, k, x, cvs, ctrls, result): - bloq = AddK(bitsize=bitsize, k=k, cvs=cvs, signed=True) + bloq = AddK(QInt(bitsize), k=k) + if cvs: + bloq = bloq.controlled(CtrlSpec(cvs=cvs)) cbloq = bloq.decompose_bloq() - bloq_classical = bloq.call_classically(ctrls=ctrls, x=x) - cbloq_classical = cbloq.call_classically(ctrls=ctrls, x=x) + bloq_classical = bloq.call_classically(ctrl=ctrls, x=x) + cbloq_classical = cbloq.call_classically(ctrl=ctrls, x=x) assert len(bloq_classical) == len(cbloq_classical) for i in range(len(bloq_classical)): diff --git a/qualtran/bloqs/arithmetic/negate.py b/qualtran/bloqs/arithmetic/negate.py index c3910fc1e..0690363df 100644 --- a/qualtran/bloqs/arithmetic/negate.py +++ b/qualtran/bloqs/arithmetic/negate.py @@ -12,11 +12,20 @@ # See the License for the specific language governing permissions and # limitations under the License. from functools import cached_property -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Union from attrs import frozen -from qualtran import Bloq, bloq_example, BloqDocSpec, QDType, QInt, Signature +from qualtran import ( + Bloq, + bloq_example, + BloqDocSpec, + QDType, + QInt, + Signature, + QMontgomeryUInt, + QUInt, +) from qualtran.bloqs.arithmetic import AddK from qualtran.bloqs.arithmetic.bitwise import BitwiseNot @@ -53,7 +62,7 @@ class Negate(Bloq): Operator "Unary Minus". Last accessed 17 July 2024. """ - dtype: QDType + dtype: Union[QUInt, QInt, QMontgomeryUInt] @cached_property def signature(self) -> 'Signature': @@ -61,7 +70,7 @@ def signature(self) -> 'Signature': def build_composite_bloq(self, bb: 'BloqBuilder', x: 'SoquetT') -> dict[str, 'SoquetT']: x = bb.add(BitwiseNot(self.dtype), x=x) # ~x - x = bb.add(AddK(self.dtype.num_qubits, k=1, signed=isinstance(self.dtype, QInt)), x=x) # -x + x = bb.add(AddK(self.dtype, k=1), x=x) # -x return {'x': x} diff --git a/qualtran/bloqs/block_encoding/sparse_matrix.py b/qualtran/bloqs/block_encoding/sparse_matrix.py index 60f8a39cf..910ba60d9 100644 --- a/qualtran/bloqs/block_encoding/sparse_matrix.py +++ b/qualtran/bloqs/block_encoding/sparse_matrix.py @@ -35,6 +35,7 @@ Signature, Soquet, SoquetT, + QInt, ) from qualtran.bloqs.arithmetic import Add, AddK from qualtran.bloqs.basic_gates import Ry, Swap @@ -334,7 +335,7 @@ def call_classically(self, l: ClassicalValT, i: ClassicalValT) -> Tuple[Classica def build_call_graph(self, ssa: 'SympySymbolAllocator') -> BloqCountDictT: return { Add(QUInt(self.system_bitsize), QUInt(self.system_bitsize)): 1, - AddK(self.system_bitsize, -self.bandsize, signed=True): 1, + AddK(QInt(self.system_bitsize), -self.bandsize): 1, } def build_composite_bloq(self, bb: BloqBuilder, l: SoquetT, i: SoquetT) -> Dict[str, SoquetT]: @@ -342,7 +343,7 @@ def build_composite_bloq(self, bb: BloqBuilder, l: SoquetT, i: SoquetT) -> Dict[ raise DecomposeTypeError(f"Cannot decompose symbolic {self=}") i, l = bb.add(Add(QUInt(self.system_bitsize), QUInt(self.system_bitsize)), a=i, b=l) - l = bb.add(AddK(self.system_bitsize, -self.bandsize, signed=True), x=l) + l = bb.add(AddK(QInt(self.system_bitsize), -self.bandsize), x=l) return {"l": l, "i": i} diff --git a/qualtran/bloqs/mod_arithmetic/mod_addition.py b/qualtran/bloqs/mod_arithmetic/mod_addition.py index a8186cdfe..6796631f8 100644 --- a/qualtran/bloqs/mod_arithmetic/mod_addition.py +++ b/qualtran/bloqs/mod_arithmetic/mod_addition.py @@ -117,7 +117,7 @@ def build_composite_bloq(self, bb: 'BloqBuilder', x: Soquet, y: Soquet) -> Dict[ x = bb.join(x_split[1:], dtype=QMontgomeryUInt(bitsize=self.bitsize)) # Add constant -p to the y register. - y = bb.add(AddK(bitsize=self.bitsize + 1, k=-1 * self.mod, signed=False, cvs=()), x=y) + y = bb.add(AddK(QMontgomeryUInt(self.bitsize + 1), k=-1 * self.mod), x=y) # Controlled addition of classical constant p if the sign of y after the last addition is # negative. @@ -127,7 +127,7 @@ def build_composite_bloq(self, bb: 'BloqBuilder', x: Soquet, y: Soquet) -> Dict[ sign_split = bb.split(sign) sign_split, y = bb.add( - AddK(bitsize=self.bitsize, k=self.mod, signed=False, cvs=(1,)), x=y, ctrls=sign_split + AddK(QMontgomeryUInt(self.bitsize), k=self.mod).controlled(), x=y, ctrl=sign_split ) sign = bb.join(sign_split) @@ -148,8 +148,8 @@ def build_composite_bloq(self, bb: 'BloqBuilder', x: Soquet, y: Soquet) -> Dict[ def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT': return { Add(QUInt(self.bitsize + 1)): 1, - AddK(self.bitsize + 1, k=-self.mod): 1, - AddK(self.bitsize, k=self.mod).controlled(): 1, + AddK(QUInt(self.bitsize + 1), k=-self.mod): 1, + AddK(QUInt(self.bitsize), k=self.mod).controlled(): 1, LinearDepthGreaterThan(self.bitsize): 1, XGate(): 1, } diff --git a/qualtran/bloqs/rotations/rz_via_phase_gradient_test.py b/qualtran/bloqs/rotations/rz_via_phase_gradient_test.py index 0e905a1e5..b5ce952a1 100644 --- a/qualtran/bloqs/rotations/rz_via_phase_gradient_test.py +++ b/qualtran/bloqs/rotations/rz_via_phase_gradient_test.py @@ -23,7 +23,7 @@ _rz_via_phase_gradient, RzViaPhaseGradient, ) -from qualtran.resource_counting import BloqCount, get_cost_value +from qualtran.resource_counting import get_cost_value, QECGatesCost, GateCounts def test_examples(bloq_autotester): @@ -34,8 +34,10 @@ def test_costs(): n = sympy.Symbol("n") dtype = QUInt(n) bloq = RzViaPhaseGradient(angle_dtype=dtype, phasegrad_dtype=dtype) - # TODO need to improve this to `4 * n - 8` (i.e. Toffoli cost of `n - 2`) - assert get_cost_value(bloq, BloqCount.for_gateset('t')) == {TGate(): 4 * n - 4} + # TODO need to improve this to `n - 2` Ands. + assert get_cost_value(bloq, QECGatesCost()) == GateCounts( + and_bloq=n - 1, clifford=9 * n - 8, measurement=n - 1 + ) @frozen