Skip to content

Commit

Permalink
Documentation improvements - Tutorial (#320)
Browse files Browse the repository at this point in the history
* Upgrade documentation

* remove image

---------

Co-authored-by: Tanuj Khattar <[email protected]>
  • Loading branch information
mpharrigan and tanujkhattar authored Jul 21, 2023
1 parent a96706b commit 681dc8f
Show file tree
Hide file tree
Showing 2 changed files with 215 additions and 52 deletions.
263 changes: 213 additions & 50 deletions qualtran/_infra/Bloqs-Tutorial.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -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."
]
},
Expand All @@ -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",
Expand Down Expand Up @@ -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."
]
},
{
Expand All @@ -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. <div class=\"alert alert-block alert-info\">The `attrs.frozen` annotation removes some of the boilerplate to write an immutable Python class with a pre-defined set of attributes.</div>"
]
},
{
Expand Down Expand Up @@ -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. <div class=\"alert alert-block alert-info\">The `n: int` line means our class has one attribute named `n` of type `int`. The attrs annotation will automatically generate an `__init__` function.</div>"
]
},
{
Expand Down Expand Up @@ -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",
Expand All @@ -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",
"<table>\n",
"<thead>\n",
"<tr>\n",
"<th>What</th>\n",
"<th>Call this</th>\n",
"<th>Override this</th>\n",
"<th>Notebook</th>\n",
"</tr>\n",
"</thead>\n",
"<tbody>\n",
" \n",
"<tr><td style='text-align: left;'>Bloq decomposition</td>\n",
"<td><code>decompose_bloq()</code></td>\n",
"<td><code>build_composite_bloq(...)</code></td>\n",
"<td>composite_bloq.ipynb</td>\n",
"</tr>\n",
"\n",
"<tr><td style='text-align: left'>Numerical simulation via<br/>quimb tensor networks</td>\n",
"<td><code>tensor_contract()</code></td>\n",
"<td><code>add_my_tensors(...)</code></td>\n",
"<td></td>\n",
"</tr>\n",
" \n",
"<tr><td style='text-align: left'>Classical simulation</td>\n",
"<td><code>call_classically(**vals)</code></td>\n",
"<td><code>on_classical_vals(...)</code></td>\n",
"<td>classical_sim.ipynb</td>\n",
"</tr>\n",
" \n",
"<tr><td style='text-align: left'>Resource counting</td>\n",
"<td colspan='2' style='text-align: center'><code>t_complexity()</code> for both</td>\n",
"<td></td>\n",
"</tr>\n",
" \n",
"<tr><td style='text-align: left'>Conversion to Cirq</td>\n",
"<td><code>to_cirq_circuit(**quregs)</code></td>\n",
"<td><code>as_cirq_op(...)</code></td>\n",
"<td>cirq_gate.ipynb</td>\n",
"</tr>\n",
" \n",
"</tbody></table>"
"| 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) |"
]
}
],
Expand Down
4 changes: 2 additions & 2 deletions qualtran/cirq_interop/cirq_interop.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -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."
]
},
{
Expand Down

0 comments on commit 681dc8f

Please sign in to comment.