diff --git a/qualtran/bloqs/arithmetic/addition.ipynb b/qualtran/bloqs/arithmetic/addition.ipynb index ab22770e1..5997adc79 100644 --- a/qualtran/bloqs/arithmetic/addition.ipynb +++ b/qualtran/bloqs/arithmetic/addition.ipynb @@ -38,7 +38,7 @@ "## `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", + "Implements $U|a\\rangle|b\\rangle \\rightarrow |a\\rangle|a+b\\rangle$ using $n-1$ AND gates.\n", "\n", "#### Parameters\n", " - `a_dtype`: Quantum datatype used to represent the integer a.\n", @@ -49,9 +49,81 @@ " - `b`: A b_dtype.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" + " - [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648). Gidney 2018. The construction used in this bloq, evolved from [2].\n", + " - [A new quantum ripple-carry addition circuit](https://arxiv.org/abs/quant-ph/0410184). Cuccaro et. al. 2004.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "263c1c98-db09-4a11-bf1b-dc7426c66fce", + "metadata": {}, + "outputs": [], + "source": [ + "from qualtran import BloqBuilder\n", + "from qualtran.bloqs.arithmetic.addition import IdiomaticAdd, _LeftAddPart, _RightAddPart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f30f4130-5d5b-489a-8ed9-50902ec15431", + "metadata": {}, + "outputs": [], + "source": [ + "bb = BloqBuilder()\n", + "c = bb.add_register('c', 1)\n", + "i = bb.add_register('i', 1)\n", + "t = bb.add_register('t', 1)\n", + "c, i, t, cc = bb.add(_LeftAddPart(), c=c, i=i, t=t)\n", + "c, i, t = bb.add(_RightAddPart(), c=c, i=i, t=t, cc=cc)\n", + "cbloq = bb.finalize(c=c, i=i, t=t)\n", + "show_bloq(cbloq, 'musical_score')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13bc1255-8e3c-49f6-8c1f-c174ae64cdc8", + "metadata": {}, + "outputs": [], + "source": [ + "show_bloq(cbloq.flatten_once(lambda b: True), 'musical_score')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77d8cfcf-bae6-4420-a145-729622da23e0", + "metadata": {}, + "outputs": [], + "source": [ + "m = IdiomaticAdd(5)\n", + "cm = m.decompose_bloq()\n", + "show_bloq(cm, 'musical_score')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "08645d99-8f4e-4324-9028-9401911228c7", + "metadata": {}, + "outputs": [], + "source": [ + "%time\n", + "m = IdiomaticAdd(10_000)\n", + "cbloq = m.decompose_bloq()\n", + "print(cbloq._binst_graph.number_of_nodes())" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "42153aa5-d2fa-46a7-b2f1-9720ef5db669", + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": null, @@ -95,7 +167,17 @@ }, "outputs": [], "source": [ - "add_large = Add(QUInt(bitsize=64))" + "add_large = Add(QUInt(bitsize=5000))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cbc705bc-b7cb-4f32-b3ba-0410f45f15cb", + "metadata": {}, + "outputs": [], + "source": [ + "add_large.decompose_bloq()" ] }, { @@ -116,7 +198,10 @@ "execution_count": null, "id": "e5b746c1", "metadata": { - "collapsed": false + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } }, "outputs": [], "source": [ @@ -337,7 +422,7 @@ { "cell_type": "code", "execution_count": null, - "id": "cd255bf9", + "id": "47b4b6d7", "metadata": { "cq.autogen": "AddK.bloq_doc.py" }, @@ -348,7 +433,7 @@ }, { "cell_type": "markdown", - "id": "7538f9a5", + "id": "7152a86b", "metadata": { "cq.autogen": "AddK.example_instances.md" }, @@ -359,7 +444,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4305289f", + "id": "a50b7ae4", "metadata": { "cq.autogen": "AddK.add_k" }, @@ -372,7 +457,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f6048819", + "id": "7f7e9c09", "metadata": { "cq.autogen": "AddK.add_k_small" }, @@ -384,7 +469,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b67fd469", + "id": "802f8da2", "metadata": { "cq.autogen": "AddK.add_k_large" }, @@ -395,7 +480,7 @@ }, { "cell_type": "markdown", - "id": "b8b04228", + "id": "21737304", "metadata": { "cq.autogen": "AddK.graphical_signature.md" }, @@ -406,7 +491,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e93e7f2e", + "id": "1849daf3", "metadata": { "cq.autogen": "AddK.graphical_signature.py" }, @@ -419,7 +504,7 @@ }, { "cell_type": "markdown", - "id": "13552795", + "id": "83759458", "metadata": { "cq.autogen": "AddK.call_graph.md" }, @@ -430,7 +515,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d8d6584e", + "id": "a3c60bc5", "metadata": { "cq.autogen": "AddK.call_graph.py" }, @@ -471,7 +556,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.11.8" } }, "nbformat": 4, diff --git a/qualtran/bloqs/arithmetic/addition.py b/qualtran/bloqs/arithmetic/addition.py index c1965bba2..584478bca 100644 --- a/qualtran/bloqs/arithmetic/addition.py +++ b/qualtran/bloqs/arithmetic/addition.py @@ -57,6 +57,7 @@ ) from qualtran.bloqs import util_bloqs from qualtran.bloqs.basic_gates import CNOT, XGate +from qualtran.bloqs.mcmt import MultiTargetCNOT from qualtran.bloqs.mcmt.and_bloq import And from qualtran.bloqs.mcmt.multi_control_multi_target_pauli import MultiControlX from qualtran.cirq_interop import decompose_from_cirq_style_method @@ -73,6 +74,82 @@ from qualtran.symbolics import SymbolicInt +@frozen +class _LeftAddPart(Bloq): + """The 'left' part of the addition building block from the reference, figure 2.""" + + @property + def signature(self) -> Signature: + return Signature( + [ + Register('c', QUInt(1)), + Register('i', QUInt(1)), + Register('t', QUInt(1)), + Register('out', QUInt(1), side=Side.RIGHT), + ] + ) + + def build_composite_bloq(self, bb: 'BloqBuilder', c, i, t) -> Dict[str, 'SoquetT']: + c, [i, t] = bb.add(MultiTargetCNOT(2), ctrl=c, targets=[i, t]) + [i, t], out = bb.add(And(), ctrl=[i, t]) + c, out = bb.add(CNOT(), ctrl=c, target=out) + return {'c': c, 'i': i, 't': t, 'out': out} + + +@frozen +class _RightAddPart(Bloq): + """The 'right' part of the addition building block from the reference, figure 2.""" + + @property + def signature(self) -> 'Signature': + return Signature( + [ + Register('c', QUInt(1)), + Register('i', QUInt(1)), + Register('t', QUInt(1)), + Register('cc', QUInt(1), side=Side.LEFT), + ] + ) + + def build_composite_bloq(self, bb: 'BloqBuilder', c, i, t, cc) -> Dict[str, 'SoquetT']: + c, cc = bb.add(CNOT(), ctrl=c, target=cc) + i, t = bb.add(And().adjoint(), ctrl=[i, t], target=cc) + c, i = bb.add(CNOT(), ctrl=c, target=i) + i, t = bb.add(CNOT(), ctrl=i, target=t) + return {'c': c, 'i': i, 't': t} + + +@frozen +class IdiomaticAdd(Bloq): + """An addition circuit; follows figure 1""" + + n: 'SymbolicInt' + + @property + def signature(self) -> Signature: + return Signature([Register('a', QUInt(self.n)), Register('b', QUInt(self.n))]) + + def build_composite_bloq(self, bb: 'BloqBuilder', a, b) -> Dict[str, 'SoquetT']: + a_bits = bb.split(a) + b_bits = bb.split(b) + anc = bb.allocate(1) + ancs = [anc] + + for i in range(self.n): + ancs[-1], a_bits[i], b_bits[i], anc = bb.add( + _LeftAddPart(), c=ancs[-1], i=a_bits[i], t=b_bits[i] + ) + ancs.append(anc) + + for i in range(self.n - 1, -1, -1): + ancs[i], a_bits[i], b_bits[i] = bb.add( + _RightAddPart(), c=ancs[i], i=a_bits[i], t=b_bits[i], cc=ancs[i + 1] + ) + + bb.free(ancs[0]) + return {'a': bb.join(a_bits), 'b': bb.join(b_bits)} + + @frozen class Add(Bloq): r"""An n-bit addition gate. diff --git a/qualtran/bloqs/mcmt/multi_control_multi_target_pauli.py b/qualtran/bloqs/mcmt/multi_control_multi_target_pauli.py index 92cbd2434..11629a28e 100644 --- a/qualtran/bloqs/mcmt/multi_control_multi_target_pauli.py +++ b/qualtran/bloqs/mcmt/multi_control_multi_target_pauli.py @@ -60,7 +60,9 @@ class MultiTargetCNOT(GateWithRegisters): @cached_property def signature(self) -> Signature: - return Signature.build(control=1, targets=self.bitsize) + return Signature( + [Register('ctrl', QBit()), Register('targets', QBit(), shape=(self.bitsize,))] + ) def decompose_from_registers( self,