\n",
23 | "$ \\newcommand{\\ket}[1]{|#1\\rangle} $\n",
24 | "$ \\newcommand{\\bra}[1]{\\langle #1|} $\n",
25 | "$\\newcommand{\\iu}{{i\\mkern1mu}}$\n",
26 | "$ \\newcommand{\\e}{\\mathrm{e}}$\n",
27 | "$\\newcommand{\\bigo}{\\mathcal{O}}$\n",
28 | "$\\newcommand{\\set}[1]{\\{#1\\}}$\n",
29 | "$\\newcommand{\\oper}[1]{\\mathcal{#1}}$\n",
30 | "$\\newcommand{\\st}[1]{\\mathcal{#1}}$\n",
31 | "$\\newcommand{\\vecs}[1]{\\mathcal{#1}}$\n",
32 | "$\\newcommand{\\group}[1]{\\mathcal{#1}}$\n",
33 | "$\\newcommand{\\union}{\\hspace{0.25em}\\cup\\hspace{0.25em}}$"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "id": "d728895f-18c2-4ef9-82e1-be8ad48b067e",
39 | "metadata": {},
40 | "source": [
41 | "We will start with density matrices and CPTP maps.\n",
42 | "\n",
43 | "> Given the Hilbert space $\\vecs{H}$, let $L(\\vecs{H})$ be the set of automorphisms on this space. Then a **density operator** is such a linear operator $\\rho \\in L(\\vecs{H})$, such that $\\rho$ is\n",
44 | " \n",
45 | " * Hermitian: $\\rho = \\rho^\\dagger$,\n",
46 | " * Normalized: $\\text{Tr}(\\rho)=1$,\n",
47 | " * Positive semidefinite $\\bra{\\psi}\\rho\\ket{\\psi} \\ge 0$ (written $\\rho \\ge 0)$).\n",
48 | "\n",
49 | "A density operator is the most complete description of a quantum system. The most general evolution of density operators, including unitary operations, transient interactions with other systems and measurements, is described by quantum operations, more formally referred to as Completely-Positive Trace-Preserving (CPTP) maps.\n",
50 | "\n",
51 | "> A **quantum operation** is a CPTP map $\\oper{S}: L(\\vecs{H}) \\to L(\\vecs{H})$ that maps density operators to density operators such that $\\oper{S}$ is \n",
52 | " \n",
53 | " * Positive: if $\\rho \\ge 0$, then $\\oper{S}(\\rho) \\ge 0$,\n",
54 | " * Completely-positive: if $\\sigma \\in L(\\vecs{H}^{AB})$ such that $\\sigma \\ge 0$ and $\\text{Tr}_B(\\sigma) = \\rho$, then $(I \\otimes S)(\\sigma) \\ge 0$.\n",
55 | " * Trace-preserving: $\\text{Tr}(S(\\rho)) = \\text{Tr}(\\rho)$. \n",
56 | " \n",
57 | "\n",
58 | "Any such operation has a representation, called the Kraus representation.\n",
59 | "\n",
60 | "> For any quantum operation $\\oper{S}$, there exist a non-unique set of operators $\\set{A_k}_k$, where $A_k\\in L(\\vecs{H})$, such that\n",
61 | " \\begin{equation}\n",
62 | " S(\\rho) = \\sum_k A_k\\rho A_k^\\dagger,\n",
63 | " \\end{equation}\n",
64 | " and $\\sum_k A_k^\\dagger A_k = I$. This is called the **Kraus representation**.\n",
65 | "\n",
66 | "\n",
67 | "\n",
68 | "**Example**: The dephasing channel...\n",
69 | "\n",
70 | "\n",
71 | "**Example**: The depolarizing channel..."
72 | ]
73 | },
74 | {
75 | "cell_type": "markdown",
76 | "id": "b38aa6ac-51a2-4d7f-93d7-51f17b75d7b7",
77 | "metadata": {},
78 | "source": [
79 | "### The noise channel model\n",
80 | "We are now ready to define the noise that any qubits sent through the channel experience. Suppose, Alice sends $n$-qubits to Bob via the channel. Then, the noise model is the same single-qubit channel $S$ applied to each qubit. So, to describe the noise the $n$-qubit quantum operation is\n",
81 | "\\begin{equation}\n",
82 | " \\oper{N} = \\oper{S}^{\\otimes n}.\n",
83 | "\\end{equation}\n",
84 | "\n",
85 | "Given the description of noise model an the $n$-qubit operator $\\oper{N}$, we can identify a Krauss decomposition\n",
86 | "\\begin{equation}\n",
87 | " \\oper{N}(\\rho) = \\sum_i A_i\\rho A_i^\\dagger,\n",
88 | "\\end{equation}\n",
89 | "where there are some $A_i$ with weight equal to $n$. However, recall that in the classical case if the probability of error $p$ is small, then large errors are unlikely. There is a similar theorem for noise in the quantum systems.\n",
90 | "\n",
91 | "\n",
92 | " A CPTP map on $n$-qubits that can be written with Krauss operators each of which is a sum of operators with weight less than $t$ is a **$t$-qubit error**.\n",
93 | "\n",
94 | "\n",
95 | "**Theorem**: Let $\\oper{S}$ be a single-qubit quantum channel close to the identity $||\\oper{S} - I||_\\diamond < \\epsilon$ for some $\\epsilon$. Then there exists a $t$-qubit error channel $\\oper{\\tilde E}$ such that\n",
96 | " \\begin{equation}\n",
97 | " ||\\oper{S}^{\\otimes n} - \\oper{\\tilde E}||_\\diamond = \\mathcal{O}\\left(\\binom{n}{t} \\epsilon^t\\right).\n",
98 | " \\end{equation}\n",
99 | "\n",
100 | "In simple language the above theorem states that if each of the $n$-qubits has only a very small chance of incurring an error, then one is very likely to observe errors on not very many qubits.\n",
101 | "\n",
102 | "Suppose now that we have a $t$-qubit error channel $\\oper{E}$, with a Krauss representation given by the operators $\\set{E_i}_i$. From now on, we will refer to the $E_i$ as errors, and $\\set{E_i}_i$ as the set of errors. For instance, if $S$ is the dephasing channel, and $n = 2$, then\n",
103 | "\\begin{equation}\n",
104 | " \\st{E} = \\set{I, Z_1, Z_2, Z_1Z_2},\n",
105 | "\\end{equation}\n",
106 | "where the $I$ is included as the no-error possibility.\n",
107 | "\n",
108 | "Our goal is to find quantum codes that are able to correct a given set of errors. Though often the research process proceeds in the opposite direction, and first a quantum code is defined and then its set of correctable errors is determined. We are now in the position to define a quantum code."
109 | ]
110 | },
111 | {
112 | "cell_type": "code",
113 | "execution_count": null,
114 | "id": "a77c58c4-2b45-4224-b7c8-64397651ea13",
115 | "metadata": {},
116 | "outputs": [],
117 | "source": []
118 | }
119 | ],
120 | "metadata": {
121 | "kernelspec": {
122 | "display_name": "Python 3 (ipykernel)",
123 | "language": "python",
124 | "name": "python3"
125 | },
126 | "language_info": {
127 | "codemirror_mode": {
128 | "name": "ipython",
129 | "version": 3
130 | },
131 | "file_extension": ".py",
132 | "mimetype": "text/x-python",
133 | "name": "python",
134 | "nbconvert_exporter": "python",
135 | "pygments_lexer": "ipython3",
136 | "version": "3.10.8"
137 | },
138 | "widgets": {
139 | "application/vnd.jupyter.widget-state+json": {
140 | "state": {},
141 | "version_major": 2,
142 | "version_minor": 0
143 | }
144 | }
145 | },
146 | "nbformat": 4,
147 | "nbformat_minor": 5
148 | }
149 |
--------------------------------------------------------------------------------
/chapters/cec/distance.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "a23fe73c",
6 | "metadata": {
7 | "tags": []
8 | },
9 | "source": [
10 | "# Distance of a code"
11 | ]
12 | },
13 | {
14 | "cell_type": "markdown",
15 | "id": "b00286a1",
16 | "metadata": {
17 | "tags": [
18 | "remove-cell"
19 | ]
20 | },
21 | "source": [
22 | "
Latex macros
\n",
23 | "$\\newcommand{\\vec}[1]{\\mathbf{#1}}$\n",
24 | "$ \\newcommand{\\ket}[1]{|#1\\rangle} $\n",
25 | "$ \\newcommand{\\bra}[1]{\\langle #1|} $\n",
26 | "$\\newcommand{\\iu}{{i\\mkern1mu}}$\n",
27 | "$ \\newcommand{\\e}{\\mathrm{e}}$\n",
28 | "$\\newcommand{\\bigo}{\\mathcal{O}}$\n",
29 | "$\\newcommand{\\set}[1]{\\{#1\\}}$\n",
30 | "$\\newcommand{\\oper}[1]{\\mathcal{#1}}$\n",
31 | "$\\newcommand{\\st}[1]{\\mathcal{#1}}$\n",
32 | "$\\newcommand{\\vecs}[1]{\\mathcal{#1}}$\n",
33 | "$\\newcommand{\\group}[1]{\\mathcal{#1}}$\n",
34 | "$\\newcommand{\\union}{\\hspace{0.25em}\\cup\\hspace{0.25em}}$"
35 | ]
36 | },
37 | {
38 | "cell_type": "markdown",
39 | "id": "06aac00c-3a56-428e-b72f-eb028e9ceb53",
40 | "metadata": {},
41 | "source": [
42 | "A crucial quantity of interest when discussing codes, is their distance. The distance determines the number of errors that can be corrected. Whenever an error occurs on a codeword $\\vec{c}$, it creates a corrupted codeword $\\vec{\\tilde{c}}$ that is at some 'distance' away from the $\\vec{c}$. If the distance is small, then it is still possible to correct the error, but if the distance is larger than some value, than the error cannot be corrected. The threshold value is called the distance of the code. We formalize it below."
43 | ]
44 | },
45 | {
46 | "cell_type": "markdown",
47 | "id": "c6d7faf0-ac17-422e-9853-0016e9545111",
48 | "metadata": {
49 | "tags": []
50 | },
51 | "source": [
52 | "## Hamming distance\n",
53 | "The Hamming distance (not to be confused with the Hamming code; [Hamming](https://en.wikipedia.org/wiki/Richard_Hamming) was just a very prolific scientist) is going to be used to build the notion of code distance.\n",
54 | "\n",
55 | "> The **Hamming distance** between two bit strings $\\vec{s},\\vec{t}$ is the number of places where they have different symbols. This will be denoted $d_H(s,t)$.\n",
56 | "\n",
57 | "The Hamming distance determines the number of substitutions or errors required to transform a length-$n$ $\\vec{s}$ into another length-$n$ vector $\\vec{t}$ or vice versa. Hence, it is a very natural definition for our purposes. The Hamming distance can be calculated as\n",
58 | "\\begin{equation}\n",
59 | " d_H(\\vec{s},\\vec{t}) = \\sum_i \\vec{s}_i + \\vec{t}_i,\n",
60 | "\\end{equation}\n",
61 | "where the addition is in the binary field, but the summation is regular integer addition.\n",
62 | "\n",
63 | "For example, the distance between $1011$ and $0111$ is two. You have to flip the first two entries to go from the first to the second or vice versa."
64 | ]
65 | },
66 | {
67 | "cell_type": "markdown",
68 | "id": "5b4979a5-6737-4f76-bdee-17faf9386477",
69 | "metadata": {
70 | "tags": []
71 | },
72 | "source": [
73 | "## Distance of a code\n",
74 | "\n",
75 | "Using the above definition, we define the notion of the distance of a code.\n",
76 | "\n",
77 | "> The distance $d$ of a code is the minimum Hamming distance between any two codewords. Formally,\n",
78 | "\\begin{equation}\n",
79 | " d = \\min_{\\vec{c}_1,\\vec{c}_2\\in \\vecs{C}} d_H(\\vec{c}_1,\\vec{c}_2).\n",
80 | "\\end{equation}\n",
81 | "\n",
82 | "This seems to be a tedious optimization problem. However, we can simplify the calculation in the following way. First, we define the weight of a bit string.\n",
83 | "\n",
84 | "> The *weight* of a string $\\vec{s}$, denoted $wt(\\vec{s})$, is the number of ones in the string.\n",
85 | "\n",
86 | "The weight can also be thought of as the Hamming distance of $\\vec{s}$ from the all $0$ string, i.e.\n",
87 | "\\begin{equation}\n",
88 | "wt(\\vec{s}) = d_H(\\vec{s},0^n).\n",
89 | "\\end{equation}\n",
90 | "\n",
91 | "Next note that the zero message, $\\vec{m} = 0^k$, maps to the zero codeword $\\vec{c} = 0^kG = 0^n$. \n",
92 | "\n",
93 | "Then, using the properties of the Hamming distance and the fact that the codespace is a vector space, and letting $\\vec{c}_1 = \\vec{x}_1G \\ne \\vec{c}_2=\\vec{x}_2G$, we have\n",
94 | "\\begin{align}\n",
95 | " d_H(\\vec{c}_1,\\vec{c}_2) &= d_H(\\vec{x}_1G, \\vec{x}_2G), \\\\\n",
96 | " &= d_H(\\vec{x}_1G + \\vec{x}_2G, 0^k), \\\\\n",
97 | " &= d_H((\\vec{x}_1 + \\vec{x}_2)G, 0^k), \\\\\n",
98 | " &= d_H(\\vec{c}_3, 0^k), \\\\\n",
99 | " &= wt(\\vec{c}_3),\n",
100 | "\\end{align}\n",
101 | "where in the second last step we have defined $\\vec{c}_3 = (\\vec{x}_1 + \\vec{x}_2)G$, which must exist in $C$ because $\\vec{x}_1 + \\vec{x}_2$ is part of the message space, and $\\vec{c}_3 \\ne 0$. So we have discovered that the distance of a code,\n",
102 | "\\begin{equation}\n",
103 | " d = \\min_{\\vec{c} \\in \\vecs{C}} wt(\\vec{c}),\n",
104 | "\\end{equation}\n",
105 | "is just the minimum weight string.\n",
106 | "\n",
107 | "Another theorem simplifies the calculation even more.\n",
108 | "\n",
109 | "**Theorem:** The distance $d$ of a code $\\vecs{C}$ is equal to the minimum number of linearly dependent columns of the parity check matrix $H$ of the code."
110 | ]
111 | },
112 | {
113 | "cell_type": "markdown",
114 | "id": "f9594844-af76-45e0-8cc3-f766e0cd6ea4",
115 | "metadata": {
116 | "tags": []
117 | },
118 | "source": [
119 | "## Relationship of distance and correctable errors\n",
120 | "\n",
121 | "The use of the distance of a code is given by the following theorem.\n",
122 | "\n",
123 | "**Theorem:** A code with distance $d$ can\n",
124 | "* Correct at most $d-1$ erasure errors,\n",
125 | "* Detect at most $d-1$ bit flip errors,\n",
126 | "* Correct at most $\\left\\lfloor \\frac{d-1}{2}\\right\\rfloor$ bit flip errors.\n",
127 | "\n",
128 | "\n",
129 | "Therefore, a single quantity tells us the power of a code at detecting and correcting various types of errors.\n",
130 | "\n",
131 | "*Question:* Compute the distance of the $[7,4]$ Hamming code, using both methods outlined above. Show $d = 3$.\n",
132 | "\n",
133 | "\n",
134 | "> If the distance $d$ of a code $\\vecs{C}$ is known, then $\\vecs{C}$ is denoted as $[n,k,d]$.\n",
135 | "\n",
136 | "\n",
137 | "*Question:* Determine the distance $d$ of the $[n,1,d]$ repetition code."
138 | ]
139 | }
140 | ],
141 | "metadata": {
142 | "kernelspec": {
143 | "display_name": "Python 3 (ipykernel)",
144 | "language": "python",
145 | "name": "python3"
146 | },
147 | "language_info": {
148 | "codemirror_mode": {
149 | "name": "ipython",
150 | "version": 3
151 | },
152 | "file_extension": ".py",
153 | "mimetype": "text/x-python",
154 | "name": "python",
155 | "nbconvert_exporter": "python",
156 | "pygments_lexer": "ipython3",
157 | "version": "3.10.8"
158 | },
159 | "widgets": {
160 | "application/vnd.jupyter.widget-state+json": {
161 | "state": {},
162 | "version_major": 2,
163 | "version_minor": 0
164 | }
165 | }
166 | },
167 | "nbformat": 4,
168 | "nbformat_minor": 5
169 | }
170 |
--------------------------------------------------------------------------------
/chapters/overview/whatandwhy.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "a23fe73c",
6 | "metadata": {
7 | "tags": []
8 | },
9 | "source": [
10 | "# Why error-correction and a model for it"
11 | ]
12 | },
13 | {
14 | "cell_type": "markdown",
15 | "id": "b00286a1",
16 | "metadata": {
17 | "tags": [
18 | "remove-cell"
19 | ]
20 | },
21 | "source": [
22 | "
Latex macros
\n",
23 | "$ \\newcommand{\\ket}[1]{|#1\\rangle} $\n",
24 | "$ \\newcommand{\\bra}[1]{\\langle #1|} $\n",
25 | "$\\newcommand{\\iu}{{i\\mkern1mu}}$\n",
26 | "$ \\newcommand{\\e}{\\mathrm{e}}$\n",
27 | "$\\newcommand{\\bigo}{\\mathcal{O}}$\n",
28 | "$\\newcommand{\\set}[1]{\\{#1\\}}$\n",
29 | "$\\newcommand{\\oper}[1]{\\mathcal{#1}}$\n",
30 | "$\\newcommand{\\st}[1]{\\mathcal{#1}}$\n",
31 | "$\\newcommand{\\vecs}[1]{\\mathcal{#1}}$\n",
32 | "$\\newcommand{\\group}[1]{\\mathcal{#1}}$\n",
33 | "$\\newcommand{\\union}{\\hspace{0.25em}\\cup\\hspace{0.25em}}$"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "id": "d34df382-fb0e-49d2-a3b2-bc850fe4e5e1",
39 | "metadata": {},
40 | "source": [
41 | "## Why do we need error-correction?\n",
42 | "\n",
43 | "All computational and communication tasks are done with physical apparatus, and such apparatus is often noisy and unreliable. This means that there is a non-zero chance that the apparatus makes errors in the execution of its processes. One solution to this problem is to try and manufacture better devices. But this is not always possible, either because there is no known way of making better devices or because it is far too costly to make near perfect devices.\n",
44 | "\n",
45 | "Thankfully, we have discovered procedures and algorithms that can be used to increase the probability that our noisy hardware outputs the correct results, with a modest increase in costs. The field of study that deals with such procedures and algorithms is called *error-correction*.\n",
46 | "\n",
47 | "This field first emerged in tandem with the development of analog and digital classical communication and computation devices in the middle of the twentieth century. Today, classical error-correction is a mature field, whose results are applied in a wide variety of circumstances. For example, if the computer you are using to read this book is connected to the internet via Wi-Fi, then there is error-correcting algorithms being employed by your computer and the router to detect if any of the received packets were corrupted during transmission.\n",
48 | "\n",
49 | "On the other hand, the field of quantum error-correction, which concerns error-correction techniques on quantum communication and computing devices, began in the late 1990s. Since then it has made rapid advancements, and many techniques and concrete algorithms have been discovered. This is very encouraging, because the hardware for quantum computers is so small and sensitive, it seems unlikely that we will ever be able to make physical error rates small enough to build quantum computers without the need for error-correction."
50 | ]
51 | },
52 | {
53 | "cell_type": "markdown",
54 | "id": "e3514d8f-6393-45b4-9783-fc449e73daf1",
55 | "metadata": {},
56 | "source": [
57 | "## A model for error-correction problems\n",
58 | "To understand the field, it is best to start with a simple problem that captures the essence of the field. This simple problem for error-correction, both classical and quantum, has an abstract model which we now present. \n",
59 | "\n",
60 | "Alice wants to send information to Bob via a transmission line or channel connecting them. The information could be a string of bits in the classical case, or a string of qubits in the quantum case. Pictorially, we have the following scenario.\n",
61 | "\n",
62 | "\n",
63 | "\n",
64 | "As you can see, we assume that the channel is noisy, so any information is subject to errors during transmission. On the other hand, we will assume, Alice and Bob have perfect apparatus, which allows them to execute error-correction algorithms with no errors. \n",
65 | "\n",
66 | "This is our basic model for error-correction problems. It seems like it is a specialized to a communication problem, but with a little bit of imagination, we can see how it applies to other types of problems as well. For example, a computer memory can deteriorate over time, causing the stored information to be corrupted. In this scenario, Alice is the person who initially recorded the information to the memory. The noisy channel in this case is across time, rather than space as above, because the memory stays in place and over time gets corrupted. Bob is the person who attempts to read the memory at a later point in time."
67 | ]
68 | },
69 | {
70 | "cell_type": "markdown",
71 | "id": "40e1cf61-69e6-418f-af0d-ba9b1d6cda03",
72 | "metadata": {},
73 | "source": [
74 | "## Our goal\n",
75 | "\n",
76 | "Our goal, over the next few chapters, is to design procedures for Alice and Bob to communicate reliably, under the above model. We will do so for both the classical and the quantum case.\n",
77 | "\n",
78 | "However, as we will discuss in detail later, this model does not capture all interesting problems that are studied by the field of error-correction. The most important such problem is that of performing quantum computation with unreliable hardware. In this case, there is no part of our system that is error-free and therefore, the problem becomes much more difficult to solve. Fortunately, we will discover that there are algorithms, called fault-tolerant algorithms, that will work for such problems as well. Fault-tolerance algorithms will be the subject of latter chapters of this book."
79 | ]
80 | },
81 | {
82 | "cell_type": "markdown",
83 | "id": "103eb374-a337-40a3-99f6-3abf1f9dea51",
84 | "metadata": {},
85 | "source": [
86 | "## No quantum without classical\n",
87 | "\n",
88 | "To have a good understanding of quantum error-correction, it is essential to first start by learning the fundamentals of classical error-correction. There are a few reasons for it. The first is that classical algorithms are often conceptually simpler than quantum, and therefore easier to understand. Secondly, many quantum error-correction algorithms are directly or indirectly constructed from classical error-correction algorithms. Therefore, if one is to learn how to do this, one should be aware of the properties of the classical algorithms.\n",
89 | "\n",
90 | "Therefore, in this book, we will first spend some time learning about classical error-correction. As we will refer to this material often later on, the reader should not skip it."
91 | ]
92 | }
93 | ],
94 | "metadata": {
95 | "kernelspec": {
96 | "display_name": "Python 3 (ipykernel)",
97 | "language": "python",
98 | "name": "python3"
99 | },
100 | "language_info": {
101 | "codemirror_mode": {
102 | "name": "ipython",
103 | "version": 3
104 | },
105 | "file_extension": ".py",
106 | "mimetype": "text/x-python",
107 | "name": "python",
108 | "nbconvert_exporter": "python",
109 | "pygments_lexer": "ipython3",
110 | "version": "3.10.8"
111 | },
112 | "widgets": {
113 | "application/vnd.jupyter.widget-state+json": {
114 | "state": {},
115 | "version_major": 2,
116 | "version_minor": 0
117 | }
118 | }
119 | },
120 | "nbformat": 4,
121 | "nbformat_minor": 5
122 | }
123 |
--------------------------------------------------------------------------------
/chapters/cec/binary-field.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "a23fe73c",
6 | "metadata": {
7 | "tags": []
8 | },
9 | "source": [
10 | "# The binary field"
11 | ]
12 | },
13 | {
14 | "cell_type": "markdown",
15 | "id": "b00286a1",
16 | "metadata": {
17 | "tags": [
18 | "remove-cell"
19 | ]
20 | },
21 | "source": [
22 | "
Latex macros
\n",
23 | "$\\newcommand{\\vec}[1]{\\mathbf{#1}}$\n",
24 | "$ \\newcommand{\\ket}[1]{|#1\\rangle} $\n",
25 | "$ \\newcommand{\\bra}[1]{\\langle #1|} $\n",
26 | "$\\newcommand{\\iu}{{i\\mkern1mu}}$\n",
27 | "$ \\newcommand{\\e}{\\mathrm{e}}$\n",
28 | "$\\newcommand{\\bigo}{\\mathcal{O}}$\n",
29 | "$\\newcommand{\\set}[1]{\\{#1\\}}$\n",
30 | "$\\newcommand{\\oper}[1]{\\mathcal{#1}}$\n",
31 | "$\\newcommand{\\st}[1]{\\mathcal{#1}}$\n",
32 | "$\\newcommand{\\vecs}[1]{\\mathcal{#1}}$\n",
33 | "$\\newcommand{\\group}[1]{\\mathcal{#1}}$\n",
34 | "$\\newcommand{\\union}{\\hspace{0.25em}\\cup\\hspace{0.25em}}$"
35 | ]
36 | },
37 | {
38 | "cell_type": "markdown",
39 | "id": "99956c4a-28cd-4e76-81df-97af128b1269",
40 | "metadata": {},
41 | "source": [
42 | "On computer systems, information is typically represented, stored and processed in the form of 0s and 1s. Which is why, when we were studying the repetition code, we assumed that the information Alice meant to send was in the form of a string of bits.\n",
43 | "\n",
44 | "At one point, we said that the corrupted codeword $\\vec{\\tilde{c}} = \\vec{m} + \\vec{e}$. What does it mean to add two strings of bits? In the following, we will discuss this in more detail, as a precursor to our study of both classical and quantum error-correcting codes. We will also figure out how to do this sort of mathematics in python."
45 | ]
46 | },
47 | {
48 | "cell_type": "markdown",
49 | "id": "42fdbf09-4b29-47ef-82ee-13b0daba8588",
50 | "metadata": {},
51 | "source": [
52 | "## What is the binary field?\n",
53 | "In the binary field, there are only two numbers $\\set{0,1}$. To do anything useful with them, we need to define their addition and multiplication. The rules for addition are summarized in the following table.\n",
54 | "\n",
55 | "| + | 0 | 1 |\n",
56 | "|---|---|---|\n",
57 | "| 0 | 0 | 1 |\n",
58 | "| 1 | 1 | 0 |\n",
59 | "\n",
60 | "Out of the four rules, the one in the bottom right corner is the most interesting one, $1+1 = 0$.\n",
61 | "\n",
62 | "Similarly, the rules of multiplication are as follows. They are pretty straightforward and not different from the rules we use to multiply real numbers or integers.\n",
63 | "\n",
64 | "| $\\times$ | 0 | 1 |\n",
65 | "|---|---|---|\n",
66 | "| 0 | 0 | 0 |\n",
67 | "| 1 | 0 | 1 |\n",
68 | "\n",
69 | "What is important about the two sets of rules is that they always ensure that the addition or multiplication of two numbers never yields anything besides $\\set{0,1}$.\n",
70 | "\n",
71 | "> The **binary field** is labelled $\\mathbb{F}_2$. "
72 | ]
73 | },
74 | {
75 | "cell_type": "markdown",
76 | "id": "49fcb38f-0ece-4a96-b69e-8cc7fd9f32f1",
77 | "metadata": {},
78 | "source": [
79 | "## Linear algebra over $\\mathbb{F}_2$\n",
80 | "Classical error-correcting codes are defined using vector fields over the binary field. This means that any vectors and matrices we will encounter will have their elements coming from the binary field. When we do any matrix algebra, we will have to apply the addition and multiplication rules of binary field. \n",
81 | "\n",
82 | "For example, let us take the two-dimensional vector space over the binary field. Define\n",
83 | "$$\n",
84 | "\\vec{v} = \\begin{pmatrix} 1 \\\\ 0 \\end{pmatrix}, \\quad \\vec{w} = \\begin{pmatrix} 1 \\\\ 1 \\end{pmatrix}.\n",
85 | "$$\n",
86 | "Then\n",
87 | "$$\n",
88 | "\\vec{v} + \\vec{w} = \\begin{pmatrix} 0 \\\\ 1 \\end{pmatrix}\n",
89 | "$$\n",
90 | "\n",
91 | "Now, also define\n",
92 | "$$\n",
93 | "M = \\begin{pmatrix} 1 & 0 \\\\ 1 & 1 \\end{pmatrix}.\n",
94 | "$$\n",
95 | "Then,\n",
96 | "$$\n",
97 | "M\\vec{w} = \\begin{pmatrix} 1\\times 1 + 0 \\times 1 \\\\ 1 \\times 1 + 1 \\times 1\\end{pmatrix} = \\begin{pmatrix} 1 \\\\ 0\\end{pmatrix}.\n",
98 | "$$\n",
99 | "\n",
100 | "Finally, we will use the following notation.\n",
101 | "\n",
102 | "> A $m$-dimensional vector space over the binary field is labelled as $\\mathbb{F}^m_2$."
103 | ]
104 | },
105 | {
106 | "cell_type": "markdown",
107 | "id": "65f57b56-cdfb-49dc-9cf9-3905651109a6",
108 | "metadata": {},
109 | "source": [
110 | "*Question:* Let $\\vec{m} = \\begin{pmatrix} 1 & 1 & 1 & 0 & 0 & 0 \\end{pmatrix}$ and $\\vec{e} = \\begin{pmatrix} 0 & 0 & 1 & 0 & 0 & 1 \\end{pmatrix}$. What is the corrupted codeword $\\vec{\\tilde{c}}$?"
111 | ]
112 | },
113 | {
114 | "cell_type": "markdown",
115 | "id": "2662f94f-9544-4b69-9f44-0873dc13f7d5",
116 | "metadata": {},
117 | "source": [
118 | "## Working with $\\mathbb{F}_2^m$ in python\n",
119 | "\n",
120 | "The following is some example code that will help you understand how to work with binary vectors and matrices in python. There are only two things you have to do.\n",
121 | "\n",
122 | "* Specify `dtype=int` for any numpy arrays you use.\n",
123 | "* For every operation, use modulus 2. This is done via `% 2` in python."
124 | ]
125 | },
126 | {
127 | "cell_type": "code",
128 | "execution_count": 2,
129 | "id": "0393df99-cce4-461f-aa33-36d3876528e6",
130 | "metadata": {},
131 | "outputs": [
132 | {
133 | "name": "stdout",
134 | "output_type": "stream",
135 | "text": [
136 | "(1 + 1) % 2 = 0\n",
137 | "\n",
138 | "M = \n",
139 | " [[1 0]\n",
140 | " [1 1]]\n",
141 | "\n",
142 | "M + M =\n",
143 | " [[2 0]\n",
144 | " [2 2]] incorrect! 2 is not part of our number system.\n",
145 | "\n",
146 | "(M + M) % 2 =\n",
147 | " [[0 0]\n",
148 | " [0 0]] correct\n",
149 | "\n",
150 | "v = [1 1]\n",
151 | "M@v = [1 2] incorrect\n",
152 | "M@v % 2 = [1 0] correct\n"
153 | ]
154 | }
155 | ],
156 | "source": [
157 | "import numpy as np\n",
158 | "\n",
159 | "print('(1 + 1) % 2 = ', (1 + 1) % 2)\n",
160 | "print('')\n",
161 | "\n",
162 | "# declare your vectors and matrices as numpy.array\n",
163 | "# with a data-type of int, rather than the default\n",
164 | "# type of float\n",
165 | "M = np.array([[1, 0], [1,1]], dtype=int)\n",
166 | "v = np.array([1, 1], dtype=int)\n",
167 | "print('M = \\n', M)\n",
168 | "print('')\n",
169 | "\n",
170 | "# if you add things as\n",
171 | "print('M + M =\\n', M+M, 'incorrect! 2 is not part of our number system.')\n",
172 | "# you will get the wrong answer. '2' is not part of our number system.\n",
173 | "\n",
174 | "print('')\n",
175 | "# Instead mod everything 2. This is done with '% 2' in python\n",
176 | "print('(M + M) % 2 =\\n', (M+M) % 2, 'correct')\n",
177 | "# The parenthesis in (M+M) % 2 are important\n",
178 | "\n",
179 | "print('')\n",
180 | "print('v = ', v)\n",
181 | "# Remember that in numpy matrix multiplication is done using the @ symbol\n",
182 | "print('M@v = ', M@v, 'incorrect')\n",
183 | "print('M@v % 2 = ', M@v % 2, 'correct')"
184 | ]
185 | },
186 | {
187 | "cell_type": "code",
188 | "execution_count": null,
189 | "id": "55225fec",
190 | "metadata": {},
191 | "outputs": [],
192 | "source": []
193 | }
194 | ],
195 | "metadata": {
196 | "kernelspec": {
197 | "display_name": "Python 3 (ipykernel)",
198 | "language": "python",
199 | "name": "python3"
200 | },
201 | "language_info": {
202 | "codemirror_mode": {
203 | "name": "ipython",
204 | "version": 3
205 | },
206 | "file_extension": ".py",
207 | "mimetype": "text/x-python",
208 | "name": "python",
209 | "nbconvert_exporter": "python",
210 | "pygments_lexer": "ipython3",
211 | "version": "3.10.8"
212 | },
213 | "widgets": {
214 | "application/vnd.jupyter.widget-state+json": {
215 | "state": {},
216 | "version_major": 2,
217 | "version_minor": 0
218 | }
219 | }
220 | },
221 | "nbformat": 4,
222 | "nbformat_minor": 5
223 | }
224 |
--------------------------------------------------------------------------------
/bookcreator.py:
--------------------------------------------------------------------------------
1 | #!/home/abdullah/Apps/miniconda3/bin/python
2 | """Creates a book out of a collection of jupyter notebooks."""
3 |
4 | import shutil
5 | import os
6 | import nbformat
7 | from nbconvert import HTMLExporter
8 | from nbconvert.preprocessors import TagRemovePreprocessor, RegexRemovePreprocessor
9 | from traitlets.config import Config
10 | from jinja2 import Environment, FileSystemLoader
11 |
12 |
13 | # Config variables for website
14 | SITEURL = 'https://abdullahkhalid.com/qecft'
15 | # SITEURL = 'http://localhost:8000'
16 |
17 |
18 | SITENAME = 'A Methods Focused Guide to Quantum Error Correction and Fault-Tolerant Quantum Computation'
19 | REPOSITORY = 'https://github.com/abdullahkhalids/qecft'
20 |
21 | SOURCEDIR = 'chapters'
22 | # Section titles are drawn from the first cell of the notebook
23 | # But for the first section, we add a custom caption.
24 | FIRST_SECTION_TITLE = 'About this book'
25 |
26 | BUILDDIR = 'build'
27 | STATIC_FOLDERS = ['static', 'images']
28 |
29 | jinja2_config = {
30 | 'comment_start_string': '',
32 | 'trim_blocks': True,
33 | 'lstrip_blocks': True,
34 | 'keep_trailing_newline': True,
35 | 'extensions': []
36 | }
37 |
38 | # Nbconvert config
39 | # These are the config settings for converting the contents of
40 | # the notebook to html via nbconvert
41 | c = Config()
42 | # Remove any cell marked with this tag
43 | c.TagRemovePreprocessor.enabled = True
44 | c.TagRemovePreprocessor.remove_cell_tags = ("remove-cell",)
45 | # remove any empty cells using this regexp
46 | c.RegexRemovePreprocessor.enabled = True
47 | c.RegexRemovePreprocessor.patterns = r'\s*\Z'
48 | # The template for nbconvert to html is stored in this folder
49 | c.HTMLExporter.template_name = "jupyter-html-template"
50 |
51 | html_exporter = HTMLExporter(config=c)
52 | html_exporter.register_preprocessor(TagRemovePreprocessor(config=c), True)
53 | html_exporter.register_preprocessor(RegexRemovePreprocessor(config=c), True)
54 | html_exporter.anchor_link_text = '#'
55 |
56 | # Jinja config
57 | # The jinja template is used to render the converted notebook html
58 | # into a complete html page.
59 | # The template is stored in the following folder and file
60 | file_loader = FileSystemLoader('website-jinja-template')
61 | env = Environment(loader=file_loader, **jinja2_config)
62 | template = env.get_template('page.html')
63 |
64 |
65 | # Various classes to make our life easier
66 | class Section:
67 | def __init__(self, ch_dir, sec_filebasename, ch_index, sec_index):
68 | self.source_dir = os.path.join(SOURCEDIR, ch_dir)
69 | self.source = os.path.join(self.source_dir,
70 | sec_filebasename + '.ipynb')
71 |
72 | self.target_dir = os.path.join(BUILDDIR, ch_dir, sec_filebasename)
73 | self.target_file = os.path.join(self.target_dir, 'index.html')
74 | self.index = sec_index
75 | self.ch_index = ch_index
76 |
77 | self.url = os.path.join(ch_dir,
78 | sec_filebasename)
79 |
80 | notebook_node = nbformat.read(self.source, 4)
81 |
82 | # This is one place where the heavy lifting is done
83 | # this converts notebook contents to html
84 | rawhtml = html_exporter.from_notebook_node(notebook_node)[0]
85 |
86 | # the title is from the first cell
87 | title = notebook_node.cells[0].source[2:]
88 |
89 | self.caption = title
90 | self.title = title
91 | self.content = rawhtml
92 |
93 | self.prevpage = None
94 | self.nextpage = None
95 |
96 | def __repr__(self):
97 | return self.source
98 |
99 | def __eq__(self, other):
100 | return self.source == other.source
101 |
102 |
103 | class Chapter:
104 | def __init__(self, ch_dir, caption, index):
105 | self.caption = caption
106 | self.source_dir = os.path.join(SOURCEDIR, ch_dir)
107 | self.target_dir = os.path.join(BUILDDIR, ch_dir)
108 | self.index = index
109 | self.sections = []
110 |
111 | def __iter__(self):
112 | return self.sections.__iter__()
113 |
114 | def __len__(self):
115 | return self.sections.__len__()
116 |
117 |
118 | class Toc:
119 | def __init__(self):
120 | self.chapters = []
121 | self.sections = []
122 |
123 | def __iter__(self):
124 | return self.chapters.__iter__()
125 |
126 | def add_section(self, section, ch_index=-1):
127 | """add section"""
128 | self.chapters[ch_index].sections.append(section)
129 | self.sections.append(section)
130 |
131 |
132 | # The jinja template system needs a dictionary of variables
133 | # to render the contents properly
134 | j2_vars = {
135 | 'SITENAME': SITENAME,
136 | 'SITEURL': SITEURL,
137 | 'REPOSITORY': REPOSITORY,
138 | 'page': '',
139 | 'toc': ''
140 | }
141 |
142 |
143 | def convert_section_to_html(section):
144 | """Convert ipynb to html."""
145 | j2_vars['page'] = section
146 | j2_vars['toc'] = toc_structure
147 |
148 | os.makedirs(section.target_dir)
149 | with open(section.target_file, 'w', encoding='utf-8') as f:
150 | # convert the section into html and write to file
151 | html_page = template.render(**j2_vars)
152 | f.write(html_page)
153 |
154 |
155 | def process_static_resources():
156 | """Copy static resources into the build folder."""
157 | for name in STATIC_FOLDERS:
158 | shutil.copytree(name,
159 | os.path.join(BUILDDIR, name))
160 |
161 |
162 | def create_toc_structure():
163 | """Determine the structure of the book."""
164 |
165 | toc_structure = Toc()
166 | # read the order file to determine the order of the chapters
167 | chapter_order_file = os.path.join(SOURCEDIR, 'order')
168 | with open(chapter_order_file, 'r') as chaptersfile:
169 | chapters = chaptersfile.read().splitlines()
170 |
171 | # Now iterate over the chapters
172 | for i, ch_dir in enumerate(chapters, start=1):
173 | # read the caption of the chapter
174 | caption_file_path = os.path.join(SOURCEDIR, ch_dir, 'caption')
175 | with open(caption_file_path, 'r') as captionfile:
176 | ch_caption = captionfile.read().strip()
177 |
178 | # Now we iterate over the sections
179 | order_file_path = os.path.join(SOURCEDIR, ch_dir, 'order')
180 | with open(order_file_path, 'r') as orderfile:
181 | sections = orderfile.read().splitlines()
182 |
183 | # if empty chapter, don't add to toc
184 | if len(sections) == 0:
185 | continue
186 |
187 | # create and add chapter to toc
188 | chapter = Chapter(ch_dir, ch_caption, i)
189 | toc_structure.chapters.append(chapter)
190 |
191 | # Iterate over sections in chapter
192 | for j, sec in enumerate(sections, start=1):
193 | # create and add chapter to toc
194 | section = Section(ch_dir, sec, i, j)
195 | toc_structure.add_section(section)
196 |
197 | # if there are at least two sections added, then we can
198 | # assign prevpage entries and next page entries
199 | if len(toc_structure.sections) >= 2:
200 | # for the latest added section, previous page should be
201 | # second latest section.
202 | toc_structure.sections[-1].prevpage = \
203 | toc_structure.sections[-2]
204 |
205 | # And for second latest section, the next page should
206 | # be this section
207 | toc_structure.sections[-2].nextpage = \
208 | toc_structure.sections[-1]
209 |
210 | # Fix the caption for the first section
211 | toc_structure.chapters[0].sections[0].title = FIRST_SECTION_TITLE
212 | toc_structure.chapters[0].sections[0].caption = FIRST_SECTION_TITLE
213 |
214 | return toc_structure
215 |
216 |
217 | if __name__ == "__main__":
218 |
219 | print("Converting to HTML now...")
220 | print("Deleting existing build directory")
221 | shutil.rmtree('build', ignore_errors=True)
222 |
223 | print(f"Copying folders: {STATIC_FOLDERS}")
224 | process_static_resources()
225 |
226 | print("Creating toc structure from order and caption files")
227 | toc_structure = create_toc_structure()
228 |
229 | # process each chapter and section
230 | print("Converting each section to a html page\n")
231 | for ch in toc_structure:
232 | for sec in ch:
233 | print(f'{sec.ch_index}.{sec.index} {sec.caption}')
234 | convert_section_to_html(sec)
235 | print("")
236 |
237 | print("Setting first section as index.html")
238 | home_page_path = os.path.join(BUILDDIR, 'index.html')
239 | shutil.copy(
240 | toc_structure.chapters[0].sections[0].target_file, home_page_path)
241 | print("html build complete\n")
242 |
--------------------------------------------------------------------------------
/chapters/stabilizer-codes/stabilizer-states.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "a23fe73c",
6 | "metadata": {
7 | "tags": []
8 | },
9 | "source": [
10 | "# Stabilizer states"
11 | ]
12 | },
13 | {
14 | "cell_type": "markdown",
15 | "id": "b00286a1",
16 | "metadata": {
17 | "tags": [
18 | "remove-cell"
19 | ]
20 | },
21 | "source": [
22 | "
Latex macros
\n",
23 | "$ \\newcommand{\\ket}[1]{|#1\\rangle} $\n",
24 | "$ \\newcommand{\\bra}[1]{\\langle #1|} $\n",
25 | "$\\newcommand{\\iu}{{i\\mkern1mu}}$\n",
26 | "$ \\newcommand{\\e}{\\mathrm{e}}$\n",
27 | "$\\newcommand{\\bigo}{\\mathcal{O}}$\n",
28 | "$\\newcommand{\\set}[1]{\\{#1\\}}$\n",
29 | "$\\newcommand{\\oper}[1]{\\mathcal{#1}}$\n",
30 | "$\\newcommand{\\st}[1]{\\mathcal{#1}}$\n",
31 | "$\\newcommand{\\vecs}[1]{\\mathcal{#1}}$\n",
32 | "$\\newcommand{\\group}[1]{\\mathcal{#1}}$\n",
33 | "$\\newcommand{\\union}{\\hspace{0.25em}\\cup\\hspace{0.25em}}$"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "id": "6d8c849d-7efb-4684-9752-77246c778d4a",
39 | "metadata": {},
40 | "source": [
41 | "The elements of the Pauli group have another important property that relates to quantum states, which we will study now. In the next section, we will use the notions introduced here to construct stabilizer groups.\n",
42 | "\n",
43 | "We will need to recall the eigenvectors and eigenvalues of $X, Y, Z$ are\n",
44 | "\\begin{equation*}\n",
45 | "\\begin{aligned}[c]\n",
46 | "X\\ket{+} &= \\ket{+},\\\\\n",
47 | "Y\\ket{+\\iu} &= \\ket{+\\iu}\\\\\n",
48 | "Z\\ket{0} &= \\ket{0},\n",
49 | "\\end{aligned}\n",
50 | "\\qquad\\qquad\\qquad\n",
51 | "\\begin{aligned}[c]\n",
52 | "X\\ket{-} &= -\\ket{-},\\\\\n",
53 | "Y\\ket{-\\iu} &= -\\ket{-\\iu},\\\\\n",
54 | "Z\\ket{1} &= -\\ket{1}.\n",
55 | "\\end{aligned}\n",
56 | "\\end{equation*}"
57 | ]
58 | },
59 | {
60 | "cell_type": "markdown",
61 | "id": "95cf86d6-9870-4e43-a854-e9e872b735ad",
62 | "metadata": {},
63 | "source": [
64 | "## Stabilizer states\n",
65 | "In the equations above, the ones on the left are +1 eigenstates. This means acting on the state by the corresponding operator leaves the state unchanged (even accounting for a phase). We are going to give a special name to such states.\n",
66 | "\n",
67 | "> Given $P \\in \\group{P}_n$, a state $\\ket{\\psi}$ such that $P\\ket{\\psi} = \\ket{\\psi}$, is called a **stabilizer state** of $P$.\n",
68 | "\n",
69 | "Let's look at some examples for $n>1$. \n",
70 | "\n",
71 | "**Example:** Consider, the Pauli operator $X \\otimes Z$. From the equations above, it is quite easy to see that\n",
72 | "\\begin{equation}\n",
73 | " (X\\otimes Z)\\ket{+}\\otimes \\ket{0} = \\ket{+} \\otimes \\ket{0}. \n",
74 | "\\end{equation}\n",
75 | "The tensor product of the $+1$ eigenvectors of the individual operators gives a $+1$ eigenvector of the tensor-product operator. But, wait, there is more. \n",
76 | "\\begin{equation}\n",
77 | " (X\\otimes Z)\\ket{-}\\otimes \\ket{1} = (-1)^2\\ket{-} \\otimes \\ket{1} = \\ket{-} \\otimes \\ket{1}. \n",
78 | "\\end{equation}\n",
79 | "In this way, we can see that the operator $X \\otimes Z$ has two $+1$ eigenvectors (it also has two $-1$ eigenvectors; why?).\n",
80 | "\n",
81 | "Of course, any state that is a linear combination of $\\ket{+} \\otimes \\ket{0}$ and $\\ket{-} \\otimes \\ket{1}$ is also a $+1$ eigenstate of $X \\otimes Z$,\n",
82 | "\\begin{align}\n",
83 | " (X\\otimes Z)(\\alpha\\ket{+}\\otimes \\ket{0} + \\beta \\ket{-}\\otimes \\ket{1}) &= \\alpha (X\\otimes Z) \\ket{+}\\otimes \\ket{0} + \\beta (X\\otimes Z) \\ket{-}\\otimes \\ket{1}, \\\\\n",
84 | " &= \\alpha\\ket{+}\\otimes \\ket{0} + \\beta \\ket{-}\\otimes \\ket{1}.\n",
85 | "\\end{align}\n",
86 | "\n",
87 | "Similarly, for any Pauli operator $P$ in $\\group{P}_n$, we can construct states that are the $+1$ eigenvectors of the operator. \n",
88 | "\n",
89 | "**Example:** Here is another example,\n",
90 | "\\begin{align}\n",
91 | " (X \\otimes I \\otimes X)\\ket{+}\\otimes \\ket{0} \\otimes \\ket{+} = \\ket{+} \\otimes \\ket{0} \\otimes \\ket{+}, \\\\\n",
92 | " (X \\otimes I \\otimes X)\\ket{-}\\otimes \\ket{0} \\otimes \\ket{-} = \\ket{-} \\otimes \\ket{0} \\otimes \\ket{-}.\n",
93 | "\\end{align}\n",
94 | "$I$ is special because every state is a $+1$ eigenvector of $I$. So, we could also have written any state in middle, such as\n",
95 | "\\begin{align}\n",
96 | " (X \\otimes I \\otimes X)\\ket{+}\\otimes \\ket{-} \\otimes \\ket{+} = \\ket{+} \\otimes \\ket{-} \\otimes \\ket{+}, \\\\\n",
97 | " (X \\otimes I \\otimes X)\\ket{-}\\otimes \\ket{-} \\otimes \\ket{-} = \\ket{-} \\otimes \\ket{-} \\otimes \\ket{-}.\n",
98 | "\\end{align}\n",
99 | "etc. From this we infer that $X \\otimes I \\otimes X$ stabilizes all states that are a linear combination of \n",
100 | "\\begin{equation}\n",
101 | "\\set{\\ket{+} \\otimes \\ket{0} \\otimes \\ket{+}, \\ket{-} \\otimes \\ket{0} \\otimes \\ket{-}, \\ket{+} \\otimes \\ket{1} \\otimes \\ket{+}, \\ket{-} \\otimes \\ket{1} \\otimes \\ket{-}}.\n",
102 | "\\end{equation}\n"
103 | ]
104 | },
105 | {
106 | "cell_type": "markdown",
107 | "id": "2db65f34-20fb-4326-b32f-6be6e4b9699c",
108 | "metadata": {},
109 | "source": [
110 | "#### Task 1 (On paper)\n",
111 | "Determine the basis of the stabilizer states of the following operators.\n",
112 | "\n",
113 | "* $X \\otimes X \\otimes X$\n",
114 | "* $Z \\otimes Z \\otimes Z$\n",
115 | "* $Z \\otimes I \\otimes X$\n",
116 | "* $Z \\otimes X \\otimes X \\otimes Z$\n",
117 | "\n",
118 | "Do you see a pattern in the number of states and the size of the operator?"
119 | ]
120 | },
121 | {
122 | "cell_type": "markdown",
123 | "id": "3126c037-bf33-4264-8558-477217b2b08b",
124 | "metadata": {},
125 | "source": [
126 | "## Stabilizer states of multiple operators\n",
127 | "We will now show that it is possible to construct states that are the simultaneously stabilized by multiple Pauli operators. Let's start with some examples.\n",
128 | "\n",
129 | "**Example:** Consider, the operators $P_1 = Z \\otimes Z \\otimes I$ and $P_2= Z \\otimes I \\otimes Z$, both in $\\group{P}_3$.\n",
130 | "\n",
131 | "* $Z \\otimes Z \\otimes I$ stabilizes four orthogonal states $\\set{\\ket{000}, \\ket{001}, \\ket{110}, \\ket{111}}$.\n",
132 | "* $Z \\otimes I \\otimes Z$ stabilizes four orthogonal states $\\set{\\ket{000}, \\ket{010}, \\ket{101}, \\ket{111}}$.\n",
133 | "\n",
134 | "The intersection of these sets is $\\set{\\ket{000}, \\ket{111}}$. This means that any state that is a linear combination of these two states will be stabilized by both $P_1$ and $P_2$.\n",
135 | "\n",
136 | "Remember, how $\\set{\\ket{000}, \\ket{111}}$ were the basis states of the quantum repetition code for bit-flip codes. It turns out that code is a stabilizer code. We will soon show this, if you have some patience."
137 | ]
138 | },
139 | {
140 | "cell_type": "markdown",
141 | "id": "5f25ded8-ab81-497b-b90b-e8a481c74442",
142 | "metadata": {},
143 | "source": [
144 | "#### Task 2 (On paper)\n",
145 | "Determine states that are simultaneously stabilized by $P_1 = X \\otimes X \\otimes I = X_0X_1$ and $P_2 = X \\otimes I \\otimes X = X_0X_2$. (The subscript based notation will ensure you don't injure your wrist!)\n",
146 | "\n",
147 | "Have you seen them before?"
148 | ]
149 | },
150 | {
151 | "cell_type": "markdown",
152 | "id": "0964188a-8976-4a8d-8a4a-80304ea38727",
153 | "metadata": {},
154 | "source": [
155 | "#### Task 3 (On paper)\n",
156 | "It is not necessary that two operators have simultaneously stabilized states. \n",
157 | "\n",
158 | "* Do $P_1 = Z_0Z_1$ and $P_2=X_0X_1$ have any simultaneously stabilized states?\n",
159 | "* What about $P_1=Z_0Z_1$ and $P_2=X_0X_2$?"
160 | ]
161 | },
162 | {
163 | "cell_type": "code",
164 | "execution_count": null,
165 | "id": "a238a409-1868-4671-a311-10ee20e3924e",
166 | "metadata": {},
167 | "outputs": [],
168 | "source": []
169 | }
170 | ],
171 | "metadata": {
172 | "kernelspec": {
173 | "display_name": "Python 3 (ipykernel)",
174 | "language": "python",
175 | "name": "python3"
176 | },
177 | "language_info": {
178 | "codemirror_mode": {
179 | "name": "ipython",
180 | "version": 3
181 | },
182 | "file_extension": ".py",
183 | "mimetype": "text/x-python",
184 | "name": "python",
185 | "nbconvert_exporter": "python",
186 | "pygments_lexer": "ipython3",
187 | "version": "3.10.8"
188 | },
189 | "widgets": {
190 | "application/vnd.jupyter.widget-state+json": {
191 | "state": {},
192 | "version_major": 2,
193 | "version_minor": 0
194 | }
195 | }
196 | },
197 | "nbformat": 4,
198 | "nbformat_minor": 5
199 | }
200 |
--------------------------------------------------------------------------------
/chapters/stabilizer-codes/vector-form-of-pauli-operators-and-generator-matrix.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "a23fe73c",
6 | "metadata": {
7 | "tags": []
8 | },
9 | "source": [
10 | "# Vector form of Pauli operators and the generator matrix"
11 | ]
12 | },
13 | {
14 | "cell_type": "markdown",
15 | "id": "b00286a1",
16 | "metadata": {
17 | "tags": [
18 | "remove-cell"
19 | ]
20 | },
21 | "source": [
22 | "
Latex macros
\n",
23 | "$ \\newcommand{\\ket}[1]{|#1\\rangle} $\n",
24 | "$ \\newcommand{\\bra}[1]{\\langle #1|} $\n",
25 | "$\\newcommand{\\iu}{{i\\mkern1mu}}$\n",
26 | "$ \\newcommand{\\e}{\\mathrm{e}}$\n",
27 | "$\\newcommand{\\bigo}{\\mathcal{O}}$\n",
28 | "$\\newcommand{\\set}[1]{\\{#1\\}}$\n",
29 | "$\\newcommand{\\oper}[1]{\\mathcal{#1}}$\n",
30 | "$\\newcommand{\\st}[1]{\\mathcal{#1}}$\n",
31 | "$\\newcommand{\\vecs}[1]{\\mathcal{#1}}$\n",
32 | "$\\newcommand{\\group}[1]{\\mathcal{#1}}$\n",
33 | "$\\newcommand{\\union}{\\hspace{0.25em}\\cup\\hspace{0.25em}}$"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "id": "7baf4865-089d-4172-a2da-d1095bc28921",
39 | "metadata": {},
40 | "source": [
41 | "It is getting quite tedious to write down multi-qubit Pauli operators like $X \\otimes X \\otimes I$. Here we are going to discuss a compact form for Pauli operators, that will make it very easy to do computations."
42 | ]
43 | },
44 | {
45 | "cell_type": "markdown",
46 | "id": "d708834d-626b-4ea7-976f-2c5f5d8cdbcd",
47 | "metadata": {},
48 | "source": [
49 | "## Vector form of Pauli operators\n",
50 | "We are going to map operators in the Pauli group $\\group{P}_n$ to binary vectors of length $2n$. \n",
51 | "\n",
52 | "The elements of $\\group{P}_1$ are\n",
53 | "\\begin{align}\n",
54 | "I \\equiv (0|0), \\\\\n",
55 | "X \\equiv (1|0), \\\\\n",
56 | "Y \\equiv (1|1), \\\\\n",
57 | "Z \\equiv (0|1).\n",
58 | "\\end{align}\n",
59 | "\n",
60 | "For larger $n$ we have a similar formula of operators getting mapped to the vector $v = (a|b)$ where \n",
61 | "\n",
62 | "* $a$ is the \"$X$ part\", and is of length $n$, and \n",
63 | "* $b$ is the \"$Z$ part\", and is also of length $n$. \n",
64 | "\n",
65 | "Let $P = \\otimes_i P_i$, where $P_i \\in \\{I_i, X_i, Y_i, Z_i\\}$. Then,\n",
66 | "\\begin{align}\n",
67 | "a_i &= 0, b_i = 0 \\text{ if } P_i = I_i, \\\\\n",
68 | "a_i &= 1, b_i = 0 \\text{ if } P_i = X_i, \\\\\n",
69 | "a_i &= 1, b_i = 1 \\text{ if } P_i = Y_i, \\\\\n",
70 | "a_i &= 0, b_i = 1 \\text{ if } P_i = Z_i.\n",
71 | "\\end{align}\n",
72 | "For example if $n=4$, then $X_0I_1Z_2Y_3$ is associated with the vector $v=(1 0 0 1|0 0 1 1)$.\n",
73 | "\n",
74 | "Important to note in this formulation is that we will completely ignore the phase of the operators. Meaning if $P \\equiv v$, then so are $-P \\equiv v$ and $\\pm \\iu P \\equiv v$. "
75 | ]
76 | },
77 | {
78 | "cell_type": "markdown",
79 | "id": "322997a5-2bd2-440a-ab9b-a0590789ab80",
80 | "metadata": {},
81 | "source": [
82 | "#### Task 1 (On paper)\n",
83 | "Determine the vector form of the following operators\n",
84 | "\n",
85 | "* $X_0Z_0X_1Z_1$\n",
86 | "* $Y_0Y_1Y_2$\n",
87 | "* $Z_0Z_1X_2$"
88 | ]
89 | },
90 | {
91 | "cell_type": "markdown",
92 | "id": "3b5ad380-250e-412d-a587-a8f3cf7edbeb",
93 | "metadata": {},
94 | "source": [
95 | "### Multiplying operators\n",
96 | "One of the immediate benefits of the binary vector form is that multiplying two Paulis corresponds to adding their vectors mod 2 (up to an overall phase).\n",
97 | "\n",
98 | "For the simplest example, note that $X = (1|0)$ and $Z = (0|1)$ and their product is $XZ = (1|0) + (0|1) = (1|1) = Y$. Quite ingenius! Please note that we will ignore the overall phase here as $XZ = -\\iu Y$, but as you delve more into error-correcting codes you will realize that such phases are not very important.\n",
99 | "\n",
100 | "*Question:* Use the binary vector form to compute $XX$."
101 | ]
102 | },
103 | {
104 | "cell_type": "markdown",
105 | "id": "17f3e9c9-e79f-4dc9-ba6a-b5c94d70f449",
106 | "metadata": {},
107 | "source": [
108 | "### Commutation relations\n",
109 | "One of the best advantages of writing Pauli operators as binary vectors is that commutation relations become very easy to compute.\n",
110 | "\n",
111 | "First, we define the *symplectic inner product* between binary vectors of length $2n$\n",
112 | "\n",
113 | "> Let $P_1 = (a|b)$ and $P_2 = (c|d)$. Then, the **symplectic inner product** between $P_1$ and $P_2$ is \n",
114 | " \\begin{equation}\n",
115 | " P_1 \\cdot P_2 = a\\cdot d + b\\cdot c\\mod 2,\n",
116 | " \\end{equation}\n",
117 | " where on the right hand side, the $\\cdot$ is the regular dot product between vectors. Be very careful about how we are multiplying the $X$-part of $P_1$ with the $Z$-part of $P_2$ and vice versa.\n",
118 | " \n",
119 | "**Example:** The symplectic inner product of $(10|01)$ and $(01|00)$ is\n",
120 | "\\begin{equation}\n",
121 | "(10|01) \\cdot (01|00) = (10)\\cdot (00) + (01)\\cdot (01) = 0 + 1 = 1.\n",
122 | "\\end{equation}"
123 | ]
124 | },
125 | {
126 | "cell_type": "markdown",
127 | "id": "212f645c-ac38-4ae8-9db2-e4bce45f6c53",
128 | "metadata": {},
129 | "source": [
130 | "#### Task 2 (On paper)\n",
131 | "Let $P_1 = (101|110)$ and $P_2 = (011|111)$. Determine $P_1\\cdot P_2$."
132 | ]
133 | },
134 | {
135 | "cell_type": "markdown",
136 | "id": "ab0c2eec-358c-41e5-b09e-28e1d9977cb4",
137 | "metadata": {},
138 | "source": [
139 | "#### Task 3\n",
140 | "Complete the function `symplectic_inner_product` that computes the symplectic product of two input vectors.\n",
141 | "* Parameters:\\\n",
142 | " `P1`: a `list`, guaranteed to only contain 0 or 1, and length divisible by 2.\\\n",
143 | " `P2`: a `list`, guaranteed to only contain 0 or 1, and length divisible by 2.\n",
144 | "* Returns:\\\n",
145 | " An `int` that is symplectic inner product of `P1` and `P2`"
146 | ]
147 | },
148 | {
149 | "cell_type": "code",
150 | "execution_count": null,
151 | "id": "a423a183-426d-481a-b0ac-38f975e2df15",
152 | "metadata": {},
153 | "outputs": [],
154 | "source": [
155 | "def symplectic_inner_product(P1, P2):\n",
156 | " pass"
157 | ]
158 | },
159 | {
160 | "cell_type": "markdown",
161 | "id": "bb05d3fc-ac99-4711-a47d-3634f88bce79",
162 | "metadata": {},
163 | "source": [
164 | "Now we state the following theorem about the commutation relations of Pauli operators.\n",
165 | "\n",
166 | "**Lemma:** Let $P_1 = (a|b)$ and $P_2 = (c|d)$ be two Pauli operators. Then, \n",
167 | "* $P_1$ and $P_2$ commute if and only if $P_1 \\cdot P_2 = 0$,\n",
168 | "* $P_1$ and $P_2$ anti-commute if and only if $P_1 \\cdot P_2 = 1$.\n",
169 | "\n",
170 | "*Question:* Use the commutation relations of $I_i,X_i,Y_i,Z_i$ to prove this theorem."
171 | ]
172 | },
173 | {
174 | "cell_type": "markdown",
175 | "id": "5c0a3f30-b9fb-42fb-9a5e-ec0b1adb27b4",
176 | "metadata": {},
177 | "source": [
178 | "#### Task 4\n",
179 | "In Task 1 in [stabilizer codes](../stabilizer-codes/stabilizer-codes.ipynb) we computed everything by hand. Now use the `symplectic_inner_product` function to recompute the table with code."
180 | ]
181 | },
182 | {
183 | "cell_type": "code",
184 | "execution_count": null,
185 | "id": "3855da52-c9be-4428-b057-9093b705f290",
186 | "metadata": {},
187 | "outputs": [],
188 | "source": []
189 | },
190 | {
191 | "cell_type": "markdown",
192 | "id": "42bbed3e-650b-47a3-8e63-b6c956aca3b7",
193 | "metadata": {},
194 | "source": [
195 | "## Generator matrix\n",
196 | "To compactly represent the stabilizer group, and its associated code, we usually write the generators of the stabilizer code as rows of a matrix. This matrix is called sometimes called the generator matrix (because it contains the generators of the code) or the check-matrix (because it is actually analogous to the classical parity-check matrix rather than the classical generator matrix). This is unfortunate and confusing terminology.\n",
197 | "\n",
198 | "Example: For the repetition code for phase flips, the generators are\n",
199 | "\\begin{align}\n",
200 | " X \\otimes X \\otimes I &= (110|000), \\\\\n",
201 | " I \\otimes X \\otimes X &= (011|000).\n",
202 | "\\end{align}\n",
203 | "Hence, the generator matrix is\n",
204 | "\\begin{equation}\n",
205 | " G = \\left(\\begin{array}{ccc|ccc}\n",
206 | " 1 & 1 & 0 & 0 & 0 & 0 \\\\\n",
207 | " 0 & 1 & 1 & 0 & 0 & 0\n",
208 | " \\end{array}\\right).\n",
209 | "\\end{equation}\n"
210 | ]
211 | },
212 | {
213 | "cell_type": "markdown",
214 | "id": "e7d654cf-4472-451b-b170-934b7b2e9f75",
215 | "metadata": {},
216 | "source": [
217 | "#### Task 5 (On paper)\n",
218 | "Write down the generator matrix for the repetition code for bit-flips."
219 | ]
220 | },
221 | {
222 | "cell_type": "code",
223 | "execution_count": null,
224 | "id": "5ae0acae-ecbd-451f-a3e2-fda1a6f652c0",
225 | "metadata": {},
226 | "outputs": [],
227 | "source": []
228 | }
229 | ],
230 | "metadata": {
231 | "kernelspec": {
232 | "display_name": "Python 3 (ipykernel)",
233 | "language": "python",
234 | "name": "python3"
235 | },
236 | "language_info": {
237 | "codemirror_mode": {
238 | "name": "ipython",
239 | "version": 3
240 | },
241 | "file_extension": ".py",
242 | "mimetype": "text/x-python",
243 | "name": "python",
244 | "nbconvert_exporter": "python",
245 | "pygments_lexer": "ipython3",
246 | "version": "3.10.8"
247 | },
248 | "widgets": {
249 | "application/vnd.jupyter.widget-state+json": {
250 | "state": {},
251 | "version_major": 2,
252 | "version_minor": 0
253 | }
254 | }
255 | },
256 | "nbformat": 4,
257 | "nbformat_minor": 5
258 | }
259 |
--------------------------------------------------------------------------------
/static/css/codemirror.css:
--------------------------------------------------------------------------------
1 | /* BASICS */
2 |
3 | .CodeMirror {
4 | /* Set height, width, borders, and global font properties here */
5 | font-family: monospace;
6 | height: 300px;
7 | color: black;
8 | direction: ltr;
9 | }
10 |
11 | /* PADDING */
12 |
13 | .CodeMirror-lines {
14 | padding: 4px 0; /* Vertical padding around content */
15 | }
16 | .CodeMirror pre.CodeMirror-line,
17 | .CodeMirror pre.CodeMirror-line-like {
18 | padding: 0 4px; /* Horizontal padding of content */
19 | }
20 |
21 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
22 | background-color: white; /* The little square between H and V scrollbars */
23 | }
24 |
25 | /* GUTTER */
26 |
27 | .CodeMirror-gutters {
28 | border-right: 1px solid #ddd;
29 | background-color: #f7f7f7;
30 | white-space: nowrap;
31 | }
32 | .CodeMirror-linenumbers {}
33 | .CodeMirror-linenumber {
34 | padding: 0 3px 0 5px;
35 | min-width: 20px;
36 | text-align: right;
37 | color: #999;
38 | white-space: nowrap;
39 | }
40 |
41 | .CodeMirror-guttermarker { color: black; }
42 | .CodeMirror-guttermarker-subtle { color: #999; }
43 |
44 | /* CURSOR */
45 |
46 | .CodeMirror-cursor {
47 | border-left: 1px solid black;
48 | border-right: none;
49 | width: 0;
50 | }
51 | /* Shown when moving in bi-directional text */
52 | .CodeMirror div.CodeMirror-secondarycursor {
53 | border-left: 1px solid silver;
54 | }
55 | .cm-fat-cursor .CodeMirror-cursor {
56 | width: auto;
57 | border: 0 !important;
58 | background: #7e7;
59 | }
60 | .cm-fat-cursor div.CodeMirror-cursors {
61 | z-index: 1;
62 | }
63 | .cm-fat-cursor-mark {
64 | background-color: rgba(20, 255, 20, 0.5);
65 | -webkit-animation: blink 1.06s steps(1) infinite;
66 | -moz-animation: blink 1.06s steps(1) infinite;
67 | animation: blink 1.06s steps(1) infinite;
68 | }
69 | .cm-animate-fat-cursor {
70 | width: auto;
71 | border: 0;
72 | -webkit-animation: blink 1.06s steps(1) infinite;
73 | -moz-animation: blink 1.06s steps(1) infinite;
74 | animation: blink 1.06s steps(1) infinite;
75 | background-color: #7e7;
76 | }
77 | @-moz-keyframes blink {
78 | 0% {}
79 | 50% { background-color: transparent; }
80 | 100% {}
81 | }
82 | @-webkit-keyframes blink {
83 | 0% {}
84 | 50% { background-color: transparent; }
85 | 100% {}
86 | }
87 | @keyframes blink {
88 | 0% {}
89 | 50% { background-color: transparent; }
90 | 100% {}
91 | }
92 |
93 | /* Can style cursor different in overwrite (non-insert) mode */
94 | .CodeMirror-overwrite .CodeMirror-cursor {}
95 |
96 | .cm-tab { display: inline-block; text-decoration: inherit; }
97 |
98 | .CodeMirror-rulers {
99 | position: absolute;
100 | left: 0; right: 0; top: -50px; bottom: 0;
101 | overflow: hidden;
102 | }
103 | .CodeMirror-ruler {
104 | border-left: 1px solid #ccc;
105 | top: 0; bottom: 0;
106 | position: absolute;
107 | }
108 |
109 | /* DEFAULT THEME */
110 |
111 | .cm-s-default .cm-header {color: blue;}
112 | .cm-s-default .cm-quote {color: #090;}
113 | .cm-negative {color: #d44;}
114 | .cm-positive {color: #292;}
115 | .cm-header, .cm-strong {font-weight: bold;}
116 | .cm-em {font-style: italic;}
117 | .cm-link {text-decoration: underline;}
118 | .cm-strikethrough {text-decoration: line-through;}
119 |
120 | .cm-s-default .cm-keyword {color: #708;}
121 | .cm-s-default .cm-atom {color: #219;}
122 | .cm-s-default .cm-number {color: #164;}
123 | .cm-s-default .cm-def {color: #00f;}
124 | .cm-s-default .cm-variable,
125 | .cm-s-default .cm-punctuation,
126 | .cm-s-default .cm-property,
127 | .cm-s-default .cm-operator {}
128 | .cm-s-default .cm-variable-2 {color: #05a;}
129 | .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
130 | .cm-s-default .cm-comment {color: #a50;}
131 | .cm-s-default .cm-string {color: #a11;}
132 | .cm-s-default .cm-string-2 {color: #f50;}
133 | .cm-s-default .cm-meta {color: #555;}
134 | .cm-s-default .cm-qualifier {color: #555;}
135 | .cm-s-default .cm-builtin {color: #30a;}
136 | .cm-s-default .cm-bracket {color: #997;}
137 | .cm-s-default .cm-tag {color: #170;}
138 | .cm-s-default .cm-attribute {color: #00c;}
139 | .cm-s-default .cm-hr {color: #999;}
140 | .cm-s-default .cm-link {color: #00c;}
141 |
142 | .cm-s-default .cm-error {color: #f00;}
143 | .cm-invalidchar {color: #f00;}
144 |
145 | .CodeMirror-composing { border-bottom: 2px solid; }
146 |
147 | /* Default styles for common addons */
148 |
149 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
150 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
151 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
152 | .CodeMirror-activeline-background {background: #e8f2ff;}
153 |
154 | /* STOP */
155 |
156 | /* The rest of this file contains styles related to the mechanics of
157 | the editor. You probably shouldn't touch them. */
158 |
159 | .CodeMirror {
160 | position: relative;
161 | overflow: hidden;
162 | background: white;
163 | }
164 |
165 | .CodeMirror-scroll {
166 | overflow: scroll !important; /* Things will break if this is overridden */
167 | /* 50px is the magic margin used to hide the element's real scrollbars */
168 | /* See overflow: hidden in .CodeMirror */
169 | margin-bottom: -50px; margin-right: -50px;
170 | padding-bottom: 50px;
171 | height: 100%;
172 | outline: none; /* Prevent dragging from highlighting the element */
173 | position: relative;
174 | }
175 | .CodeMirror-sizer {
176 | position: relative;
177 | border-right: 50px solid transparent;
178 | }
179 |
180 | /* The fake, visible scrollbars. Used to force redraw during scrolling
181 | before actual scrolling happens, thus preventing shaking and
182 | flickering artifacts. */
183 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
184 | position: absolute;
185 | z-index: 6;
186 | display: none;
187 | outline: none;
188 | }
189 | .CodeMirror-vscrollbar {
190 | right: 0; top: 0;
191 | overflow-x: hidden;
192 | overflow-y: scroll;
193 | }
194 | .CodeMirror-hscrollbar {
195 | bottom: 0; left: 0;
196 | overflow-y: hidden;
197 | overflow-x: scroll;
198 | }
199 | .CodeMirror-scrollbar-filler {
200 | right: 0; bottom: 0;
201 | }
202 | .CodeMirror-gutter-filler {
203 | left: 0; bottom: 0;
204 | }
205 |
206 | .CodeMirror-gutters {
207 | position: absolute; left: 0; top: 0;
208 | min-height: 100%;
209 | z-index: 3;
210 | }
211 | .CodeMirror-gutter {
212 | white-space: normal;
213 | height: 100%;
214 | display: inline-block;
215 | vertical-align: top;
216 | margin-bottom: -50px;
217 | }
218 | .CodeMirror-gutter-wrapper {
219 | position: absolute;
220 | z-index: 4;
221 | background: none !important;
222 | border: none !important;
223 | }
224 | .CodeMirror-gutter-background {
225 | position: absolute;
226 | top: 0; bottom: 0;
227 | z-index: 4;
228 | }
229 | .CodeMirror-gutter-elt {
230 | position: absolute;
231 | cursor: default;
232 | z-index: 4;
233 | }
234 | .CodeMirror-gutter-wrapper ::selection { background-color: transparent }
235 | .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
236 |
237 | .CodeMirror-lines {
238 | cursor: text;
239 | min-height: 1px; /* prevents collapsing before first draw */
240 | }
241 | .CodeMirror pre.CodeMirror-line,
242 | .CodeMirror pre.CodeMirror-line-like {
243 | /* Reset some styles that the rest of the page might have set */
244 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
245 | border-width: 0;
246 | background: transparent;
247 | font-family: inherit;
248 | font-size: inherit;
249 | margin: 0;
250 | white-space: pre;
251 | word-wrap: normal;
252 | line-height: inherit;
253 | color: inherit;
254 | z-index: 2;
255 | position: relative;
256 | overflow: visible;
257 | -webkit-tap-highlight-color: transparent;
258 | -webkit-font-variant-ligatures: contextual;
259 | font-variant-ligatures: contextual;
260 | }
261 | .CodeMirror-wrap pre.CodeMirror-line,
262 | .CodeMirror-wrap pre.CodeMirror-line-like {
263 | word-wrap: break-word;
264 | white-space: pre-wrap;
265 | word-break: normal;
266 | }
267 |
268 | .CodeMirror-linebackground {
269 | position: absolute;
270 | left: 0; right: 0; top: 0; bottom: 0;
271 | z-index: 0;
272 | }
273 |
274 | .CodeMirror-linewidget {
275 | position: relative;
276 | z-index: 2;
277 | padding: 0.1px; /* Force widget margins to stay inside of the container */
278 | }
279 |
280 | .CodeMirror-widget {}
281 |
282 | .CodeMirror-rtl pre { direction: rtl; }
283 |
284 | .CodeMirror-code {
285 | outline: none;
286 | }
287 |
288 | /* Force content-box sizing for the elements where we expect it */
289 | .CodeMirror-scroll,
290 | .CodeMirror-sizer,
291 | .CodeMirror-gutter,
292 | .CodeMirror-gutters,
293 | .CodeMirror-linenumber {
294 | -moz-box-sizing: content-box;
295 | box-sizing: content-box;
296 | }
297 |
298 | .CodeMirror-measure {
299 | position: absolute;
300 | width: 100%;
301 | height: 0;
302 | overflow: hidden;
303 | visibility: hidden;
304 | }
305 |
306 | .CodeMirror-cursor {
307 | position: absolute;
308 | pointer-events: none;
309 | }
310 | .CodeMirror-measure pre { position: static; }
311 |
312 | div.CodeMirror-cursors {
313 | visibility: hidden;
314 | position: relative;
315 | z-index: 3;
316 | }
317 | div.CodeMirror-dragcursors {
318 | visibility: visible;
319 | }
320 |
321 | .CodeMirror-focused div.CodeMirror-cursors {
322 | visibility: visible;
323 | }
324 |
325 | .CodeMirror-selected { background: #d9d9d9; }
326 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
327 | .CodeMirror-crosshair { cursor: crosshair; }
328 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
329 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
330 |
331 | .cm-searching {
332 | background-color: #ffa;
333 | background-color: rgba(255, 255, 0, .4);
334 | }
335 |
336 | /* Used to force a border model for a node */
337 | .cm-force-border { padding-right: .1px; }
338 |
339 | @media print {
340 | /* Hide the cursor when printing */
341 | .CodeMirror div.CodeMirror-cursors {
342 | visibility: hidden;
343 | }
344 | }
345 |
346 | /* See issue #2901 */
347 | .cm-tab-wrap-hack:after { content: ''; }
348 |
349 | /* Help users use markselection to safely style text background */
350 | span.CodeMirror-selectedtext { background: none; }
351 |
352 |
--------------------------------------------------------------------------------
/drafts/_color_codes_syndrome_measurements.py:
--------------------------------------------------------------------------------
1 | # Contributor: Victor Onofre
2 | # Licensed under GPLV3
3 |
4 | import stac
5 | import networkx as nx
6 |
7 |
8 | def vertices_physical_qubits(N):
9 | """
10 | Create graph for coordinates of the vertices/physical qubits.
11 |
12 | Only for 4.8.8 color code family.
13 |
14 | Parameter
15 | -----------
16 | N : distance
17 |
18 | Return
19 | ------
20 | H : Graph for coordinates of the vertices/physical qubits
21 | faces: Faces of the graph
22 | """
23 | # Number of qubits in the bottom row
24 | # Note that the distance of each graph is equiavalent to the number of
25 | # vertices in the bottom row.
26 | # We also have N total rows as well.
27 |
28 | # create a graph
29 | H = nx.Graph()
30 |
31 | # This stores the vertices in each row.
32 | row_wise = [[] for _ in range(N)]
33 | # print(row_wise)
34 |
35 | # planar layout (pl) is used to store the coordinates of each vertex
36 | # keys are vertices and values are the coordinates.
37 | # This is later used by networkx to draw the graph
38 | pl = dict()
39 |
40 | # heights of each column
41 | if (N-1) % 4 == 0:
42 | # Case for N=5,9,...
43 | heights = [i for i in range(3, N + 1, 2)] + \
44 | [i for i in range(N, 0, -2)]
45 | else:
46 | # Case for N=3, 7, 11...
47 | heights = [i for i in range(1, N + 1, 2)] + \
48 | [i for i in range(N, 2, -2)]
49 |
50 | # So lets go column by column
51 | # [x, y] is the coordinate of the vertex
52 | for x, j in enumerate(heights):
53 | # print(x,j)
54 | for y in range(j):
55 | # add node to graph
56 | H.add_node((x, y))
57 | row_wise[y].append((x, y))
58 | # print(row_wise)
59 | # we are labelling the vertex at [x,y] by (x,y)
60 | pl[(x, y)] = [x, y]
61 | # print(pl)
62 | # We can add all the vertical edges right here
63 | if y != 0:
64 | H.add_edge((x, y), (x, y - 1))
65 | # print(row_wise[0])
66 | # add all the edges in the bottom row
67 | for i in range(len(row_wise[0]) - 1):
68 | H.add_edge(row_wise[0][i], row_wise[0][i + 1])
69 | # add the edges in the other rows. Remember these alternate.
70 | for y in range(1, N):
71 | for i in range(len(row_wise[y]) - 1):
72 | if i % 2 == 1:
73 | continue
74 | H.add_edge(row_wise[y][i], row_wise[y][i + 1])
75 |
76 | # add diagonal edges
77 | for y in range(N):
78 | if (N-1) % 4 == 0:
79 | # Case for N=5,9,...
80 | # left diagonal edges
81 | if y % 2 == 0 and y < N-1:
82 | H.add_edge(row_wise[y][0], row_wise[y+2][0])
83 | # right diagonal edges
84 | elif y % 3 == 0:
85 | H.add_edge(row_wise[y-1][-1], row_wise[y-3][-1])
86 | else: # Case for N=3, 7, 11...
87 | # left diagonal edges
88 | if y % 4 == 0:
89 | H.add_edge(row_wise[y][0], row_wise[y+2][0])
90 | # right diagonl edges
91 | elif y % 2 == 0 and y < N-1:
92 | H.add_edge(row_wise[y][-1], row_wise[y+2][-1])
93 | # now draw it, giving networkx the positions of each vertex as well
94 | # nx.draw(H, pos=pl)
95 |
96 | # First networkx has to check that the graph is indeed planar.
97 | # the planar embedding (pe) is its record of this.
98 | _, pe = nx.check_planarity(H)
99 |
100 | # Lets create a set to store all the faces
101 | F = set()
102 |
103 | # This the brute forcing.
104 | for edge in pe.edges:
105 | # find a face
106 | f = pe.traverse_face(*edge)
107 | # if the face is length 4 and 8 we process more
108 | # sometimes traverse_face returns big cycles eg. the whole outside of
109 | # the graph
110 | # lets throw those away.
111 | if N > 3:
112 | if len(f) <= 8 and len(f) > 3:
113 | # before adding we are going to sort the edges and then turn
114 | # the list into
115 | # a tuple so it can be added to the set.
116 | F.add(tuple(sorted(f)))
117 | else:
118 | if len(f) <= 4:
119 | F.add(tuple(sorted(f)))
120 | # turn the faces set to a list and sort it.
121 | # not really necessary but nice.
122 | faces = sorted(list(F))
123 |
124 | return faces, H
125 |
126 |
127 | def _color_codes_syndrome_measurements(d):
128 | """
129 | Create syndrome measurement circuit for 4.8.8 color code family.
130 |
131 | Parameter
132 | -----------
133 | d : distance
134 |
135 | Return
136 | ------
137 | stac.Circuit with appended gates
138 | """
139 | # Retrieve graph for coordinates of the vertices/physical qubits and
140 | # the respective faces of the graph given the distance d
141 | faces, H = vertices_physical_qubits(d)
142 |
143 | # Define circuit for syndrome measurement
144 | circ = stac.Circuit()
145 | # Data qubits
146 | circ.append_register(stac.QubitRegister('d', 0, len(H.nodes)))
147 | # Subregister for ancilla qubits
148 | circ.append_register(stac.RegisterRegister('s', 0))
149 | # Subregister for Z stabilizers ancilla qubits
150 | circ.register[0, 1].append(stac.RegisterRegister('t', 0))
151 | # Subregister for X stabilizers ancilla qubits
152 | circ.register[0, 1].append(stac.RegisterRegister('t', 1))
153 |
154 | # Define number of ancilla qubits in each subregister depending on the
155 | # face 4 or 8
156 | for i in range(len(faces)):
157 | if len(faces[i]) == 4:
158 | circ.register[0, 1][0].append(stac.QubitRegister('f', i, 1))
159 | circ.register[0, 1][1].append(stac.QubitRegister('f', i, 1))
160 | else:
161 | circ.register[0, 1][0].append(stac.QubitRegister('f', i, 5))
162 | circ.register[0, 1][1].append(stac.QubitRegister('f', i, 5))
163 |
164 | # Total number of data qubits
165 | qubit_number = list(H.nodes)
166 |
167 | # 4 qubit Z stabilizer
168 | for f, face in enumerate(faces):
169 | if len(face) == 4:
170 | for j in range(4):
171 | number = qubit_number.index(face[j])
172 | # (level, syndrome, type, face, i) = (0, 1, t, f, i)
173 | circ.append('CX', (0, 0, number), (0, 1, 0, f, 0))
174 | circ.append('MR', (0, 1, 0, f, 0))
175 | circ.append('TICK')
176 |
177 | # 4 qubit X stabilizer
178 | for f, face in enumerate(faces):
179 | if len(face) == 4:
180 | circ.append('H', (0, 1, 1, f, 0))
181 | for j in range(4):
182 | number = qubit_number.index(face[j])
183 | # (level, syndrome, type, face, i) = (0, 1, t, f, i)
184 | circ.append('CX', (0, 1, 1, f, 0), (0, 0, number))
185 | circ.append('H', (0, 1, 1, f, 0))
186 | circ.append('MR', (0, 1, 1, f, 0))
187 | circ.append('TICK')
188 |
189 | # Preparation of a 4 qubit cat state for the 8 qubit X stabibilizer
190 | for f, face in enumerate(faces):
191 | if len(face) == 8:
192 | # (level, syndrome, type, face, i) = (0, 1, t, f, i)
193 | circ.append('H', (0, 1, 0, f, 0))
194 | circ.append('CX', (0, 1, 0, f, 0), (0, 1, 0, f, 1))
195 | circ.append('CX', (0, 1, 0, f, 0), (0, 1, 0, f, 2))
196 | circ.append('CX', (0, 1, 0, f, 0), (0, 1, 0, f, 3))
197 | circ.append('CX', (0, 1, 0, f, 0), (0, 1, 0, f, 4))
198 | circ.append('CX', (0, 1, 0, f, 1), (0, 1, 0, f, 0))
199 | circ.append('H', (0, 1, 0, f, 0))
200 | circ.append('MR', (0, 1, 0, f, 0))
201 | circ.append('TICK')
202 |
203 | # Preparation of a 4 qubit cat state for the 8 qubit Z stabibilizer
204 | for f, face in enumerate(faces):
205 | if len(face) == 8:
206 | # (level, syndrome, type, face, i) = (0, 1, t, f, i)
207 | circ.append('H', (0, 1, 1, f, 0))
208 | circ.append('CX', (0, 1, 1, f, 0), (0, 1, 1, f, 1))
209 | circ.append('CX', (0, 1, 1, f, 0), (0, 1, 1, f, 2))
210 | circ.append('CX', (0, 1, 1, f, 0), (0, 1, 1, f, 3))
211 | circ.append('CX', (0, 1, 1, f, 0), (0, 1, 1, f, 4))
212 | circ.append('CX', (0, 1, 1, f, 1), (0, 1, 1, f, 0))
213 | circ.append('H', (0, 1, 1, f, 0))
214 | circ.append('MR', (0, 1, 1, f, 0))
215 | circ.append('TICK')
216 |
217 | # 8 qubit X stabibilizer
218 | for f, face in enumerate(faces):
219 | if len(face) == 8:
220 | counter_x = 1
221 | for qubits in range(0, 8, 2):
222 | number1 = qubit_number.index(face[qubits])
223 | number2 = qubit_number.index(face[qubits + 1])
224 | # (level, syndrome, type, face, i) = (0, 1, t, f, i)
225 | circ.append('CX', (0, 1, 1, f, counter_x), (0, 0, number1))
226 | circ.append('CX', (0, 1, 1, f, counter_x), (0, 0, number2))
227 | counter_x = counter_x + 1
228 |
229 | for k in range(1, 5):
230 | circ.append('H', (0, 1, 1, f, k))
231 | circ.append('MR', (0, 1, 1, f, k))
232 | circ.append('TICK')
233 |
234 | # 8 qubit Z stabibilizer
235 | for f, face in enumerate(faces):
236 | if len(face) == 8:
237 | counter_z = 1
238 | for k in range(1, 5):
239 | circ.append('H', (0, 1, 0, f, k))
240 | for qubits in range(0, 8, 2):
241 | number1 = qubit_number.index(face[qubits])
242 | number2 = qubit_number.index(face[qubits + 1])
243 | # (level, syndrome, type, face, i) = (0, 1, t, f, i)
244 | circ.append('CX', (0, 0, number1), (0, 1, 0, f, counter_z))
245 | circ.append('CX', (0, 0, number2), (0, 1, 0, f, counter_z))
246 | counter_z = counter_z + 1
247 |
248 | for k in range(1, 5):
249 | circ.append('MR', (0, 1, 0, f, k))
250 | circ.append('TICK')
251 |
252 | return circ
253 |
254 |
255 | print(_color_codes_syndrome_measurements(7))
256 |
--------------------------------------------------------------------------------
/chapters/stabilizer-codes/stabilizer-groups.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "a23fe73c",
6 | "metadata": {
7 | "tags": []
8 | },
9 | "source": [
10 | "# Stabilizer groups"
11 | ]
12 | },
13 | {
14 | "cell_type": "markdown",
15 | "id": "b00286a1",
16 | "metadata": {
17 | "tags": [
18 | "remove-cell"
19 | ]
20 | },
21 | "source": [
22 | "
Latex macros
\n",
23 | "$ \\newcommand{\\ket}[1]{|#1\\rangle} $\n",
24 | "$ \\newcommand{\\bra}[1]{\\langle #1|} $\n",
25 | "$\\newcommand{\\iu}{{i\\mkern1mu}}$\n",
26 | "$ \\newcommand{\\e}{\\mathrm{e}}$\n",
27 | "$\\newcommand{\\bigo}{\\mathcal{O}}$\n",
28 | "$\\newcommand{\\set}[1]{\\{#1\\}}$\n",
29 | "$\\newcommand{\\oper}[1]{\\mathcal{#1}}$\n",
30 | "$\\newcommand{\\st}[1]{\\mathcal{#1}}$\n",
31 | "$\\newcommand{\\vecs}[1]{\\mathcal{#1}}$\n",
32 | "$\\newcommand{\\group}[1]{\\mathcal{#1}}$\n",
33 | "$\\newcommand{\\union}{\\hspace{0.25em}\\cup\\hspace{0.25em}}$"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "id": "bef71833-f6c7-404b-ad71-5d2c3cb3157c",
39 | "metadata": {},
40 | "source": [
41 | "Among the many subgroups of the Pauli group $\\group{P}_n$, there is a particular type of subgroups, called the stabilizer group $S$, which satisfy the following properties, in addition to the group axioms:\n",
42 | "\n",
43 | "* All pairs of elements in $S$ commute, i.e. for every $P,Q \\in S$, we have $PQ = QP$. \n",
44 | "* $-I = - I_0 \\otimes I_1 \\otimes \\cdots \\otimes I_{n-1}$ is not a part of $S$. \n",
45 | "\n",
46 | "We will see later on why these properties are useful, but for now, let's try to create some examples of these groups so we can build some intuition.\n",
47 | "\n",
48 | "**Example:** Let\n",
49 | "\\begin{align}\n",
50 | "P_0 &= I \\otimes I \\otimes I, \\\\\n",
51 | "P_1 &= Z \\otimes Z \\otimes I, \\\\\n",
52 | "P_2 &= Z \\otimes I \\otimes Z, \\\\\n",
53 | "P_3 &= I \\otimes Z \\otimes Z,\n",
54 | "\\end{align}\n",
55 | "and consider the subset,\n",
56 | "\\begin{equation}\n",
57 | " S = \\set{P_0, P_1, P_2, P_3},\n",
58 | "\\end{equation}\n",
59 | "of $\\group{P}_3$.\n",
60 | "\n"
61 | ]
62 | },
63 | {
64 | "cell_type": "markdown",
65 | "id": "6bea1600-3033-4426-ba14-d598bb5309d7",
66 | "metadata": {},
67 | "source": [
68 | "#### Task 1 (On paper)\n",
69 | "Produce the multiplication table of the subgroup $S$ defined in example above, to show that $S$ is closed under multiplication and hence a subgroup of $\\group{P}_3$. (We will often just write $I$ instead of $P_0 = I\\otimes I \\otimes I$.)\n",
70 | "\n",
71 | "| $\\times$ | $I$ | $P_1$ | $P_2$ | $P_3$ | \n",
72 | "|:--------:|:---:|:------:|:------:|:------:|\n",
73 | "| $I$ | | | | |\n",
74 | "| $P_1$ | | | | |\n",
75 | "| $P_2$ | | | | |\n",
76 | "| $P_3$ | | | | |\n"
77 | ]
78 | },
79 | {
80 | "cell_type": "markdown",
81 | "id": "a53eb3b5-3a9c-47f7-b21c-e1550c0c3b73",
82 | "metadata": {},
83 | "source": [
84 | "Further, we claim that $S$ is a stabilizer group. \n",
85 | "\n",
86 | "* From the multiplication table you just created, you can also read off the commutation relations. You can verify that all elements of $S$ commute.\n",
87 | "* Moreover $-I$ is not part of $S$, so the second property is trivially satisfied. "
88 | ]
89 | },
90 | {
91 | "cell_type": "markdown",
92 | "id": "83ac997d-891e-4781-b590-71cfc703561b",
93 | "metadata": {},
94 | "source": [
95 | "#### Task 2 (On paper)\n",
96 | "One way of constructing subgroups is to start with a subset of elements of the group. We multiply these elements together in every way possible. Whenever we create an element that is not part of the subset, we add it to the subset. Eventually, we will have exhausted all possible multiplications and we will have a subset closed under multiplication. \n",
97 | "\n",
98 | "Take $P_1 = X \\otimes X \\otimes I$ and $P_2 = I \\otimes X \\otimes X$. \n",
99 | "\n",
100 | "* Multiply them together in every combination, till you find a set of elements that is closed.\n",
101 | "\n",
102 | "* Write down the multiplication table of this group, and verify that this is a stabilizer group."
103 | ]
104 | },
105 | {
106 | "cell_type": "markdown",
107 | "id": "01800c92-26b4-4f8c-96ed-4cf2ec6235e0",
108 | "metadata": {},
109 | "source": [
110 | "## Generators of a group\n",
111 | "This is an opportune moment to introduce the notion of the *generators* of a group. The generators of a group can be used to construct all other elements of the group. \n",
112 | "\n",
113 | "**Example:** In the first example above, every element in the group is some product of $P_1$ and $P_2$,\n",
114 | "\\begin{align}\n",
115 | " I \\otimes I \\otimes I &= P_1^2 = P_2^2, \\\\\n",
116 | " Z \\otimes Z \\otimes I &= P_1, \\\\\n",
117 | " I \\otimes Z \\otimes Z &= P_2, \\\\\n",
118 | " Z \\otimes I \\otimes Z &= P_1P_2.\n",
119 | "\\end{align}\n",
120 | "Therefore, $P_1$ and $P_2$ are called the generators of the group $S$.\n",
121 | "\n",
122 | "> The **generators** of a group is a subset of elements of the group, and that can be used to construct all elements of the group. The generators are not unique.\n",
123 | "\n",
124 | "**Example:** In Task 2, you created a group generated by $X \\otimes X \\otimes I$ and $I \\otimes X \\otimes X$.\n",
125 | "\n",
126 | "*Question:* Show that the stabilizer group in the first example, can be generated by $P_1$ and $P_3$ as well, i.e. show how to write every element of the group in terms of these two elements.\n"
127 | ]
128 | },
129 | {
130 | "cell_type": "markdown",
131 | "id": "5536ef6a-c912-4c6e-a97c-7dfb71b660ee",
132 | "metadata": {},
133 | "source": [
134 | "The advantage of identifying the generators for stabilizer groups is that they reduce the storage space for specifying a group. This is because of two properties.\n",
135 | "\n",
136 | "* Property of stabilizer groups: All elements commute.\n",
137 | "* Property of all Pauli operators: $P^2$ is either $I$ or $-I$.\n",
138 | "\n",
139 | "Let $S$ be a stabilizer group, with generators $\\set{g_i}_{i=0}^{m-1}$. The first property means that we can write any element $h$ in $S$ as the product.\n",
140 | "\\begin{equation}\n",
141 | " h = g_0^{j_0} g_1^{j_1} \\cdots g_{m-1}^{j_{m-1}},\n",
142 | "\\end{equation}\n",
143 | "where we don't care about the order of the generators in the product. The power of $j_i$ on each generator tells us how many times it appears in the product in order to create $h$.\n",
144 | "\n",
145 | "The second property means that $j_i$ can only be $0$ or $1$.\n",
146 | "\n",
147 | "This means that any element $h$ is completely specified by the bitstring $(j_0, j_1, \\dots, j_{m-1})$. Recognizing that there are $2^m$ such strings, the following lemma immediately follows.\n",
148 | "\n",
149 | "**Lemma:** If $S$ has $m$ generators, then it has $2^m$ elements.\n",
150 | "\n",
151 | "To work with stabilizer codes, we will be depending highly on generators of the stabilizer groups."
152 | ]
153 | },
154 | {
155 | "cell_type": "markdown",
156 | "id": "a7bfc4d0-b29a-47a2-a0f3-45e93e4d2bb7",
157 | "metadata": {},
158 | "source": [
159 | "#### Task 3 (On paper)\n",
160 | "Determine the bitstring representation of all elements in $S$ for\n",
161 | "\n",
162 | "* Example 1\n",
163 | "* Task 2"
164 | ]
165 | },
166 | {
167 | "cell_type": "markdown",
168 | "id": "a7c89096-21a3-479c-af1e-9d8886219d8c",
169 | "metadata": {
170 | "tags": []
171 | },
172 | "source": [
173 | "## States stabilized by stabilizer groups\n",
174 | "In [stabilizer states](../stabilizer-codes/stabilizer-states.ipynb), we saw that multiple operators can stabilize the same state.\n",
175 | "\n",
176 | "Since, stabilizer groups are just a set of operators, a very natural question to ask is, what states are stabilized by these operators?\n",
177 | "\n",
178 | "**Example:** Above, we created the stabilizer group\n",
179 | "\\begin{equation}\n",
180 | " S = \\set{I \\otimes I \\otimes I, Z \\otimes Z \\otimes I, Z \\otimes I \\otimes Z, I \\otimes Z \\otimes Z}.\n",
181 | "\\end{equation}\n",
182 | "Let's try to construct the state simultaneously stabilized by all these operators.\n",
183 | "\n",
184 | "* The identity stabilizes all states.\n",
185 | "* We already know that $Z \\otimes Z \\otimes I, I \\otimes Z \\otimes Z$ jointly stabilize $\\set{\\ket{000},\\ket{111}}$. \n",
186 | "* It's easy to discover that $Z \\otimes I \\otimes Z$ has stabilizer states $\\set{\\ket{000}, \\ket{010}, \\ket{101}, \\ket{111}}$.\n",
187 | "\n",
188 | "Hence, the common intersection is $\\set{\\ket{000},\\ket{111}}$. Therefore, \n",
189 | "\n",
190 | "> The group $S$ stabilizes the states $\\set{\\ket{000},\\ket{111}}$, and any of their linear combinations.\n",
191 | "\n",
192 | "We note that $\\set{\\ket{000},\\ket{111}}$ is the basis of the repetition code. The connection between $S$ and repetition code will be fleshed out in the next notebook."
193 | ]
194 | },
195 | {
196 | "cell_type": "markdown",
197 | "id": "bf52e888-8b0f-4f07-bd7d-22e45e3f8d85",
198 | "metadata": {},
199 | "source": [
200 | "### Generators are sufficient to determine the stabilizer states of a group\n",
201 | "Actually, we are doing too much work in determining the stabilizer states. Each element of $S$ is specified by a product of two generators $P_1$ and $P_2$. Note the following two self-evident properties.\n",
202 | "\n",
203 | "* If $P_1$ and $P_2$ both stabilize a state $\\ket{\\psi}$, then any $h \\in S$ (which is just some product of $P_1$ and $P_2$) will also stabilize $\\ket{\\psi}$.\n",
204 | "\n",
205 | "* If $P_1$ or $P_2$ don't stabilize a state, then that state cannot be included in our final set of states.\n",
206 | "\n",
207 | "Taken together this means that we only need to determine states that are stablized by the all generators to determine the states stabilized by the stabilizer group.\n",
208 | "\n",
209 | "**Lemma:** The basis of states stabilized by a stabilizer group $S$, is the intersection of states stabilized by the generators of $S$."
210 | ]
211 | },
212 | {
213 | "cell_type": "code",
214 | "execution_count": null,
215 | "id": "fe6bf57f-eb97-4241-9125-7d1eb3dca167",
216 | "metadata": {},
217 | "outputs": [],
218 | "source": []
219 | }
220 | ],
221 | "metadata": {
222 | "kernelspec": {
223 | "display_name": "Python 3 (ipykernel)",
224 | "language": "python",
225 | "name": "python3"
226 | },
227 | "language_info": {
228 | "codemirror_mode": {
229 | "name": "ipython",
230 | "version": 3
231 | },
232 | "file_extension": ".py",
233 | "mimetype": "text/x-python",
234 | "name": "python",
235 | "nbconvert_exporter": "python",
236 | "pygments_lexer": "ipython3",
237 | "version": "3.10.8"
238 | },
239 | "widgets": {
240 | "application/vnd.jupyter.widget-state+json": {
241 | "state": {},
242 | "version_major": 2,
243 | "version_minor": 0
244 | }
245 | }
246 | },
247 | "nbformat": 4,
248 | "nbformat_minor": 5
249 | }
250 |
--------------------------------------------------------------------------------
/chapters/qec-formal/quantum-error-correcting-codes.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "a23fe73c",
6 | "metadata": {
7 | "tags": []
8 | },
9 | "source": [
10 | "# Quantum error-correcting codes"
11 | ]
12 | },
13 | {
14 | "cell_type": "markdown",
15 | "id": "b00286a1",
16 | "metadata": {
17 | "tags": [
18 | "remove-cell"
19 | ]
20 | },
21 | "source": [
22 | "
Latex macros
\n",
23 | "$ \\newcommand{\\ket}[1]{|#1\\rangle} $\n",
24 | "$ \\newcommand{\\bra}[1]{\\langle #1|} $\n",
25 | "$\\newcommand{\\iu}{{i\\mkern1mu}}$\n",
26 | "$ \\newcommand{\\e}{\\mathrm{e}}$\n",
27 | "$\\newcommand{\\bigo}{\\mathcal{O}}$\n",
28 | "$\\newcommand{\\set}[1]{\\{#1\\}}$\n",
29 | "$\\newcommand{\\oper}[1]{\\mathcal{#1}}$\n",
30 | "$\\newcommand{\\st}[1]{\\mathcal{#1}}$\n",
31 | "$\\newcommand{\\vecs}[1]{\\mathcal{#1}}$\n",
32 | "$\\newcommand{\\group}[1]{\\mathcal{#1}}$\n",
33 | "$\\newcommand{\\union}{\\hspace{0.25em}\\cup\\hspace{0.25em}}$"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "id": "e8d146ed-0b66-4446-82c0-9e6101fc744f",
39 | "metadata": {},
40 | "source": [
41 | "Given a quantum code, it is not clear if it is useful. The above definition only talks about how to encode a message into the codespace. It does not speak at all about which errors can be detected or corrected with such an encoding. Why is this a problem, when this was not a significant problem for classical codes. There are three critical issues which make the construction of a quantum error-correction codes a more difficult task than their classical counterparts.\n",
42 | "\n",
43 | "* The no-cloning theorem forbids us from copying quantum states. This limits the types of operations we can perform during the decoding process.\n",
44 | "* Measuring quantum states in superposition destroys the superposition, so we can't simply measure the received blocks to determine what state they are in.\n",
45 | "* Classical codes only have to deal with bit-flip errors, but quantum codes will have to deal with any sort of unitary or non-unitary noisy interactions. This includes phase errors, and it includes continuous rotations etc. \n",
46 | "\n",
47 | "Fortunately, we will discover that all of these challenges can be surmounted. To build the theory to do so we start by explicitly defining what conditions make a quantum code an error-correcting one."
48 | ]
49 | },
50 | {
51 | "cell_type": "markdown",
52 | "id": "895184aa-68a4-4a79-b1d4-2a41073f7cf5",
53 | "metadata": {},
54 | "source": [
55 | "An encoding map is a transformation from the message space to the code, the subspace of the codespace, formally defined as follows.\n",
56 | "\n",
57 | "> Given a quantum code $\\vecs{C}$, an **encoding map** or encoder is a unitary $U:\\vecs{H}^m \\to \\vecs{H}^c$ that maps the message space to the code.\n",
58 | "\n",
59 | "Given any message $\\ket{\\psi} \\in \\vecs{H}^m$, the state $U\\ket{\\psi}$ is an element of the code $C$. The encoding map is akin to the generator matrix for classical codes. \n",
60 | "\n",
61 | "What we require is that every code $C$ be paired with a set of errors $\\st{E}$ that it can correct, and that there exist a process to actually do so.\n",
62 | "\n",
63 | "> A **quantum error-correction code** ($C,\\st{E}$) is a quantum code along with a set of errors $\\st{E}$, such that given an encoding map $U$ associated with the code, there exists a quantum channel $\\oper{D}$, called the decoder, such that for all $E \\in \\st{E}$ and for all $\\ket{\\psi} \\in \\vecs{H}^m$,\n",
64 | " \\begin{equation}\n",
65 | " \\oper{D}\\left(EU\\ket{\\psi}\\bra{\\psi}U^\\dagger E^\\dagger\\right) = c(E,\\ket{\\psi}) \\ket{\\psi}\\bra{\\psi},\n",
66 | " \\end{equation}\n",
67 | " where $c(E,\\ket{\\psi})$ is a constant.\n",
68 | "\n",
69 | "This definition postulates that the existence of a decoder is necessary for a code to be an error-correcting one. When can such a decoder exist? Whenever, there is enough information extractable from every corrupted codeword so as to reverse the effects of the error. Let's build some basic results that will lead us to some precise mathematical conditions.\n",
70 | "\n",
71 | "* First, note that given any error $E_a$ and any state $\\ket{\\psi} \\in \\vecs{C}$ the corrupted codeword $\\ket{\\tilde\\psi} = E_a\\ket{\\psi}$ must be completely outside the code. If it is not, what would happen? We could write $\\ket{\\psi} = \\ket{\\psi_c} + \\ket{\\psi_\\perp}$ such that $E_a\\ket{\\psi_\\perp} \\in \\vecs{C}$ and $E_a\\ket{\\psi_\\perp} \\in \\vecs{C}^{\\perp}$. Now, $\\ket{\\psi}$ is in the code, then so is $\\ket{\\psi_c}$ (up to a normalization) and it is some other valid codeword. Hence, the effect of the error $E_a$ on $\\ket{\\psi_c}$ is to keep it inside the code, i.e.\\ map it onto a different codeword. This is clearly a bad situation because Bob would just assume that no error has occurred and $E_a\\ket{\\psi_c}$ is the codeword Alice meant to send. Hence, by contradiction, we infer that our code must be designed so that all errors move every codeword out of the code.\n",
72 | "\n",
73 | "* Next, suppose we have an orthogonal basis $\\set{\\ket{\\psi_i}}_i$ for $\\vecs{C}$. Given any error $E_a$, what do we require of $E_a\\ket{\\psi_i}$ and $E_a\\ket{\\psi_j}$ for $i\\ne j$, so the decoder can function. We will need these two corrupted codewords to remain orthogonal, so that the decoder has enough information to correctly decode. To see this, let's assume to the contrary that the corrupted states are not orthogonal, i.e. $(\\bra{\\psi_i}E_a^\\dagger)( E_a\\ket{\\psi_j}) \\ne 0$. To tease out the non-orthogonal part of this inner product, let's write\n",
74 | " \\begin{equation}\n",
75 | " \\ket{\\psi_j} = \\ket{\\psi_\\parallel} + \\ket{\\psi_\\perp},\n",
76 | " \\end{equation}\n",
77 | " such that $E_a\\ket{\\psi_\\parallel}$ is parallel to $E_a\\ket{\\psi_i}$, while $E_a\\ket{\\psi_\\perp}$ is perpendicular to $E_a\\ket{\\psi_i}$. As in the argument above, $\\ket{\\psi_\\parallel}$ must also be a valid codeword (because it is part of $\\ket{\\psi_j}$). And because $\\ket{\\psi_i}$ and $\\ket{\\psi_j}$ are perpendicular, $\\ket{\\psi_\\parallel}$ and $\\ket{\\psi_i}$ are distinct codewords. Hence, we discover that two distinct codewords, $\\ket{\\psi_\\parallel}$ and $\\ket{\\psi_i}$, when acted upon by error $E_a$ result in the same corrupted codeword. This is again a bad situation, because the decoder will be unable to decide how to fix the error on the corrupted codeword. Hence, in order to have an error-correcting code that works, we need each error to map orthogonal basis vectors of $C$ to orthgonal states,\n",
78 | " \\begin{equation}\n",
79 | " (\\bra{\\psi_i}E_a^\\dagger) (E_a\\ket{\\psi_j}) = 0, \\quad i\\ne j.\n",
80 | " \\end{equation}\n",
81 | "\n",
82 | "* Let's now make an even stronger condition on our code. Consider the corrupted codewords $E_a\\ket{\\psi_i}$ and $E_b\\ket{\\psi_j}$, i.e. distinct errors on two distinct basis states. Do these need to be orthogonal? By exactly the same argument as above, yes. In words, if these two corrupted codewords are not orthogonal, then there is a part of $\\ket{\\psi_j}$, called $\\ket{\\psi_\\parallel}$, that under the action of $E_b$ becomes parallel to $E_a\\ket{\\psi_i}$. As above, $\\ket{\\psi_\\parallel}$ is a valid codeword. If the Bob receives $E_a\\ket{\\psi_i} = E_b\\ket{\\psi_\\parallel}$, then his decoder will be unable to tell if Alice sent $\\ket{\\psi_i}$ which was distorted by $E_a$ or $\\ket{\\psi_\\parallel}$ which was distorted by $E_b$. Hence, we can conclude that for a valid error-correcting code,\n",
83 | " \\begin{equation}\n",
84 | " (\\bra{\\psi_i}E_a^\\dagger) (E_b\\ket{\\psi_j}) = 0, \\quad i\\ne j.\n",
85 | " \\end{equation}\n",
86 | "\n",
87 | "* What about the case when $i = j$ with distinct errors? Meaning, is there any requirement on $E_a\\ket{\\psi_i}$ and $E_b\\ket{psi_i}$? First of all, let's make it clear that these don't need to be orthogonal - though they can be. We saw the example of the phase-flip code where the action of $Z$ on different qubits yielded the same corrupted codeword. But this was not a problem, because in each case, the correction operation was the same. So up till now, our condition is\n",
88 | " \\begin{equation}\n",
89 | " \\bra{\\psi_i}E_a^\\dagger E_b\\ket{\\psi_j} = \\delta_{ij}A,\n",
90 | " \\end{equation}\n",
91 | " where the $\\delta_{ij}$ ensures that the inner product is zero if we are dealing with two different basis states, and possibly non-zero if they are the same. This last part is determined by the unknown constant $A$. \n",
92 | " However, we can recognize that if $E_a\\ket{\\psi_i}$ and $E_b\\ket{\\psi_i}$ are indeed orthogonal for every pair of errors $E_a$ and $E_b$, then our code will be an error-correcting one. We assume,\n",
93 | " \\begin{equation}\n",
94 | " \\bra{\\psi_i}E_a^\\dagger E_b\\ket{\\psi_j} = \\delta_{ij}\\delta_{ab}A.\n",
95 | " \\end{equation}\n",
96 | " Now, recall that when we went from the noise channel to the set $\\set{E_a}_a$ we choose from the one of the infinite such sets. Let's transform to a different set $\\set{F_c}$ where\n",
97 | " \\begin{equation}\n",
98 | " F_c = \\sum_a\\alpha_{ca}E_a.\n",
99 | " \\end{equation}\n",
100 | " Now, let's evaluate the inner product\n",
101 | " \\begin{align}\n",
102 | " \\bra{\\psi_i}F_c^\\dagger F_d\\ket{\\psi_j} &= \\sum_{ab}\\alpha_{ca}^*\\alpha_{db}\\bra{\\psi_i}E_a^\\dagger E_b\\ket{\\psi_j}, \\\\\n",
103 | " &= \\sum_{ab}\\alpha_{ca}^*\\alpha_{db}\\delta_{ij}\\delta_{ab}A, \\\\\n",
104 | " &= \\left(\\sum_a\\alpha_{ca}^*\\alpha_{da}A\\right)\\delta_{ij}, \\\\\n",
105 | " &= A_{cd}'\\delta_{ij}. \\tag{*}\n",
106 | " \\end{align}\n",
107 | " What this calculation tells us that, unless we pick a very clever basis for the errors, the states $E_a\\ket{\\psi_i}$ and $E_b\\ket{\\psi_i}$ will not be orthogonal for every $i$ and every pair of $E_a$ and $E_b$. \n",
108 | "\n",
109 | "In the above, we have taken care of every possible case of possible confusion of the detector, and resolved them. We can now use Eq. (*) to write down the following theorem.\n",
110 | "\n",
111 | "**Theorem:** A code $\\vecs{C}$ is a quantum error-correcting code for the set of errors $\\set{E_a}$ iff\n",
112 | " \\begin{equation}\n",
113 | " \\bra{\\psi_i}E_a^\\dagger E_b\\ket{\\psi_j} = A_{ab}\\delta_{ij},\n",
114 | " \\end{equation}\n",
115 | " for the orthogonal basis $\\set{\\ket{\\psi_i}}_i$.\n"
116 | ]
117 | },
118 | {
119 | "cell_type": "code",
120 | "execution_count": null,
121 | "id": "4b798a76-aa63-4a4c-b2be-e22d0e0e1d0c",
122 | "metadata": {},
123 | "outputs": [],
124 | "source": []
125 | }
126 | ],
127 | "metadata": {
128 | "kernelspec": {
129 | "display_name": "Python 3 (ipykernel)",
130 | "language": "python",
131 | "name": "python3"
132 | },
133 | "language_info": {
134 | "codemirror_mode": {
135 | "name": "ipython",
136 | "version": 3
137 | },
138 | "file_extension": ".py",
139 | "mimetype": "text/x-python",
140 | "name": "python",
141 | "nbconvert_exporter": "python",
142 | "pygments_lexer": "ipython3",
143 | "version": "3.10.8"
144 | },
145 | "widgets": {
146 | "application/vnd.jupyter.widget-state+json": {
147 | "state": {},
148 | "version_major": 2,
149 | "version_minor": 0
150 | }
151 | }
152 | },
153 | "nbformat": 4,
154 | "nbformat_minor": 5
155 | }
156 |
--------------------------------------------------------------------------------
/chapters/stabilizer-codes/pauli-group.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "a23fe73c",
6 | "metadata": {
7 | "tags": []
8 | },
9 | "source": [
10 | "# Pauli group"
11 | ]
12 | },
13 | {
14 | "cell_type": "markdown",
15 | "id": "b00286a1",
16 | "metadata": {
17 | "tags": [
18 | "remove-cell"
19 | ]
20 | },
21 | "source": [
22 | "
Latex macros
\n",
23 | "$ \\newcommand{\\ket}[1]{|#1\\rangle} $\n",
24 | "$ \\newcommand{\\bra}[1]{\\langle #1|} $\n",
25 | "$\\newcommand{\\iu}{{i\\mkern1mu}}$\n",
26 | "$ \\newcommand{\\e}{\\mathrm{e}}$\n",
27 | "$\\newcommand{\\bigo}{\\mathcal{O}}$\n",
28 | "$\\newcommand{\\set}[1]{\\{#1\\}}$\n",
29 | "$\\newcommand{\\oper}[1]{\\mathcal{#1}}$\n",
30 | "$\\newcommand{\\st}[1]{\\mathcal{#1}}$\n",
31 | "$\\newcommand{\\vecs}[1]{\\mathcal{#1}}$\n",
32 | "$\\newcommand{\\group}[1]{\\mathcal{#1}}$\n",
33 | "$\\newcommand{\\union}{\\hspace{0.25em}\\cup\\hspace{0.25em}}$"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "id": "3a4a37fe-8dc1-4649-bd69-71a2c37444fb",
39 | "metadata": {},
40 | "source": [
41 | "We are going to study the simplest class of quantum error-correcting codes, called the stabilizer codes. To understand the stabilizer codes, we need to build up some theory."
42 | ]
43 | },
44 | {
45 | "cell_type": "markdown",
46 | "id": "d85aa834-3079-4520-a8b6-0fdcc94e2773",
47 | "metadata": {},
48 | "source": [
49 | "## The Pauli matrices\n",
50 | "Recall the Pauli matrices along with the identity matrix, $\\set{I, X, Y, Z}$. These matrices have the property that multiplying them together in any way, gets us back to the same set of matrices up to a factor of $\\pm 1$ or $\\pm \\iu$.\n",
51 | "\n",
52 | "For instance,\n",
53 | "\\begin{equation}\n",
54 | " XY = \\iu Z, \\quad YZ = \\iu X, \\quad ZX = \\iu X.\n",
55 | "\\end{equation}\n",
56 | "\n"
57 | ]
58 | },
59 | {
60 | "cell_type": "markdown",
61 | "id": "467f8d01-e5f1-4c78-8778-b1fc772ae163",
62 | "metadata": {},
63 | "source": [
64 | "#### Task 1 (On paper)\n",
65 | "Complete the following multiplication table.\n",
66 | "\n",
67 | "| $\\times$ | $I$ | $X$ | $Y$ | $Z$ |\n",
68 | "|:--------------------:|:-----------:|:----------:|:----------:|:-----------:|\n",
69 | "| $I$ | $II = I$ | $IX=X$ | $IY=Y$ | $IZ=Z$ |\n",
70 | "| $X$ | $XI = X$ | | $XY=\\iu Z$ | |\n",
71 | "| $Y$ | $YI = Y$ | | | |\n",
72 | "| $Z$ | $ZI = Z$ | | | |\n",
73 | "\n",
74 | "The first row and colum have been completed for you, as well as one additional element.\n",
75 | "\n",
76 | "\n"
77 | ]
78 | },
79 | {
80 | "cell_type": "markdown",
81 | "id": "52bd7939-4e8c-428a-bfa2-2957910673c9",
82 | "metadata": {},
83 | "source": [
84 | "### Commutation relations\n",
85 | "There are one additional property of these matrices that we will use over and over again, and that is the *commutation properties*.\n",
86 | "\n",
87 | "For $P, Q \\in \\set{I, X, Y, Z}$, we have either \n",
88 | "\n",
89 | "* $P$ and $Q$ commute, i.e. $PQ = QP$, or\n",
90 | "* $P$ and $Q$ anti-commute, i.e. $PQ = -QP$. \n",
91 | "\n",
92 | "You can verify this by comparing the $(i,j)$ entry in the table above with the $(j,i)$ one. Either the entries are the same ($i$th and $j$th operators commute) or they differ by a minus sign ($i$th and $j$th operators anti-commute).\n",
93 | "\n"
94 | ]
95 | },
96 | {
97 | "cell_type": "markdown",
98 | "id": "7d7e5380-7caf-4a1f-aef9-962b470368d3",
99 | "metadata": {},
100 | "source": [
101 | "### The Pauli group $\\group{P}_1$\n",
102 | "What we have discovered that the 16 matrices,\n",
103 | "\\begin{equation}\n",
104 | " \\group{P}_1 = \\set{\\pm I, \\pm \\iu I, \\pm X, \\pm \\iu X, \\pm Y, \\pm \\iu Y, \\pm Z, \\pm \\iu Z},\n",
105 | "\\end{equation}\n",
106 | "form a [group](https://en.wikipedia.org/wiki/Group_(mathematics)). This means the elements of $\\group{P}_1$ satisfy the group axioms:\n",
107 | "\n",
108 | "* Multiplying any two elements of $\\group{P}_1$ gives us another element of $\\group{P}_1$. You have explicitly checked this above. This property is called closure (under multiplication) - equivalently, we say the set $\\group{P}_1$ is closed under multiplication. \n",
109 | "* Given three elements $P, Q, R$ in the $\\group{P}_1$, we have $(PQ)R = P(QR)$. This is true because the elements of $\\group{P}_1$ are matrices. This property is called associativity of multiplication.\n",
110 | "* There is an identity element, $I$, in $\\group{P}_1$. \n",
111 | "* For every element in $\\group{P}_1$, its inverse is also inside $\\group{P}_1$. For example, the inverse of $X$ is $X$ itself. The inverse of $\\iu X$ is $-\\iu X$.\n",
112 | "\n",
113 | "*Question:* Determine the inverse of every element in $\\group{P}_1$.\n",
114 | "\n",
115 | "Note that $\\group{P_1}$ has 16 elements here, because there are 4 Pauli matrices and 4 possible phase factors ($\\pm 1, \\pm \\iu$).\n"
116 | ]
117 | },
118 | {
119 | "cell_type": "markdown",
120 | "id": "4de780f6-2a7e-40c9-95e8-838ce645304f",
121 | "metadata": {},
122 | "source": [
123 | "### The Pauli group $\\group{P}_2$\n",
124 | "Now, consider the tensor product of two Pauli matrices, meaning elements such as $X \\otimes Z$ or $ Y \\otimes Z$. You are already familiar with the multiplication of such elements. Given $P = P^{(0)} \\otimes P^{(1)}$ and $Q = Q^{(0)} \\otimes Q^{(1)}$ in $\\group{P}_2$,\n",
125 | "\\begin{equation}\n",
126 | "PQ = (P^{(0)} \\otimes P^{(1)})(Q^{(0)} \\otimes Q^{(1)}) = P^{(0)}Q^{(0)} \\otimes P^{(1)}Q^{(1)}.\n",
127 | "\\end{equation}\n",
128 | "\n",
129 | "**Example:** $(X \\otimes Z)(Y \\otimes Z) = (\\iu Z) \\otimes I = \\iu(Z \\otimes I)$.\n",
130 | "\n",
131 | "Suppose, we collect all such elements into a set. How many such elements are there? In $P^{(0)}\\otimes P^{(1)}$, the matrix $P^{(0)}$ can be one of four Pauli matrices, and so can $P^{(1)}$. Additionally, we have four possible phase factors. In total, we will have $4^{2+1}=64$ elements in a set we will call $\\group{P}_2$.\n",
132 | "\n",
133 | "Again, we go over the four axioms of groups, and make sure $\\group{P}_2$ satisfies them.\n",
134 | "\n",
135 | "* We have confirmed that multipling these 64 elements together in any way gives us one of the 64 elements.\n",
136 | "* Multiplication continues to be associative.\n",
137 | "* The element $I \\otimes I$ is the identity element.\n",
138 | "* We can easily construct the inverse of any element $P^{(0)} \\otimes P^{(1)}$ as ${P^{(0)}}^{-1} \\otimes {P^{(1)}}^{-1}$, and it will be one among the 64 elements.\n",
139 | "\n",
140 | "*Question:* Determine the inverse of $\\iu (X \\otimes Y)$.\n",
141 | "\n"
142 | ]
143 | },
144 | {
145 | "cell_type": "markdown",
146 | "id": "13fb6783-d172-4233-9368-6b2f91fa24b5",
147 | "metadata": {},
148 | "source": [
149 | "### The Pauli group $\\group{P}_n$\n",
150 | "It should be clear now, how to extend the above analysis to tensor products of $n$ Pauli matrices, $P^{(0)} \\otimes P^{(1)} \\otimes \\cdots \\otimes P^{(n-1)}$. By the same arguments as above, there will be $4^{n+1}$ elements in $\\group{P}_n$.\n",
151 | "\n",
152 | "All our discussion of the stabilizer codes will occur in the context of the Pauli group."
153 | ]
154 | },
155 | {
156 | "cell_type": "markdown",
157 | "id": "72530c14-1149-4932-afb9-4a2e5b3b8830",
158 | "metadata": {},
159 | "source": [
160 | "## Subgroups of the Pauli group\n",
161 | "A *subgroup* is a subset of the elements of the group that itself form a group. Meaning the subset satisfies all four of the axioms of the group.\n",
162 | "\n",
163 | "**Example:** Consider the $\\set{I,X} \\subset \\group{P}_1$. We can check all four axioms.\n",
164 | "\n",
165 | "* $IX = X$, $I^2 = I$, $X^2 = I$ are all in the subset, so closure of multiplication is satified.\n",
166 | "* Associativity continues to hold.\n",
167 | "* The identity is in the subset.\n",
168 | "* The inverse of $I$ is $I$ and the inverse of $X$ is $X$. So all elements have their inverse in the subset as well.\n",
169 | "\n",
170 | "Hence, this subset is a subgroup of $\\group{P}_1$.\n",
171 | "\n",
172 | "*Question:* Does $\\set{I,\\iu X} \\subset \\group{P}_1$ form a subgroup of $\\group{P}_1$?\n",
173 | "\n",
174 | "**Example:** Consider $\\group{P}_3$, which has elements of the form $P = P^{(0)} \\otimes P^{(1)} \\otimes P^{(2)}$. This group has a subgroup, which is defined as follow.\n",
175 | "\n",
176 | "Let $P^{(1)} = I$, so that $Q = P^{(0)} \\otimes I \\otimes P^{(2)}$, and consider all such elements in $\\group{P}_3$, i.e., we have the subset\n",
177 | "\\begin{equation}\n",
178 | " \\group{H} = \\set{ Q \\in \\group{P}_3 \\text{ such that } Q=P^{(0)} \\otimes I \\otimes P^{(2)}}.\n",
179 | "\\end{equation}\n",
180 | "\n",
181 | "To check that $\\group{H}$ is indeed a subgroup, we check all four properties.\n",
182 | "\n",
183 | "* Closure under multiplication. Its quite clear that multiplying two elements of the form $Q$ will yield another element of the form $Q$, and we included all elements of this form in $\\group{H}$, so this property is satisfied.\n",
184 | "* Associativity continues to hold.\n",
185 | "* The identity $I \\otimes I \\otimes I$ is of the form $Q$, so it is in $\\group{H}$.\n",
186 | "* The inverse of $Q$ is just ${P^{(0)}}^{-1} \\otimes I \\otimes {P^{(2)}}^{-1}$, which is still in the form of $Q$. So all elements of $\\group{H}$ also have their inverse inside $\\group{H}$.\n",
187 | "\n",
188 | "Therefore $\\group{H}$ is a subgroup of $\\group{P}_3$.\n",
189 | "\n",
190 | "*Question:* How many elements are there in $G$?\n",
191 | "\n",
192 | "For finite-sized groups, such as the Pauli group, we don't actually have to check all four axioms. \n",
193 | "\n",
194 | "*Theorem:* Let $\\group{H}$ be a nonempty finite subset of a group $\\group{G}$. Then $\\group{H}$ is a subgroup of $\\group{G}$ if $\\group{H}$ is closed under multiplication.\n",
195 | "\n",
196 | "So, we only have to check the closure axiom, and if it holds so will the other three axioms. This makes the task of checking if a subset is a subgroup much easier.\n",
197 | "\n",
198 | "*Question:* Consider the set\n",
199 | "\\begin{equation}\n",
200 | " G = \\set{I \\otimes I \\otimes I, X \\otimes I \\otimes I, I \\otimes Z \\otimes I, X \\otimes Z \\otimes I}.\n",
201 | "\\end{equation}\n",
202 | "Does this form a subgroup of $\\group{P}_3$?"
203 | ]
204 | },
205 | {
206 | "cell_type": "code",
207 | "execution_count": null,
208 | "id": "9ddf27b2-414f-402f-8acd-05aee5c4889d",
209 | "metadata": {},
210 | "outputs": [],
211 | "source": []
212 | }
213 | ],
214 | "metadata": {
215 | "kernelspec": {
216 | "display_name": "Python 3 (ipykernel)",
217 | "language": "python",
218 | "name": "python3"
219 | },
220 | "language_info": {
221 | "codemirror_mode": {
222 | "name": "ipython",
223 | "version": 3
224 | },
225 | "file_extension": ".py",
226 | "mimetype": "text/x-python",
227 | "name": "python",
228 | "nbconvert_exporter": "python",
229 | "pygments_lexer": "ipython3",
230 | "version": "3.10.12"
231 | },
232 | "widgets": {
233 | "application/vnd.jupyter.widget-state+json": {
234 | "state": {},
235 | "version_major": 2,
236 | "version_minor": 0
237 | }
238 | }
239 | },
240 | "nbformat": 4,
241 | "nbformat_minor": 5
242 | }
243 |
--------------------------------------------------------------------------------
/chapters/qec-intro/shor-code.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "a23fe73c",
6 | "metadata": {
7 | "tags": []
8 | },
9 | "source": [
10 | "# The Shor Code"
11 | ]
12 | },
13 | {
14 | "cell_type": "markdown",
15 | "id": "b00286a1",
16 | "metadata": {
17 | "tags": [
18 | "remove-cell"
19 | ]
20 | },
21 | "source": [
22 | "
Latex macros
\n",
23 | "$ \\newcommand{\\ket}[1]{|#1\\rangle} $\n",
24 | "$ \\newcommand{\\bra}[1]{\\langle #1|} $\n",
25 | "$\\newcommand{\\iu}{{i\\mkern1mu}}$\n",
26 | "$ \\newcommand{\\e}{\\mathrm{e}}$\n",
27 | "$\\newcommand{\\bigo}{\\mathcal{O}}$\n",
28 | "$\\newcommand{\\set}[1]{\\{#1\\}}$\n",
29 | "$\\newcommand{\\oper}[1]{\\mathcal{#1}}$\n",
30 | "$\\newcommand{\\st}[1]{\\mathcal{#1}}$\n",
31 | "$\\newcommand{\\vecs}[1]{\\mathcal{#1}}$\n",
32 | "$\\newcommand{\\group}[1]{\\mathcal{#1}}$\n",
33 | "$\\newcommand{\\union}{\\hspace{0.25em}\\cup\\hspace{0.25em}}$"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "id": "8322accd-289e-45e9-a4c4-a80bfdc8da84",
39 | "metadata": {},
40 | "source": [
41 | "It is now time to construct a code that correct more than one type of error. This will also give us the opportunity to show off a code construction technique called code concatenation.\n",
42 | "\n",
43 | "In code concatenation, we first encode the logical qubit using a particular code into $n$ data qubits. Then, we encode each of then $n$ data qubits individually using the same or another code. To illustrate this, recall that, in the phase-flip code the logical basis states are encoded as\n",
44 | "\\begin{align}\n",
45 | " \\ket{0} \\to \\ket{\\bar{0}} = \\ket{+++}, \\\\\n",
46 | " \\ket{1} \\to \\ket{\\bar{1}} = \\ket{---}. \n",
47 | "\\end{align}\n",
48 | "Now, we are going to take each of the three data qubits and encode them using the bit-flip repetition code. Recall that in the bit-flip repetition code, the plus and minus states encode as\n",
49 | "\\begin{align}\n",
50 | " \\ket{+} &= \\frac{\\ket{0} + \\ket{1}}{\\sqrt{2}} \\to \\ket{\\bar{+}} = \\frac{\\ket{000} + \\ket{111}}{\\sqrt{2}}, \\\\\n",
51 | " \\ket{-} &= \\frac{\\ket{0} - \\ket{1}}{\\sqrt{2}} \\to \\ket{\\bar{-}} = \\frac{\\ket{000} - \\ket{111}}{\\sqrt{2}}.\n",
52 | "\\end{align}\n",
53 | "When we apply this encoding to each of the three qubits in the phase-flip encoding, we obtain\n",
54 | "\\begin{align}\n",
55 | " \\ket{+++} \\to \\ket{\\bar{\\bar{0}}} = \\frac{(\\ket{000} + \\ket{111})(\\ket{000} + \\ket{111})(\\ket{000} + \\ket{111})}{2\\sqrt{2}}, \\\\\n",
56 | " \\ket{---} \\to \\ket{\\bar{\\bar{1}}} = \\frac{(\\ket{000} - \\ket{111})(\\ket{000} - \\ket{111})(\\ket{000} - \\ket{111})}{2\\sqrt{2}}.\n",
57 | "\\end{align}\n",
58 | "Visually, we can draw a tree diagram to show how one qubit is first encoded into three via the phase-flip code and then each of the three qubits is encoded into three more via the bit-flip code.\n",
59 | "\n",
60 | "\n",
61 | "\n",
62 | "\n",
63 | "Here we refer to the phase-flip code as the *outer* code (level 1) and the bit-flip code as the *inner* code (level 2). "
64 | ]
65 | },
66 | {
67 | "cell_type": "markdown",
68 | "id": "6386dc93-f7cc-4b8e-bf9e-905d9c70d9b1",
69 | "metadata": {},
70 | "source": [
71 | "### Encoding\n",
72 | "This combined action that maps one logical qubit to the state of nine qubits as\n",
73 | "\\begin{align}\n",
74 | " \\ket{0} \\to \\ket{\\bar{\\bar{0}}} = \\frac{(\\ket{000} + \\ket{111})(\\ket{000} + \\ket{111})(\\ket{000} + \\ket{111})}{2\\sqrt{2}}, \\\\\n",
75 | " \\ket{1} \\to \\ket{\\bar{\\bar{1}}} = \\frac{(\\ket{000} - \\ket{111})(\\ket{000} - \\ket{111})(\\ket{000} - \\ket{111})}{2\\sqrt{2}},\n",
76 | "\\end{align}\n",
77 | "defines the encoding for the Shor code."
78 | ]
79 | },
80 | {
81 | "cell_type": "markdown",
82 | "id": "374d09c8-b8b5-4c42-8a6b-d90bb54de3f2",
83 | "metadata": {},
84 | "source": [
85 | "#### Task 1\n",
86 | "Create the encoding circuit for the Shor code. You will use nine qubits.\n",
87 | "\n",
88 | "1. Apply the phase-flip repetition code's encoding circuit to qubits 0, 3, 6.\n",
89 | "2. Apply the bit-flip repetition code's encoding circuit to \n",
90 | " * qubits 0,1,2\n",
91 | " * qubits 3,4,5\n",
92 | " * qubits 6,7,8\n",
93 | "\n",
94 | "Then use the `circ.simulate()` method to check if you get the correct output."
95 | ]
96 | },
97 | {
98 | "cell_type": "code",
99 | "execution_count": 1,
100 | "id": "a0300cb0-146f-474f-91c7-29f630d07065",
101 | "metadata": {},
102 | "outputs": [],
103 | "source": [
104 | "import stac\n",
105 | "enc_circ = stac.Circuit.simple(9)\n"
106 | ]
107 | },
108 | {
109 | "cell_type": "markdown",
110 | "id": "5f53a37f-7d46-486e-9d33-3f488d888127",
111 | "metadata": {},
112 | "source": [
113 | "*Question:* Construct the basis states of a nine-qubit code with the bit-flip code as the outer code and the phase-flip code as the inner code."
114 | ]
115 | },
116 | {
117 | "cell_type": "markdown",
118 | "id": "392d27fb-d085-43ce-b72e-a71e39ac04f9",
119 | "metadata": {},
120 | "source": [
121 | "### Errors and decoding\n",
122 | "We will now show that this code can correct every possible single-qubit error. Let's first show that it can correct bit-flip and phase-flip errors on any of the nine data qubits."
123 | ]
124 | },
125 | {
126 | "cell_type": "markdown",
127 | "id": "b8087a90-3af1-4a24-8f2d-affbe4453d17",
128 | "metadata": {},
129 | "source": [
130 | "#### $X$-errors\n",
131 | "Suppose a single-qubit $X$ error occurs, on say the fourth qubit.\n",
132 | "\\begin{align}\n",
133 | " X_4\\ket{\\bar{\\bar{0}}} = \\frac{(\\ket{000} + \\ket{111})(\\ket{010} + \\ket{101})(\\ket{000} + \\ket{111})}{2\\sqrt{2}}, \\\\\n",
134 | " X_4\\ket{\\bar{\\bar{1}}} = \\frac{(\\ket{000} - \\ket{111})(\\ket{010} - \\ket{101})(\\ket{000} - \\ket{111})}{2\\sqrt{2}},\n",
135 | "\\end{align}\n",
136 | "How can one detect that this occur occured? We will do the following syndrome measurements, in the style of the bit-flip code.\n",
137 | "\n",
138 | "* Compare the values of qubits 0,1,2\n",
139 | "* Compare the values of qubits 3,4,5\n",
140 | "* Compare the values of qubits 6,7,8\n",
141 | "\n",
142 | "In this example, comparing the value of the 3rd qubit with the 4th, and the 4th with the 5th will show that the 3rd qubit has a different value. The error can be fixed as before."
143 | ]
144 | },
145 | {
146 | "cell_type": "markdown",
147 | "id": "cad1bac7-ffc9-49da-9276-1a90e2720765",
148 | "metadata": {},
149 | "source": [
150 | "#### Task 2\n",
151 | "Create the syndrome measurement circuit for detecting $X$ errors in the Shor code."
152 | ]
153 | },
154 | {
155 | "cell_type": "code",
156 | "execution_count": null,
157 | "id": "42a4395f-edac-4381-8ecc-29560b826590",
158 | "metadata": {},
159 | "outputs": [],
160 | "source": [
161 | "# specify how many qubits you will need\n",
162 | "sync_circ_x = stac.Circuit.simple()\n"
163 | ]
164 | },
165 | {
166 | "cell_type": "markdown",
167 | "id": "5e625df3-9e60-4e3d-99a1-dda16b525823",
168 | "metadata": {},
169 | "source": [
170 | "#### $Z$-errors\n",
171 | "The case for a single-qubit $Z$ errors is slightly more difficult to see. Note, for instance, that if a $Z$ error occurs on any one of the first three qubits, the sign in the first block will change. For $i = 0,1,2$,\n",
172 | "\\begin{align}\n",
173 | " Z_i\\ket{\\bar{\\bar{0}}} = \\frac{(\\ket{000} - \\ket{111})(\\ket{000} + \\ket{111})(\\ket{000} + \\ket{111})}{2\\sqrt{2}}, \\\\\n",
174 | " Z_i\\ket{\\bar{\\bar{1}}} = \\frac{(\\ket{000} + \\ket{111})(\\ket{000} - \\ket{111})(\\ket{000} - \\ket{111})}{2\\sqrt{2}}.\n",
175 | "\\end{align}\n",
176 | "A different way of seeing it is at the outer code level, where the encoding is\n",
177 | "\\begin{align}\n",
178 | " \\ket{\\bar{\\bar{0}}} = \\ket{\\bar{+}}\\ket{\\bar{+}}\\ket{\\bar{+}}, \\\\\n",
179 | " \\ket{\\bar{\\bar{1}}} = \\ket{\\bar{-}}\\ket{\\bar{-}}\\ket{\\bar{-}}.\n",
180 | "\\end{align}\n",
181 | "Then the action of $Z_i$ for $i=0,1,2$ is\n",
182 | "\\begin{align}\n",
183 | " Z_i\\ket{\\bar{\\bar{0}}} = \\ket{\\bar{-}}\\ket{\\bar{+}}\\ket{\\bar{+}}, \\\\\n",
184 | " Z_i\\ket{\\bar{\\bar{1}}} = \\ket{\\bar{+}}\\ket{\\bar{-}}\\ket{\\bar{-}}.\n",
185 | "\\end{align}\n",
186 | "At this level, it is quite easy to see the error-detecting strategy. Apply the phase-flip code's error-detection procedure to the three encoded qubits (at the outer level). We will discuss how one can do this later."
187 | ]
188 | },
189 | {
190 | "cell_type": "markdown",
191 | "id": "cf4edd63-222e-49fb-a5d3-30754639c1d5",
192 | "metadata": {},
193 | "source": [
194 | "#### $Y$-errors\n",
195 | "Right now, we want to show that the Shor code can detect and correct errors beyond just $X$ and $Z$ errors. One such error is the $Y$ error, which is just $Y = \\iu ZX$, i.e. a combined bit-flip and phase-flip error. For instance, $Y_4$ will result in the corrupted basis states\n",
196 | "\\begin{align}\n",
197 | " Y_4\\ket{\\bar{\\bar{0}}} = \\iu \\frac{(\\ket{000} + \\ket{111})(\\ket{010} - \\ket{101})(\\ket{000} + \\ket{111})}{2\\sqrt{2}}, \\\\\n",
198 | " Y_4\\ket{\\bar{\\bar{1}}} = \\iu \\frac{(\\ket{000} - \\ket{111})(\\ket{010} + \\ket{101})(\\ket{000} - \\ket{111})}{2\\sqrt{2}},\n",
199 | "\\end{align}\n",
200 | "\n",
201 | "\n",
202 | "This error is detected at both the $X$ detection stage, and the $Z$ detection stage, and corrected at both as well. First, we detect $X$ errors, find that the fourth is flipped and fix it. Then we detect $Z$ errors and determine that the middle block has experienced an error and fix it.\n",
203 | "\n",
204 | "\n",
205 | "Hence, we have shown up till now that the Shor code can correct all errors in the set,\n",
206 | "\\begin{equation}\n",
207 | " \\st{E} = I \\union \\set{X_i}_i \\union \\set{Y_i}_i \\union \\set{Z_i}_i, \\quad i = 0,\\dots,8.\n",
208 | "\\end{equation}"
209 | ]
210 | },
211 | {
212 | "cell_type": "markdown",
213 | "id": "8b78b2d0-c40a-4227-b610-797bf0ec2d6c",
214 | "metadata": {},
215 | "source": [
216 | "#### More general errors\n",
217 | "We showed before that the bit-flip code could also correct for $X$ rotation errors. By the same arguments of linearity and collapse, the Shor code (and most quantum codes we will encounter) can correct any error which is of the form,\n",
218 | "\\begin{equation}\n",
219 | " E_i = e_0 I + e_1X_i + e_2 Y_i + e_3Z_i.\n",
220 | "\\end{equation}"
221 | ]
222 | },
223 | {
224 | "cell_type": "markdown",
225 | "id": "5473ae6f-2929-4064-b7da-64aaaed2054d",
226 | "metadata": {},
227 | "source": [
228 | "#### Task 3 (On paper)\n",
229 | "Apply $E_i$ to $\\ket{\\bar\\psi}$ and then show that the error-detection circuit will collapse the state to just one of the possible errors."
230 | ]
231 | },
232 | {
233 | "cell_type": "markdown",
234 | "id": "4e595511-3ec1-43d8-b727-8bcac57cfd27",
235 | "metadata": {},
236 | "source": [
237 | "*Question:* Suppose the state $\\ket{\\bar\\psi}$ encoded by the Shor code undergoes the two-qubit error $Z_1Z_2$. What is the impact of this error? This is a phenomena not seen in classical codes.\n",
238 | "\n",
239 | "*Question:* The Shor can correct some (but not all) two-qubit errors as well. Characterize all these errors. Is there any three-qubit error that the Shor code can handle?"
240 | ]
241 | },
242 | {
243 | "cell_type": "code",
244 | "execution_count": null,
245 | "id": "45189efb-af41-4043-9716-d439517ca880",
246 | "metadata": {},
247 | "outputs": [],
248 | "source": []
249 | }
250 | ],
251 | "metadata": {
252 | "kernelspec": {
253 | "display_name": "Python 3 (ipykernel)",
254 | "language": "python",
255 | "name": "python3"
256 | },
257 | "language_info": {
258 | "codemirror_mode": {
259 | "name": "ipython",
260 | "version": 3
261 | },
262 | "file_extension": ".py",
263 | "mimetype": "text/x-python",
264 | "name": "python",
265 | "nbconvert_exporter": "python",
266 | "pygments_lexer": "ipython3",
267 | "version": "3.10.8"
268 | },
269 | "widgets": {
270 | "application/vnd.jupyter.widget-state+json": {
271 | "state": {},
272 | "version_major": 2,
273 | "version_minor": 0
274 | }
275 | }
276 | },
277 | "nbformat": 4,
278 | "nbformat_minor": 5
279 | }
280 |
--------------------------------------------------------------------------------
/static/script/runmode-standalone.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | function copyObj(obj, target, overwrite) {
5 | if (!target) { target = {}; }
6 | for (var prop in obj)
7 | { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
8 | { target[prop] = obj[prop]; } }
9 | return target
10 | }
11 |
12 | // Counts the column offset in a string, taking tabs into account.
13 | // Used mostly to find indentation.
14 | function countColumn(string, end, tabSize, startIndex, startValue) {
15 | if (end == null) {
16 | end = string.search(/[^\s\u00a0]/);
17 | if (end == -1) { end = string.length; }
18 | }
19 | for (var i = startIndex || 0, n = startValue || 0;;) {
20 | var nextTab = string.indexOf("\t", i);
21 | if (nextTab < 0 || nextTab >= end)
22 | { return n + (end - i) }
23 | n += nextTab - i;
24 | n += tabSize - (n % tabSize);
25 | i = nextTab + 1;
26 | }
27 | }
28 |
29 | function nothing() {}
30 |
31 | function createObj(base, props) {
32 | var inst;
33 | if (Object.create) {
34 | inst = Object.create(base);
35 | } else {
36 | nothing.prototype = base;
37 | inst = new nothing();
38 | }
39 | if (props) { copyObj(props, inst); }
40 | return inst
41 | }
42 |
43 | // STRING STREAM
44 |
45 | // Fed to the mode parsers, provides helper functions to make
46 | // parsers more succinct.
47 |
48 | var StringStream = function(string, tabSize, lineOracle) {
49 | this.pos = this.start = 0;
50 | this.string = string;
51 | this.tabSize = tabSize || 8;
52 | this.lastColumnPos = this.lastColumnValue = 0;
53 | this.lineStart = 0;
54 | this.lineOracle = lineOracle;
55 | };
56 |
57 | StringStream.prototype.eol = function () {return this.pos >= this.string.length};
58 | StringStream.prototype.sol = function () {return this.pos == this.lineStart};
59 | StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
60 | StringStream.prototype.next = function () {
61 | if (this.pos < this.string.length)
62 | { return this.string.charAt(this.pos++) }
63 | };
64 | StringStream.prototype.eat = function (match) {
65 | var ch = this.string.charAt(this.pos);
66 | var ok;
67 | if (typeof match == "string") { ok = ch == match; }
68 | else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
69 | if (ok) {++this.pos; return ch}
70 | };
71 | StringStream.prototype.eatWhile = function (match) {
72 | var start = this.pos;
73 | while (this.eat(match)){}
74 | return this.pos > start
75 | };
76 | StringStream.prototype.eatSpace = function () {
77 | var start = this.pos;
78 | while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; }
79 | return this.pos > start
80 | };
81 | StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
82 | StringStream.prototype.skipTo = function (ch) {
83 | var found = this.string.indexOf(ch, this.pos);
84 | if (found > -1) {this.pos = found; return true}
85 | };
86 | StringStream.prototype.backUp = function (n) {this.pos -= n;};
87 | StringStream.prototype.column = function () {
88 | if (this.lastColumnPos < this.start) {
89 | this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
90 | this.lastColumnPos = this.start;
91 | }
92 | return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
93 | };
94 | StringStream.prototype.indentation = function () {
95 | return countColumn(this.string, null, this.tabSize) -
96 | (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
97 | };
98 | StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
99 | if (typeof pattern == "string") {
100 | var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
101 | var substr = this.string.substr(this.pos, pattern.length);
102 | if (cased(substr) == cased(pattern)) {
103 | if (consume !== false) { this.pos += pattern.length; }
104 | return true
105 | }
106 | } else {
107 | var match = this.string.slice(this.pos).match(pattern);
108 | if (match && match.index > 0) { return null }
109 | if (match && consume !== false) { this.pos += match[0].length; }
110 | return match
111 | }
112 | };
113 | StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
114 | StringStream.prototype.hideFirstChars = function (n, inner) {
115 | this.lineStart += n;
116 | try { return inner() }
117 | finally { this.lineStart -= n; }
118 | };
119 | StringStream.prototype.lookAhead = function (n) {
120 | var oracle = this.lineOracle;
121 | return oracle && oracle.lookAhead(n)
122 | };
123 | StringStream.prototype.baseToken = function () {
124 | var oracle = this.lineOracle;
125 | return oracle && oracle.baseToken(this.pos)
126 | };
127 |
128 | // Known modes, by name and by MIME
129 | var modes = {}, mimeModes = {};
130 |
131 | // Extra arguments are stored as the mode's dependencies, which is
132 | // used by (legacy) mechanisms like loadmode.js to automatically
133 | // load a mode. (Preferred mechanism is the require/define calls.)
134 | function defineMode(name, mode) {
135 | if (arguments.length > 2)
136 | { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
137 | modes[name] = mode;
138 | }
139 |
140 | function defineMIME(mime, spec) {
141 | mimeModes[mime] = spec;
142 | }
143 |
144 | // Given a MIME type, a {name, ...options} config object, or a name
145 | // string, return a mode config object.
146 | function resolveMode(spec) {
147 | if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
148 | spec = mimeModes[spec];
149 | } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
150 | var found = mimeModes[spec.name];
151 | if (typeof found == "string") { found = {name: found}; }
152 | spec = createObj(found, spec);
153 | spec.name = found.name;
154 | } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
155 | return resolveMode("application/xml")
156 | } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
157 | return resolveMode("application/json")
158 | }
159 | if (typeof spec == "string") { return {name: spec} }
160 | else { return spec || {name: "null"} }
161 | }
162 |
163 | // Given a mode spec (anything that resolveMode accepts), find and
164 | // initialize an actual mode object.
165 | function getMode(options, spec) {
166 | spec = resolveMode(spec);
167 | var mfactory = modes[spec.name];
168 | if (!mfactory) { return getMode(options, "text/plain") }
169 | var modeObj = mfactory(options, spec);
170 | if (modeExtensions.hasOwnProperty(spec.name)) {
171 | var exts = modeExtensions[spec.name];
172 | for (var prop in exts) {
173 | if (!exts.hasOwnProperty(prop)) { continue }
174 | if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
175 | modeObj[prop] = exts[prop];
176 | }
177 | }
178 | modeObj.name = spec.name;
179 | if (spec.helperType) { modeObj.helperType = spec.helperType; }
180 | if (spec.modeProps) { for (var prop$1 in spec.modeProps)
181 | { modeObj[prop$1] = spec.modeProps[prop$1]; } }
182 |
183 | return modeObj
184 | }
185 |
186 | // This can be used to attach properties to mode objects from
187 | // outside the actual mode definition.
188 | var modeExtensions = {};
189 | function extendMode(mode, properties) {
190 | var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
191 | copyObj(properties, exts);
192 | }
193 |
194 | function copyState(mode, state) {
195 | if (state === true) { return state }
196 | if (mode.copyState) { return mode.copyState(state) }
197 | var nstate = {};
198 | for (var n in state) {
199 | var val = state[n];
200 | if (val instanceof Array) { val = val.concat([]); }
201 | nstate[n] = val;
202 | }
203 | return nstate
204 | }
205 |
206 | // Given a mode and a state (for that mode), find the inner mode and
207 | // state at the position that the state refers to.
208 | function innerMode(mode, state) {
209 | var info;
210 | while (mode.innerMode) {
211 | info = mode.innerMode(state);
212 | if (!info || info.mode == mode) { break }
213 | state = info.state;
214 | mode = info.mode;
215 | }
216 | return info || {mode: mode, state: state}
217 | }
218 |
219 | function startState(mode, a1, a2) {
220 | return mode.startState ? mode.startState(a1, a2) : true
221 | }
222 |
223 | var modeMethods = {
224 | __proto__: null,
225 | modes: modes,
226 | mimeModes: mimeModes,
227 | defineMode: defineMode,
228 | defineMIME: defineMIME,
229 | resolveMode: resolveMode,
230 | getMode: getMode,
231 | modeExtensions: modeExtensions,
232 | extendMode: extendMode,
233 | copyState: copyState,
234 | innerMode: innerMode,
235 | startState: startState
236 | };
237 |
238 | // declare global: globalThis, CodeMirror
239 |
240 | // Create a minimal CodeMirror needed to use runMode, and assign to root.
241 | var root = typeof globalThis !== 'undefined' ? globalThis : window;
242 | root.CodeMirror = {};
243 |
244 | // Copy StringStream and mode methods into CodeMirror object.
245 | CodeMirror.StringStream = StringStream;
246 | for (var exported in modeMethods) { CodeMirror[exported] = modeMethods[exported]; }
247 |
248 | // Minimal default mode.
249 | CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
250 | CodeMirror.defineMIME("text/plain", "null");
251 |
252 | CodeMirror.registerHelper = CodeMirror.registerGlobalHelper = Math.min;
253 | CodeMirror.splitLines = function(string) { return string.split(/\r?\n|\r/) };
254 | CodeMirror.countColumn = countColumn;
255 |
256 | CodeMirror.defaults = { indentUnit: 2 };
257 |
258 | // CodeMirror, copyright (c) by Marijn Haverbeke and others
259 | // Distributed under an MIT license: https://codemirror.net/5/LICENSE
260 |
261 | (function(mod) {
262 | if (typeof exports == "object" && typeof module == "object") // CommonJS
263 | { mod(require("../../lib/codemirror")); }
264 | else if (typeof define == "function" && define.amd) // AMD
265 | { define(["../../lib/codemirror"], mod); }
266 | else // Plain browser env
267 | { mod(CodeMirror); }
268 | })(function(CodeMirror) {
269 |
270 | CodeMirror.runMode = function(string, modespec, callback, options) {
271 | var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);
272 | var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize;
273 |
274 | // Create a tokenizing callback function if passed-in callback is a DOM element.
275 | if (callback.appendChild) {
276 | var ie = /MSIE \d/.test(navigator.userAgent);
277 | var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
278 | var node = callback, col = 0;
279 | node.textContent = "";
280 | callback = function(text, style) {
281 | if (text == "\n") {
282 | // Emitting LF or CRLF on IE8 or earlier results in an incorrect display.
283 | // Emitting a carriage return makes everything ok.
284 | node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text));
285 | col = 0;
286 | return;
287 | }
288 | var content = "";
289 | // replace tabs
290 | for (var pos = 0;;) {
291 | var idx = text.indexOf("\t", pos);
292 | if (idx == -1) {
293 | content += text.slice(pos);
294 | col += text.length - pos;
295 | break;
296 | } else {
297 | col += idx - pos;
298 | content += text.slice(pos, idx);
299 | var size = tabSize - col % tabSize;
300 | col += size;
301 | for (var i = 0; i < size; ++i) { content += " "; }
302 | pos = idx + 1;
303 | }
304 | }
305 | // Create a node with token style and append it to the callback DOM element.
306 | if (style) {
307 | var sp = node.appendChild(document.createElement("span"));
308 | sp.className = "cm-" + style.replace(/ +/g, " cm-");
309 | sp.appendChild(document.createTextNode(content));
310 | } else {
311 | node.appendChild(document.createTextNode(content));
312 | }
313 | };
314 | }
315 |
316 | var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode);
317 | for (var i = 0, e = lines.length; i < e; ++i) {
318 | if (i) { callback("\n"); }
319 | var stream = new CodeMirror.StringStream(lines[i], null, {
320 | lookAhead: function(n) { return lines[i + n] },
321 | baseToken: function() {}
322 | });
323 | if (!stream.string && mode.blankLine) { mode.blankLine(state); }
324 | while (!stream.eol()) {
325 | var style = mode.token(stream, state);
326 | callback(stream.current(), style, i, stream.start, state, mode);
327 | stream.start = stream.pos;
328 | }
329 | }
330 | };
331 |
332 | });
333 |
334 | }());
335 |
--------------------------------------------------------------------------------
/chapters/stabilizer-codes/stabilizer-codes.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "a23fe73c",
6 | "metadata": {
7 | "tags": []
8 | },
9 | "source": [
10 | "# Stabilizer codes"
11 | ]
12 | },
13 | {
14 | "cell_type": "markdown",
15 | "id": "26e5cc63-b229-46e2-9d6f-afcbdf917b90",
16 | "metadata": {
17 | "tags": [
18 | "remove-cell"
19 | ]
20 | },
21 | "source": [
22 | "
Latex macros
\n",
23 | "$ \\newcommand{\\ket}[1]{|#1\\rangle} $\n",
24 | "$ \\newcommand{\\bra}[1]{\\langle #1|} $\n",
25 | "$\\newcommand{\\iu}{{i\\mkern1mu}}$\n",
26 | "$ \\newcommand{\\e}{\\mathrm{e}}$\n",
27 | "$\\newcommand{\\bigo}{\\mathcal{O}}$\n",
28 | "$\\newcommand{\\set}[1]{\\{#1\\}}$\n",
29 | "$\\newcommand{\\oper}[1]{\\mathcal{#1}}$\n",
30 | "$\\newcommand{\\st}[1]{\\mathcal{#1}}$\n",
31 | "$\\newcommand{\\vecs}[1]{\\mathcal{#1}}$\n",
32 | "$\\newcommand{\\group}[1]{\\mathcal{#1}}$\n",
33 | "$\\newcommand{\\union}{\\hspace{0.25em}\\cup\\hspace{0.25em}}$"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "id": "009bcaad-14c6-44f4-b2d9-677b62e7ce95",
39 | "metadata": {},
40 | "source": [
41 | "We now have all the ingredients to define stabilizer codes. To define a error-correcting code we need four ingredients\n",
42 | "\n",
43 | "* Encoding: A method to encode each message into a codewords. For quantum codes, we need to map each basis element of the message space to a basis element of the codespace.\n",
44 | "* Syndrome measurements: A method to do syndrome measurements on the corrupted codewords, to extract information about the error.\n",
45 | "* Decoding: A method to transfrom the corrected codewords back into a message.\n",
46 | "* Logical gates: Methods to implement logical gates directly on an encoded qubit.\n",
47 | "\n",
48 | "Stabilizer codes are defined with respect to a stabilizer group $S$. We label the stabilizer code based on $S$ as $C(S)$. Then, the ingredients for $C(S)$ are\n",
49 | "\n",
50 | "* Encoding: The simultaneous stabilizer states of the stabilizer group $S$ define the basis elements of the codespace. An encoding circuit can be systematically constructed using the generators of $S$; a procedure we will show later.\n",
51 | "* Syndrome measurements: All generators of $S$ are measured one by one. The results of the measurement indicate which error has occured. \n",
52 | "* Decoding: Decoding is also based on $S$.\n",
53 | "* Logical gates: These can also be constructed from $S$."
54 | ]
55 | },
56 | {
57 | "cell_type": "markdown",
58 | "id": "40b5c3aa-ecd3-48a8-8756-74cf1a939b68",
59 | "metadata": {},
60 | "source": [
61 | "## Repetition code for bit-flips\n",
62 | "Let's see how all this applies to the repetition code. The repetition code is based on the group $S$ generated by $P_1 = Z \\otimes Z \\otimes I$ and $P_2 = I \\otimes Z \\otimes Z$.\n",
63 | "\n",
64 | "* Encoding: We saw in [stabilizer groups](../stabilizer-codes/stabilizer-groups.ipynb) that this group simultaneously stabilizes the states $\\set{\\ket{000},\\ket{111}}$. In [quantum repetition code for bit-flips](../qec-intro/quantum-repetition-code-for-bit-flips.ipynb) we presented an ad-hoc encoding circuit for this code as\n",
65 | "\n",
66 | " \n",
67 | " \n",
68 | " We will see later on how to construct this from $S$.\n",
69 | " \n",
70 | "* Syndrome measurements: We saw in [syndrome measurements for stabilizer codes](../stabilizer-codes/syndrome-measurements.ipynb) this is achieved using the following circuit.\n",
71 | " \n",
72 | " \n",
73 | " \n",
74 | " Here we are measuring the two generators $P_1$ and $P_2$. \n",
75 | "\n",
76 | "* Decoding: In [quantum repetition code for bit-flips](../qec-intro/quantum-repetition-code-for-bit-flips.ipynb) we noted that the encoding circuit for the repetition code is self-inverse, and can also be used for decoding. We will present a different procedure later on.\n"
77 | ]
78 | },
79 | {
80 | "cell_type": "markdown",
81 | "id": "cb2de5e9-40e9-4790-a302-c0286c3e170f",
82 | "metadata": {},
83 | "source": [
84 | "#### Task 1 (On paper)\n",
85 | "Show that the repetition code for phase-flips is also a stabilizer code."
86 | ]
87 | },
88 | {
89 | "cell_type": "markdown",
90 | "id": "41d0ddcb-33ec-4055-9173-2456da35c41a",
91 | "metadata": {},
92 | "source": [
93 | "### Which errors are correctable by a stabilizer code\n",
94 | "Up till now, we have discussed three codes, and discovered that they all correct different sets of errors\n",
95 | "\n",
96 | "* The repetition code for bit-flips corrects all single-qubit $X$ errors.\n",
97 | "* The repetition code for phase-flips corrects all single-qubit $Z$ errors.\n",
98 | "* The Shor code corrects all single-qubit errors.\n",
99 | "\n",
100 | "Is there a rule that tells us which errors are correctable by the code? Indeed there is.\n",
101 | "\n",
102 | "**Theorem:** Let $S$ be a stabilizer group, with generators $\\set{g_i}$, and let $C(S)$ be the associated stabilizer code. Then $C(S)$ detects an error $E \\in \\st{P}_n$ if it anti-commutes with at least one generator $g_k$ of $S$.\n",
103 | "\n",
104 | "Let's explore why this theorem is correct. To detect errors, we measure all the generators of the stabilizer group. Let's concentrate on the measurement of just one generator $g_k$. There are three possibilities.\n",
105 | "\n",
106 | "1. No error has occured and the data qubits are in state $\\ket{\\bar\\psi}$.\n",
107 | "2. Error $E$ has occured, and the data qubits are in state $E\\ket{\\bar\\psi}$. The two sub-possibilities are\n",
108 | " + (a) $E$ commutes with $g_k$.\n",
109 | " + (b) $E$ anti-commutes with $g_k$.\n",
110 | " \n",
111 | "Let's analyze each possibility one by one. We will need to recall from [syndrome measurements, an alternate view](../qec-intro/measurements.ipynb) that if we measure $g_k$ on some state $\\ket{\\phi}$, then at the end of the measurement circuit, the qubits will be in state\n",
112 | "\\begin{equation}\n",
113 | "\\ket{\\Phi} = (\\ket{\\phi} + g_k\\ket{\\phi})\\ket{0} + (\\ket{\\phi} - g_k\\ket{\\phi})\\ket{1},\n",
114 | "\\end{equation}\n",
115 | "where as per the possibilities above either $\\ket{\\phi}$ is the uncorrupted state $\\ket{\\bar\\psi}$ or the corrupted state $E\\ket{\\bar\\psi}\\}$.\n",
116 | "\n",
117 | "#### Possibility 1: no error.\n",
118 | "If there is no error, then $\\ket{\\phi} = \\ket{\\bar\\psi}$ and so $\\ket{\\Phi} = \\ket{\\bar\\psi}\\ket{0}$. Consequently measuring the ancilla will yield 0. \n",
119 | "\n",
120 | "#### Possibility 2 (a) $E$ commutes with $g_k$\n",
121 | "Now, $\\ket{\\phi} = E\\ket{\\bar\\psi}$. First note that\n",
122 | "\\begin{equation}\n",
123 | "g\\ket{\\phi} = g(E\\ket{\\bar\\psi}) = Eg\\ket{\\bar\\psi} = E\\ket{\\bar\\psi} = \\ket{\\phi}.\n",
124 | "\\end{equation}\n",
125 | "Hence, $\\ket{\\Phi} = \\ket{\\phi}\\ket{0}$. Again, we discover that the ancilla will measure 0.\n",
126 | "\n",
127 | "#### Possibility 2 (b) $E$ anti-commutes with $g_k$\n",
128 | "Now, $\\ket{\\phi} = E\\ket{\\bar\\psi}$. First note that\n",
129 | "\\begin{equation}\n",
130 | "g\\ket{\\phi} = g(E\\ket{\\bar\\psi}) = -Eg\\ket{\\bar\\psi} = -(E\\ket{\\bar\\psi}) = -\\ket{\\phi}.\n",
131 | "\\end{equation}\n",
132 | "Hence, $\\ket{\\Phi} = \\ket{\\phi}\\ket{1}$. This time, the ancilla will measure 1.\n",
133 | "\n",
134 | "What we have discovered is that if and only if $E$ and $g_k$ anti-commute does the ancilla trigger. Therefore, it is quite easy to see that if at least one of $E$ and $\\set{g_i}$ anti-commute, the code will detect the error. \n",
135 | "\n",
136 | "#### A fourth possibility\n",
137 | "Note, that any code is also immune to errors that don't change the state. For instance, for the Shor code, we saw that errors like $Z_0Z_1$ leave $\\ket{\\bar\\psi}$ unchanged."
138 | ]
139 | },
140 | {
141 | "cell_type": "markdown",
142 | "id": "a023fffa-89dc-4eaf-b5b2-6f246a6ee6c1",
143 | "metadata": {},
144 | "source": [
145 | "#### Task 2 (On paper)\n",
146 | "Determine the commutation relations of single-qubit $X$ errors with the generators of the repetition code for bit-flips. Put 0 in the table if they commute, and 1 if they anti-commute.\n",
147 | "\n",
148 | "| Error \\ Generator | $Z \\otimes Z \\otimes I$ | $I \\otimes Z \\otimes Z$ |\n",
149 | "|:-----------------------:|:-----------------------:|:-----------------------:|\n",
150 | "| $I \\otimes I \\otimes I$ | | |\n",
151 | "| $X \\otimes I \\otimes I$ | | |\n",
152 | "| $I \\otimes X \\otimes I$ | | |\n",
153 | "| $I \\otimes I \\otimes X$ | | |\n",
154 | "\n",
155 | "Compare with the syndrome table that we presented in [quantum repetition code for bit-flips](../qec-intro/quantum-repetition-code-for-bit-flips.ipynb)."
156 | ]
157 | },
158 | {
159 | "cell_type": "markdown",
160 | "id": "89c0c76f-951d-4242-90fc-817999a8160b",
161 | "metadata": {},
162 | "source": [
163 | "#### Task 3 (On paper)\n",
164 | "Determine the commutation relations of single-qubit $Z$ errors with the generators of the repetition code for phase-flips. Put 0 in the table if they commute, and 1 if they anti-commute.\n",
165 | "\n",
166 | "| Error \\ Generator | $X \\otimes X \\otimes I$ | $I \\otimes X \\otimes X$ |\n",
167 | "|:-----------------------:|:-----------------------:|:-----------------------:|\n",
168 | "| $I \\otimes I \\otimes I$ | | |\n",
169 | "| $Z \\otimes I \\otimes I$ | | |\n",
170 | "| $I \\otimes Z \\otimes I$ | | |\n",
171 | "| $I \\otimes I \\otimes Z$ | | |\n",
172 | "\n",
173 | "Compare with the syndrome table that we presented in [quantum repetition code for phase-flips](../qec-intro/quantum-repetition-code-for-phase-flips.ipynb)\n"
174 | ]
175 | },
176 | {
177 | "cell_type": "markdown",
178 | "id": "d2a693f5-ac97-4034-a205-8fe656935aa8",
179 | "metadata": {},
180 | "source": [
181 | "One important lesson you can take away from the above is that to detect $X$ type errors, we need $Z$ type generators. And to detect $Z$-type errors, we need $X$-type generators. This observation will be useful, when we construct more stabilizer codes."
182 | ]
183 | },
184 | {
185 | "cell_type": "markdown",
186 | "id": "3ddc9cab-18d1-442d-bc22-bb291c4bddea",
187 | "metadata": {},
188 | "source": [
189 | "## Size of a stabilizer code\n",
190 | "Recall the sizes of the codes we have seen\n",
191 | "\n",
192 | "| Code | No. of logical bits/qubits | No. of physical bits/qubits |\n",
193 | "|:--------------------------:|:--------------------------:|:---------------------------:|\n",
194 | "| classical repetition code | 1 | 3 |\n",
195 | "| Hamming code | 4 | 7 |\n",
196 | "| quantum repetition codes | 1 | 3 |\n",
197 | "| Shor code | 1 | 9 |\n",
198 | "\n",
199 | "If we define a stabilizer code $C(S)$ using some stabilizer group $S$, how many logical qubits are encoded into how many physical qubits? For this the rules are as follows.\n",
200 | "\n",
201 | "* If $S$ is a subgroup of $\\group{P}_n$, then $C(S)$ has $n$ physical qubits.\n",
202 | "* If $S$ has $m$ generators, then $C(S)$ has $k=n-m$ logical qubits. \n",
203 | "\n",
204 | "These three quantities $n,m,k$ will be very important in our further analysis.\n",
205 | "\n",
206 | "> A quantum stabilizer code with distance $d$ is labeled as $[[n,k,d]]$. The double brackets remind us that it is a quantum code."
207 | ]
208 | },
209 | {
210 | "cell_type": "markdown",
211 | "id": "39bd098a-20af-425e-be19-230a8d492247",
212 | "metadata": {},
213 | "source": [
214 | "## Distance of a stabilizer code\n",
215 | "The distance of the code determines how many errors it can correct. A code with distance $d$ can correct $\\lfloor (d-1)/2 \\rfloor$ errors (the $\\lfloor * \\rfloor$ indicates the floor function).\n",
216 | "\n",
217 | "The simplest way to find the distance is as follows.\n",
218 | "\n",
219 | "> The **weight** of a Pauli operator is the number of non-identity operators in it.\n",
220 | "\n",
221 | "**Example:** $X \\otimes I \\otimes Z \\otimes I$ has a weight of two.\n",
222 | "\n",
223 | "> The **distance** of stabilizer code $C(S)$ is the minimum weight operator in $\\group{P}_n$ that is not in $S$ but still commutes with all members of $S$.\n",
224 | "\n",
225 | "**Example:** For the repetition code for bit-flips $S$ was generated by $P_1 = Z \\otimes Z \\otimes I$ and $P_2 = I \\otimes Z \\otimes Z$. Now, the operator $Q = Z \\otimes I \\otimes I$ commutes with both $P_1$ and $P_2$ but is not in $S$. $Q$ has weight 1, so the code has distance $d=1$.\n",
226 | "\n",
227 | "Since $\\lfloor (1-1)/2 \\rfloor = 0$, this indicates that there are one qubit errors that the repetition code cannot correct. Obviously, these are the single-qubit phase errors."
228 | ]
229 | },
230 | {
231 | "cell_type": "code",
232 | "execution_count": null,
233 | "id": "5a00d3ce-c25a-47ed-9d54-65a91ca59e21",
234 | "metadata": {},
235 | "outputs": [],
236 | "source": []
237 | }
238 | ],
239 | "metadata": {
240 | "kernelspec": {
241 | "display_name": "Python 3 (ipykernel)",
242 | "language": "python",
243 | "name": "python3"
244 | },
245 | "language_info": {
246 | "codemirror_mode": {
247 | "name": "ipython",
248 | "version": 3
249 | },
250 | "file_extension": ".py",
251 | "mimetype": "text/x-python",
252 | "name": "python",
253 | "nbconvert_exporter": "python",
254 | "pygments_lexer": "ipython3",
255 | "version": "3.10.8"
256 | },
257 | "widgets": {
258 | "application/vnd.jupyter.widget-state+json": {
259 | "state": {},
260 | "version_major": 2,
261 | "version_minor": 0
262 | }
263 | }
264 | },
265 | "nbformat": 4,
266 | "nbformat_minor": 5
267 | }
268 |
--------------------------------------------------------------------------------