diff --git a/qualtran/_infra/Bloqs-Tutorial.ipynb b/qualtran/_infra/Bloqs-Tutorial.ipynb index f43624aa3..64ed5ee5a 100644 --- a/qualtran/_infra/Bloqs-Tutorial.ipynb +++ b/qualtran/_infra/Bloqs-Tutorial.ipynb @@ -7,7 +7,7 @@ "source": [ "# Bloqs Tutorial\n", "\n", - "Bloqs lets you represent high-level quantum programs and subroutines as a hierarchical\n", + "Qualtran lets you represent high-level quantum programs and subroutines as a hierarchical\n", "collection of Python objects. The main interface is the `Bloq` abstract base class." ] }, @@ -26,6 +26,17 @@ " ..." ] }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "d4169ffa", + "metadata": {}, + "source": [ + "We use a graph-like container to wire up collections of bloqs to define new bloqs.\n", + "\n", + "By the end of this tutorial, you should understand how to declare bloqs, wire them up, use named registers, use high-bitsize registers, use 'bookkeeping' operations to split and join wires, represent allocations as operations in the graph, and use linear logic to prevent violations of the no cloning theorem." + ] + }, { "cell_type": "markdown", "id": "7a3d76e5", @@ -80,13 +91,14 @@ "bloq, which we will add as we go along.\n", "\n", "The mandatory method is the `Bloq.signature` property. This declares what the inputs and\n", - "outputs are for our bloq. In particular, we declare a name and type information for each\n", - "quantum \"register\" on which the bloq operates. This property can be thought of as analogous\n", - "to the function signature in ordinary programming. For example, it is analogous to function\n", - "declarations in a C header (`*.h`) file.\n", + "outputs are for our bloq, and is a list of registers. A register has a name and quantum-type\n", + "information. By default, a register declares both an input and an output allowing quantum\n", + "data to pass through it, like the \"control\" register below. We call these `THRU` registers.\n", "\n", - "Concretely, we return an ordered collection of `Register` objects, each of which corresponds to a named\n", - "register." + "The `Bloq.signature` property can be thought of as analogous\n", + "to the function signature in ordinary programming. You can think of a bloq\n", + "with just this property implemented like a function\n", + "declarations in a C header (`*.h`) file." ] }, { @@ -108,7 +120,7 @@ "source": [ "The above declares a register named \"control\" with a size of 1. We'll return this as well\n", "as a register for the \"target\" input/output of the CNOT bloq wrapped in the `Signature`\n", - "container." + "container.
The `attrs.frozen` annotation removes some of the boilerplate to write an immutable Python class with a pre-defined set of attributes.
" ] }, { @@ -473,7 +485,7 @@ "## Larger registers\n", "\n", "Our two bloqs have still been operating at the level of individual bits. We now consider\n", - "a general swap between two `n`-sized registers." + "a general swap between two `n`-sized registers.
The `n: int` line means our class has one attribute named `n` of type `int`. The attrs annotation will automatically generate an `__init__` function.
" ] }, { @@ -768,6 +780,191 @@ "show_bloq(cbloq)" ] }, + { + "cell_type": "markdown", + "id": "c20817f9", + "metadata": {}, + "source": [ + "The interleaved wires can get a little confusing. An alternative method of visualization via the familiar \"musical score\" diagram is also available" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e35b5bb", + "metadata": {}, + "outputs": [], + "source": [ + "from qualtran.drawing import get_musical_score_data, draw_musical_score\n", + "msd = get_musical_score_data(cbloq)\n", + "fig, ax = draw_musical_score(msd)\n", + "fig.set_figwidth(9)" + ] + }, + { + "cell_type": "markdown", + "id": "a56b18da", + "metadata": {}, + "source": [ + "## Allocations and de-allocations\n", + "\n", + "We can encode operations that allocate and/or de-allocate quantum data as well. Each `Register` has an attribute called `side`. By default, it is set to `THRU` meaning that the quantum data moves through the register and that register is available for use as both an input and an output." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b195829", + "metadata": {}, + "outputs": [], + "source": [ + "reg = Register('ctrl', 1)\n", + "reg.side" + ] + }, + { + "cell_type": "markdown", + "id": "4788d0c9", + "metadata": {}, + "source": [ + "### `LEFT` and `RIGHT`\n", + "\n", + "We can declare registers that are input-only (\"LEFT\") or output-only (\"RIGHT\"). Pure-state quantum evolution is unitary; so using registers like these implies you're encoding a non-unitary operation. For example: bloqs which allocate a new qubit or discard an existing qubit would have asymmetric registers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "02d510b9", + "metadata": {}, + "outputs": [], + "source": [ + "from qualtran import Side\n", + "\n", + "@attrs.frozen\n", + "class ReAlloc(Bloq):\n", + " @property\n", + " def signature(self):\n", + " return Signature([\n", + " Register('input_only', bitsize=1, side=Side.LEFT),\n", + " Register('output_only', bitsize=1, side=Side.RIGHT),\n", + " ])\n", + " \n", + "show_bloq(ReAlloc())" + ] + }, + { + "cell_type": "markdown", + "id": "1537693c", + "metadata": {}, + "source": [ + "Of course, the signature *only* provides the `side` of the register. It is up to the bloq author to give it functionality by providing a decomposition or annotating it with simulation information. We'll use the `State` and `Effect` one-qubit bloqs provided by the library to explore their behavior under simulation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ad7b8708", + "metadata": {}, + "outputs": [], + "source": [ + "from qualtran.bloqs.basic_gates import ZeroState\n", + "\n", + "# Show a simple allocating bloq and its tensor representation\n", + "show_bloq(ZeroState())\n", + "ZeroState().tensor_contract()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b5a764f7", + "metadata": {}, + "outputs": [], + "source": [ + "from qualtran.bloqs.basic_gates import PlusState, ZeroEffect\n", + "\n", + "bb = BloqBuilder()\n", + "\n", + "# Wire up <+|0>\n", + "q = bb.add(PlusState())\n", + "bb.add(ZeroEffect(), q=q)\n", + "\n", + "# Show it and find its probability\n", + "cbloq = bb.finalize()\n", + "show_bloq(cbloq)\n", + "cbloq.tensor_contract() ** 2" + ] + }, + { + "cell_type": "markdown", + "id": "83c0a66d", + "metadata": {}, + "source": [ + "## Algorithms\n", + "\n", + "We've been looking at small, familiar bloqs to get acquainted with the functionality. Bloqs can represent quantum operations at any level of complexity, but are particularly useful for reasining about high-level algorithms. For example, Qualtran includes a reference implementation of modular exponentiation (the limiting operation for Shor's factoring)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5fd2783", + "metadata": {}, + "outputs": [], + "source": [ + "from qualtran.bloqs.factoring import ModExp\n", + "\n", + "mod_exp = ModExp(base=8, mod=13*17, exp_bitsize=3, x_bitsize=1024)\n", + "show_bloq(mod_exp)" + ] + }, + { + "cell_type": "markdown", + "id": "e6ff4cfd", + "metadata": {}, + "source": [ + "High-level bloqs should be defined in terms of only-slightly-less high-level bloqs to keep each step of the decomposition understandable. The `ModExp` bloq's decomposition is:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f5bf452", + "metadata": {}, + "outputs": [], + "source": [ + "def build_composite_bloq(self, bb: 'BloqBuilder', exponent: 'SoquetT') -> Dict[str, 'SoquetT']:\n", + " x = bb.add(IntState(val=1, bitsize=self.x_bitsize))\n", + " exponent = bb.split(exponent)\n", + "\n", + " # https://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method\n", + " base = self.base\n", + " for j in range(self.exp_bitsize - 1, 0 - 1, -1):\n", + " exponent[j], x = bb.add(self._CtrlModMul(k=base), ctrl=exponent[j], x=x)\n", + " base = base * base % self.mod\n", + "\n", + " return {'exponent': bb.join(exponent), 'x': x}" + ] + }, + { + "cell_type": "markdown", + "id": "5a17bad6", + "metadata": {}, + "source": [ + "In addition to decomposing and visualizing, we can use other protocols to query properties of the bloq or test its correctness. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c2268b3c", + "metadata": {}, + "outputs": [], + "source": [ + "show_bloq(mod_exp.decompose_bloq())" + ] + }, { "cell_type": "markdown", "id": "e74ee2b3", @@ -778,47 +975,13 @@ "Bloqs support a growing list of protocols that let you annotate a given `Bloq` with more\n", "definitions or known information. In the following table we summarize the available protocols. Please note that the method you override as a bloq writer is often different than the method you call as a bloq user.\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - " \n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - " \n", - "\n", - "\n", - "\n", - "\n", - "\n", - " \n", - "\n", - "\n", - "\n", - "\n", - " \n", - "\n", - "\n", - "\n", - "\n", - "\n", - " \n", - "
WhatCall thisOverride thisNotebook
Bloq decompositiondecompose_bloq()build_composite_bloq(...)composite_bloq.ipynb
Numerical simulation via
quimb tensor networks
tensor_contract()add_my_tensors(...)
Classical simulationcall_classically(**vals)on_classical_vals(...)classical_sim.ipynb
Resource countingt_complexity() for both
Conversion to Cirqto_cirq_circuit(**quregs)as_cirq_op(...)cirq_gate.ipynb
" + "| What | Call this | Override this | Docs |\n", + "|----------------------|-----------------------------|-----------------------------|---------------------------------------------------------|\n", + "| Bloq decomposition | `decompose_bloq()` | `build_composite_bloq(...)` | [Composite bloq](/_infra/composite_bloq.ipynb) |\n", + "| Numerical simulation | `tensor_contract()` | `add_my_tensors(...)` | |\n", + "| Classical simulation | `call_classically(**vals)` | `on_classical_vals(...)` | [Classical simulation](/simulation/classical_sim.ipynb) |\n", + "| Resource counting | `bloq_counts()` | `bloq_counts()` | [Bloq counts](/resource_counting/bloq_counts.ipynb) |\n", + "| Conversion to Cirq | `to_cirq_circuit(**quregs)` | `as_cirq_op(...)` | [Cirq Interop](/cirq_interop/cirq_interop.ipynb) |" ] } ], diff --git a/qualtran/cirq_interop/cirq_interop.ipynb b/qualtran/cirq_interop/cirq_interop.ipynb index 6b4dcc560..ca96d877e 100644 --- a/qualtran/cirq_interop/cirq_interop.ipynb +++ b/qualtran/cirq_interop/cirq_interop.ipynb @@ -5,9 +5,9 @@ "id": "eb6226ee", "metadata": {}, "source": [ - "# Cirq Conversion\n", + "# Cirq Interoperability\n", "\n", - "Cirq is a quantum SDK for explicitly addressing physical qubits and scheduling gates. You can consider it analogous to a quantum assembly language. The bloqs infrastructure provides interoperability with Cirq." + "Cirq is a quantum SDK for explicitly addressing physical qubits and scheduling gates. You can consider it analogous to a quantum assembly language. Qualtran provides interoperability with Cirq." ] }, {