├── 1_Bits.ipynb ├── 2_Single-Qubit-Gates.ipynb ├── 3_Two-Qubit-Gates.ipynb ├── 4_Beyond-Clifford.ipynb ├── 5_Bell-Test.ipynb ├── How-to-Use.ipynb ├── Index.ipynb ├── LICENSE ├── QusDev-Surface-Codes.ipynb ├── README.md ├── engine.py └── environment.yml /1_Bits.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Hello Quantum - Qiskit Edition\n", 8 | "## Chapter 1: Beginning with bits" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "This is a Jupyter notebook, in which you will run some puzzles and learn about quantum computing. Don't worry if you've never used a Jupyter notebook before. It just means you'll see lots of grey boxes with code in, like the one below. These are known as cells." 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "print(\"Hello! I'm a code cell\")" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": {}, 30 | "source": [ 31 | "You'll need to run the cells to use the tutorial. To do this, click on the cell and press **Shift-Enter**. Get started by doing this for the cell below (it will take a second or two to run)." 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "print('Set up started...')\n", 41 | "%matplotlib notebook\n", 42 | "import engine\n", 43 | "print('Set up complete!')" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": {}, 49 | "source": [ 50 | "The rest of the cells in this notebook will create puzzles for you to solve. So just go and run the cells for the puzzles you want to do." 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "metadata": {}, 56 | "source": [ 57 | "# Level 1\n", 58 | "\n", 59 | "### Intro\n", 60 | "* Normal computers are made of bits, and quantum computers are made of qubits.\n", 61 | "* Qubits are basically an upgraded version of bits, so let's make sure we understand basic bit-based programming first.\n", 62 | "* The defining feature of a bit is that it has two possible output values.\n", 63 | "* These are often called `1` and `0`, though we'll also be thinking of them as _on_ and _off_.\n", 64 | "* We use bits represent and process information, but for this we need lots of them,\n", 65 | "\n", 66 | "\n", 67 | "* To help you understand how to use bits, we'll give you one to play with.\n", 68 | "* The simplest thing you can do to a bit (other than leave it alone) is tghe `NOT` operation.\n", 69 | "* Try it out and see what happens.\n", 70 | "\n", 71 | "### Exercise\n", 72 | "* Use the `NOT` command 3 times on bit 0." 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": null, 78 | "metadata": { 79 | "scrolled": false 80 | }, 81 | "outputs": [], 82 | "source": [ 83 | "initialize = []\n", 84 | "success_condition = {}\n", 85 | "allowed_gates = {'0': {'NOT': 3}, '1': {}, 'both': {}}\n", 86 | "vi = [[1], False, False]\n", 87 | "qubit_names = {'0':'the only bit', '1':None}\n", 88 | "engine.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "### Outro\n", 96 | "* Here our bit was depicted by a circle that was either on (white) or off (black).\n", 97 | "* The effect of the `NOT` command was to turn it on and off.\n", 98 | "* This flips out bit between `0` and `1`." 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "metadata": {}, 104 | "source": [ 105 | "# Level 2\n", 106 | "\n", 107 | "### Intro\n", 108 | "* Now let's do the same thing to a different bit.\n", 109 | "* This will look the same as before. But because it is a different bit, it'll be in a different place.\n", 110 | "\n", 111 | "### Exercise\n", 112 | "* Turn the other bit on." 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": null, 118 | "metadata": {}, 119 | "outputs": [], 120 | "source": [ 121 | "initialize = []\n", 122 | "success_condition = {}\n", 123 | "allowed_gates = {'0': {}, '1': {'NOT': 0}, 'both': {}}\n", 124 | "vi = [[], False, False]\n", 125 | "qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}\n", 126 | "engine.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)" 127 | ] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "metadata": {}, 132 | "source": [ 133 | "### Outro\n", 134 | "* You've now mastered the NOT command: the most basic building block of computing." 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": {}, 140 | "source": [ 141 | "# Level 3\n", 142 | "\n", 143 | "### Intro\n", 144 | "* To really process information stored in bits, computers need more than just `NOT`s.\n", 145 | "* We need commands that let us manipulate some bits in a way that depends on other bits.\n", 146 | "* The simplest example is the controlled-`NOT`, or `CNOT`.\n", 147 | "* For this you need to choose one bit to be the _target_, and the other to be the _control_.\n", 148 | "* The `CNOT` then does a `NOT` on the target bit, but only if the control bit is on.\n", 149 | "\n", 150 | "### Exercise\n", 151 | "* Use the `CNOT` to turn on bit 1.\n", 152 | "* **Note**: The `CNOT` acts on both bits, bit you still need to choose which will be the target bit." 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": null, 158 | "metadata": {}, 159 | "outputs": [], 160 | "source": [ 161 | "initialize = [['x', '0']]\n", 162 | "success_condition = {'IZ': -1.0}\n", 163 | "allowed_gates = {'0': {'CNOT': 0}, '1': {'CNOT': 0}, 'both': {}}\n", 164 | "vi = [[], False, False]\n", 165 | "qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}\n", 166 | "engine.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)" 167 | ] 168 | }, 169 | { 170 | "cell_type": "markdown", 171 | "metadata": {}, 172 | "source": [ 173 | "# Level 4\n", 174 | "\n", 175 | "### Intro\n", 176 | "* Now do the same again, but also turn off the bit on the left.\n", 177 | "\n", 178 | "### Exercise\n", 179 | "* Use some CNOTs to turn bit 0 off and bit 1 on." 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": null, 185 | "metadata": {}, 186 | "outputs": [], 187 | "source": [ 188 | "initialize = [['x', '0']]\n", 189 | "success_condition = {'ZI': 1.0, 'IZ': -1.0}\n", 190 | "allowed_gates = {'0': {'CNOT': 0}, '1': {'CNOT': 0}, 'both': {}}\n", 191 | "vi = [[], False, False]\n", 192 | "qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}\n", 193 | "engine.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)" 194 | ] 195 | }, 196 | { 197 | "cell_type": "markdown", 198 | "metadata": {}, 199 | "source": [ 200 | "### Outro\n", 201 | "* Well done!\n", 202 | "* These kind of manipulations are what all computing compiles down to\n", 203 | "* With more bits and a controlled-controlled-`NOT`, you can do everything from Tetris to self-driving cars." 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "metadata": {}, 209 | "source": [ 210 | "# Level 5\n", 211 | "\n", 212 | "### Intro\n", 213 | "* Qubits have some similarities to random bit values, so let's take a few exercises to look at them.\n", 214 | "* For a bit that will give us a `0` or `1` with equal probability, we'll use a grey circle.\n", 215 | "\n", 216 | "### Exercise\n", 217 | "* Make the bit on the right random using a CNOT." 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": null, 223 | "metadata": {}, 224 | "outputs": [], 225 | "source": [ 226 | "initialize = [['h', '0']]\n", 227 | "success_condition = {'IZ': 0.0}\n", 228 | "allowed_gates = {'0': {'CNOT': 0}, '1': {'CNOT': 0}, 'both': {}}\n", 229 | "vi = [[], False, False]\n", 230 | "qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}\n", 231 | "engine.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)" 232 | ] 233 | }, 234 | { 235 | "cell_type": "markdown", 236 | "metadata": {}, 237 | "source": [ 238 | "### Outro\n", 239 | "* Well done!\n", 240 | "* If the left bit was off, the right bit stayed off. If the left bit was on, the right bit got switched on too.\n", 241 | "* So the random value of the left bit was copied over to the right by the `CNOT`.\n", 242 | "* In this case, despite the randomness, both bits will always output the same result.\n", 243 | "* But we could also create cases where they are independently random, or always have different results.\n", 244 | "* How can we keep track of this information?" 245 | ] 246 | }, 247 | { 248 | "cell_type": "markdown", 249 | "metadata": {}, 250 | "source": [ 251 | "# Level 6\n", 252 | "\n", 253 | "### Intro\n", 254 | "* To keep track of correlations, we'll add another circle.\n", 255 | "* This doesn't represent a new bit. It simply tells us whether our two bits will agree or not.\n", 256 | "* It will be off when they agree, on when they don't, and grey when they aren't correlated\n", 257 | "\n", 258 | "### Exercise\n", 259 | "* Make the two bits always disagree" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": null, 265 | "metadata": { 266 | "scrolled": false 267 | }, 268 | "outputs": [], 269 | "source": [ 270 | "initialize = [['h', '0']]\n", 271 | "success_condition = {'ZZ': -1.0}\n", 272 | "allowed_gates = {'0': {'NOT': 0, 'CNOT': 0}, '1': {'NOT': 0, 'CNOT': 0}, 'both': {}}\n", 273 | "vi = [[], False, True]\n", 274 | "qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}\n", 275 | "engine.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)" 276 | ] 277 | }, 278 | { 279 | "cell_type": "markdown", 280 | "metadata": {}, 281 | "source": [ 282 | "# Level 7\n", 283 | "\n", 284 | "### Intro\n", 285 | "* Now you know pretty much need all you need to know about bits.\n", 286 | "* Let's have one more exercise before we move on.\n", 287 | "\n", 288 | "### Exercise\n", 289 | "* Turn on bit 1" 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "execution_count": null, 295 | "metadata": {}, 296 | "outputs": [], 297 | "source": [ 298 | "initialize = [['h', '1']]\n", 299 | "success_condition = {'IZ': -1.0}\n", 300 | "allowed_gates = {'0': {'NOT': 0, 'CNOT': 0}, '1': {'NOT': 0, 'CNOT': 0}, 'both': {}}\n", 301 | "vi = [[], False, True]\n", 302 | "qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}\n", 303 | "engine.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)" 304 | ] 305 | }, 306 | { 307 | "cell_type": "markdown", 308 | "metadata": {}, 309 | "source": [ 310 | "### Outro\n", 311 | "* That's is the end of this set of exercises.\n", 312 | "* Time to move on to qubits!" 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": null, 318 | "metadata": {}, 319 | "outputs": [], 320 | "source": [] 321 | } 322 | ], 323 | "metadata": { 324 | "kernelspec": { 325 | "display_name": "Python 3", 326 | "language": "python", 327 | "name": "python3" 328 | }, 329 | "language_info": { 330 | "codemirror_mode": { 331 | "name": "ipython", 332 | "version": 3 333 | }, 334 | "file_extension": ".py", 335 | "mimetype": "text/x-python", 336 | "name": "python", 337 | "nbconvert_exporter": "python", 338 | "pygments_lexer": "ipython3", 339 | "version": "3.7.0" 340 | } 341 | }, 342 | "nbformat": 4, 343 | "nbformat_minor": 2 344 | } 345 | -------------------------------------------------------------------------------- /Index.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Hello Quantum - Qiskit Edition\n", 8 | "\n", 9 | "This folder contains an upgraded version of the [command line Hello Quantum tutorial](https://medium.com/qiskit/visualizing-bits-and-qubits-9af287047b28). It has been made for Jupyter notebooks.\n", 10 | "\n", 11 | "* [Chapter 1: Bits](1_Bits.ipynb)\n", 12 | "* [Chapter 2: Single Qubit Gates](2_Single-Qubit-Gates.ipynb)\n", 13 | "* [Chapter 3: Two Qubit Gates](3_Two-Qubit-Gates.ipynb)\n", 14 | "* [Chapter 4: Beyond Clifford](4_Beyond-Clifford.ipynb)\n", 15 | "* [Chapter 5: Bell Test](5_Bell-Test.ipynb)\n", 16 | "\n", 17 | "Comments required. The more critical, the better!" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [] 26 | } 27 | ], 28 | "metadata": { 29 | "kernelspec": { 30 | "display_name": "Python 3", 31 | "language": "python", 32 | "name": "python3" 33 | }, 34 | "language_info": { 35 | "codemirror_mode": { 36 | "name": "ipython", 37 | "version": 3 38 | }, 39 | "file_extension": ".py", 40 | "mimetype": "text/x-python", 41 | "name": "python", 42 | "nbconvert_exporter": "python", 43 | "pygments_lexer": "ipython3", 44 | "version": "3.7.0" 45 | } 46 | }, 47 | "nbformat": 4, 48 | "nbformat_minor": 2 49 | } 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /QusDev-Surface-Codes.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# The QuDev Surface Code" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "To install the required version of `topological_codes` use\n", 15 | " pip install git+https://github.com/NCCR-SPIN/topological_codes.git" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": 1, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, Aer\n", 25 | "from qiskit.providers.aer.noise.errors import pauli_error\n", 26 | "from topological_codes import SurfaceCode, GraphDecoder\n", 27 | "import numpy as np\n", 28 | "import pickle" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "The surface code of the [\"Realizing Repeated Quantum Error Correction in a Distance-Three Surface Code\"](https://arxiv.org/abs/2112.03708) paper by the QuDev lab doesn't use the same conventions for the naming of plaquettes or syndrome measurement as the `SurfaceCode` class of `topological_codes`. We therefore need custom methods to replace the standard ones." 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 2, 41 | "metadata": {}, 42 | "outputs": [], 43 | "source": [ 44 | "def _get_plaquettes(self):\n", 45 | " assert self.d==3, 'The QuDev convention is not defined d='+str(self.d)+': only d=3 can be used.'\n", 46 | " zplaqs = [[0,3,None,None],[4,7,3,6],[2,4,1,5],[None,None,5,8]]\n", 47 | " xplaqs = [[None,None,2,1],[0,1,4,3],[4,5,8,7],[6,7,None,None]]\n", 48 | " return zplaqs, xplaqs\n", 49 | "\n", 50 | "def syndrome_measurement(self, final=False, barrier=False):\n", 51 | " \"\"\"\n", 52 | " Application of a syndrome measurement round.\n", 53 | " Args:\n", 54 | " final (bool): If set to true add a reset at the end of each measurement.\n", 55 | " barrier (bool): Boolean denoting whether to include a barrier at the end.\n", 56 | " \"\"\"\n", 57 | " \n", 58 | " self._resets = False\n", 59 | "\n", 60 | " num_bits = int((self.d**2 - 1)/2)\n", 61 | "\n", 62 | " # classical registers for this round\n", 63 | " self.zplaq_bits.append(ClassicalRegister(\n", 64 | " self._num_xy, 'round_' + str(self.T) + '_zplaq_bit'))\n", 65 | " self.xplaq_bits.append(ClassicalRegister(\n", 66 | " self._num_xy, 'round_' + str(self.T) + '_xplaq_bit'))\n", 67 | "\n", 68 | " for log in ['0', '1']:\n", 69 | "\n", 70 | " self.circuit[log].add_register(self.zplaq_bits[-1])\n", 71 | " self.circuit[log].add_register(self.xplaq_bits[-1])\n", 72 | "\n", 73 | " # z plaquette measurement\n", 74 | "\n", 75 | " self.circuit[log].h(self.zplaq_qubit) # part of ry(pi/2)\n", 76 | " self.circuit[log].x(self.zplaq_qubit) # part of ry(pi/2)\n", 77 | "\n", 78 | " self.circuit[log].cz(self.code_qubit[0], self.zplaq_qubit[0])\n", 79 | " self.circuit[log].cz(self.code_qubit[4], self.zplaq_qubit[1])\n", 80 | " self.circuit[log].cz(self.code_qubit[2], self.zplaq_qubit[2])\n", 81 | "\n", 82 | " self.circuit[log].cz(self.code_qubit[3], self.zplaq_qubit[0])\n", 83 | " self.circuit[log].cz(self.code_qubit[7], self.zplaq_qubit[1])\n", 84 | " self.circuit[log].cz(self.code_qubit[4], self.zplaq_qubit[2])\n", 85 | "\n", 86 | " self.circuit[log].y(self.code_qubit)\n", 87 | "\n", 88 | " self.circuit[log].cz(self.code_qubit[3], self.zplaq_qubit[1])\n", 89 | " self.circuit[log].cz(self.code_qubit[1], self.zplaq_qubit[2])\n", 90 | " self.circuit[log].cz(self.code_qubit[5], self.zplaq_qubit[3])\n", 91 | "\n", 92 | " self.circuit[log].cz(self.code_qubit[6], self.zplaq_qubit[1])\n", 93 | " self.circuit[log].cz(self.code_qubit[5], self.zplaq_qubit[2])\n", 94 | " self.circuit[log].cz(self.code_qubit[8], self.zplaq_qubit[3])\n", 95 | "\n", 96 | " self.circuit[log].x(self.zplaq_qubit) # part of ry(-pi/2)\n", 97 | " self.circuit[log].h(self.zplaq_qubit) # part of ry(-pi/2)\n", 98 | "\n", 99 | " self.circuit[log].measure(self.zplaq_qubit, self.zplaq_bits[self.T])\n", 100 | "\n", 101 | " # x plaquette measurement\n", 102 | "\n", 103 | " self.circuit[log].h(self.xplaq_qubit) # part of ry(pi/2)\n", 104 | " self.circuit[log].x(self.xplaq_qubit) # part of ry(pi/2)\n", 105 | "\n", 106 | " self.circuit[log].x(self.code_qubit) # part of ry(-pi/2)\n", 107 | " self.circuit[log].h(self.code_qubit) # part of ry(-pi/2)\n", 108 | "\n", 109 | " self.circuit[log].cz(self.code_qubit[0], self.xplaq_qubit[1])\n", 110 | " self.circuit[log].cz(self.code_qubit[4], self.xplaq_qubit[2]) \n", 111 | " self.circuit[log].cz(self.code_qubit[6], self.xplaq_qubit[3])\n", 112 | "\n", 113 | " self.circuit[log].cz(self.code_qubit[1], self.xplaq_qubit[1])\n", 114 | " self.circuit[log].cz(self.code_qubit[5], self.xplaq_qubit[2]) \n", 115 | " self.circuit[log].cz(self.code_qubit[7], self.xplaq_qubit[3]) \n", 116 | "\n", 117 | " self.circuit[log].y(self.code_qubit)\n", 118 | "\n", 119 | " self.circuit[log].cz(self.code_qubit[2], self.xplaq_qubit[0])\n", 120 | " self.circuit[log].cz(self.code_qubit[4], self.xplaq_qubit[1]) \n", 121 | " self.circuit[log].cz(self.code_qubit[8], self.xplaq_qubit[2])\n", 122 | "\n", 123 | " self.circuit[log].cz(self.code_qubit[1], self.xplaq_qubit[0])\n", 124 | " self.circuit[log].cz(self.code_qubit[3], self.xplaq_qubit[1]) \n", 125 | " self.circuit[log].cz(self.code_qubit[7], self.xplaq_qubit[2])\n", 126 | "\n", 127 | " self.circuit[log].x(self.code_qubit) # part of ry(-pi/2)\n", 128 | " self.circuit[log].h(self.code_qubit) # part of ry(-pi/2)\n", 129 | " self.circuit[log].x(self.xplaq_qubit) # part of ry(-pi/2)\n", 130 | " self.circuit[log].h(self.xplaq_qubit) # part of ry(-pi/2)\n", 131 | "\n", 132 | " self.circuit[log].measure(self.xplaq_qubit, self.xplaq_bits[self.T])\n", 133 | "\n", 134 | " self.T += 1" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": {}, 140 | "source": [ 141 | "With these we can construct a class for QuDev surface codes." 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": 3, 147 | "metadata": {}, 148 | "outputs": [], 149 | "source": [ 150 | "class QudevSurfaceCode(SurfaceCode):\n", 151 | " pass\n", 152 | "\n", 153 | "QudevSurfaceCode._get_plaquettes = _get_plaquettes\n", 154 | "QudevSurfaceCode.syndrome_measurement = syndrome_measurement" 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "metadata": {}, 160 | "source": [ 161 | "To create a `QudevSurfaceCode` object we need to specify:\n", 162 | "* `d` - The code distance (which has to be 3);\n", 163 | "* `T` - the number of syndrome measurement rounds (there's currently a bug if this is not even);\n", 164 | "* `basis` - Whether to encode the states $|0\\rangle$ and $|1\\rangle$ (`basis='z'`), or $|+\\rangle$ and $|-\\rangle$ (`basis='x'`).\n", 165 | "\n", 166 | "Note that qubits and stabilizers are numbered from 1 in the paper but from 0 in this object. So `code_qubit[0]` is qubit D1, `zplaq_qubit[0]` is the auxillary qubit for the stabilizer $S^{Z1}$, and so on." 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": 4, 172 | "metadata": {}, 173 | "outputs": [], 174 | "source": [ 175 | "d = 3\n", 176 | "T = 2\n", 177 | "basis = 'z'\n", 178 | "\n", 179 | "code = QudevSurfaceCode(d,T,basis=basis)" 180 | ] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "metadata": {}, 185 | "source": [ 186 | "The code object contains circuits for the two possible logical encoded states: `code.circuit['0']` `code.circuit['1']`. Note that `'0'` and `'1'` are used even for `basis='x'`.\n", 187 | "\n", 188 | "We can run these on a simulato to see the format of `raw_results` expected when processing results." 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": 5, 194 | "metadata": {}, 195 | "outputs": [ 196 | { 197 | "data": { 198 | "text/plain": [ 199 | "{'0': {'000000110 0000 0000 1010 0000': 1,\n", 200 | " '101110000 0000 0000 0100 0000': 1,\n", 201 | " '101101101 0000 0000 0001 0000': 1,\n", 202 | " '110101011 0000 0000 0111 0000': 1,\n", 203 | " '101101011 0000 0000 0000 0000': 1},\n", 204 | " '1': {'100100010 0000 0000 1010 0000': 1,\n", 205 | " '100111111 0000 0000 0101 0000': 1,\n", 206 | " '100100010 0000 0000 1111 0000': 1,\n", 207 | " '001001001 0000 0000 0011 0000': 1,\n", 208 | " '100111111 0000 0000 1100 0000': 1}}" 209 | ] 210 | }, 211 | "execution_count": 5, 212 | "metadata": {}, 213 | "output_type": "execute_result" 214 | } 215 | ], 216 | "source": [ 217 | "job = Aer.get_backend('aer_simulator').run([code.circuit['0'],code.circuit['1']],shots=5)\n", 218 | "raw_results = {'0':job.result().get_counts(0), '1':job.result().get_counts(1)}\n", 219 | "raw_results" 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": {}, 225 | "source": [ 226 | "Here the output bit strings are of the form\n", 227 | "\n", 228 | " 'yxwvutsrq ponm lkji hgfe dcba'\n", 229 | "\n", 230 | "Where\n", 231 | "* `dcba` is the result of the first round of Z syndrome measurements (`a` is the result of $S^{Z1}$, `b` is the result of $S^{Z2}$, and so on).\n", 232 | "* `hgfe` is the result of the first round of X syndrome measurements.\n", 233 | "* `lkji` is the result of the second round of Z syndrome measurements.\n", 234 | "* `ponm` is the result of the second round of X syndrome measurements.\n", 235 | "* `yxwvutsrq` is the result of a final measurement of all qubits in the basis specified by `basis` (`q` is the result from qubit D1, and so on)." 236 | ] 237 | }, 238 | { 239 | "cell_type": "markdown", 240 | "metadata": {}, 241 | "source": [ 242 | "The code object has the method `process_results` to put this into a more useful form for the decoder." 243 | ] 244 | }, 245 | { 246 | "cell_type": "code", 247 | "execution_count": 6, 248 | "metadata": {}, 249 | "outputs": [ 250 | { 251 | "data": { 252 | "text/plain": [ 253 | "{'0': {'0 0 0000 0000 0000': 5}, '1': {'1 1 0000 0000 0000': 5}}" 254 | ] 255 | }, 256 | "execution_count": 6, 257 | "metadata": {}, 258 | "output_type": "execute_result" 259 | } 260 | ], 261 | "source": [ 262 | "results = code.process_results(raw_results)\n", 263 | "results" 264 | ] 265 | }, 266 | { 267 | "cell_type": "markdown", 268 | "metadata": {}, 269 | "source": [ 270 | "These strings are of the form\n", 271 | "\n", 272 | "`'n m dcba hgfe lkji'`\n", 273 | "\n", 274 | "where\n", 275 | "* `dcba` is the result of the first round of relevant syndrome measurements for the given `basis`;\n", 276 | "* `hgfe` has the differences between the first and second round;\n", 277 | "* `lkji` has the differences between the first and an effective final round extracted from the final code qubit measurements;\n", 278 | "* `n` and `m` are the two different measurements of the bare logical value for `basis`.\n", 279 | "\n", 280 | "Decoder objects are constructed for a given code object using `decoder = GraphDecoder(code)`. The process of creating the syndrome graph can take a while, so best to load in a premade one if possible." 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": 7, 286 | "metadata": {}, 287 | "outputs": [], 288 | "source": [ 289 | "try:\n", 290 | " S = pickle.load(open( 'graphs/S'+str(T)+'.p', 'rb' ))\n", 291 | " decoder = GraphDecoder(code, S=S)\n", 292 | "except:\n", 293 | " decoder = GraphDecoder(code)" 294 | ] 295 | }, 296 | { 297 | "cell_type": "markdown", 298 | "metadata": {}, 299 | "source": [ 300 | "Each edge in the syndrome graph represents a single qubit error that could occur at some point in the circuit. One of the things that the decoder can do is analzse the results to determine the probability of each of these errors." 301 | ] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": 8, 306 | "metadata": {}, 307 | "outputs": [ 308 | { 309 | "data": { 310 | "text/plain": [ 311 | "{((1, 0, 3), (1, 1, 3)): 0,\n", 312 | " ((1, 0, 2), (1, 1, 2)): 0,\n", 313 | " ((1, 0, 1), (1, 1, 1)): 0,\n", 314 | " ((1, 0, 0), (1, 1, 0)): 0,\n", 315 | " ((1, 0, 1), (1, 0, 2)): 0,\n", 316 | " ((1, 0, 2), (1, 0, 3)): 0,\n", 317 | " ((1, 0, 1), (1, 1, 2)): 0,\n", 318 | " ((1, 0, 2), (1, 1, 3)): 0,\n", 319 | " ((1, 1, 1), (1, 1, 2)): 0,\n", 320 | " ((1, 0, 0), (1, 0, 1)): 0,\n", 321 | " ((1, 0, 1), (1, 1, 0)): 0,\n", 322 | " ((1, 1, 2), (1, 1, 3)): 0,\n", 323 | " ((1, 1, 0), (1, 1, 1)): 0,\n", 324 | " ((1, 1, 1), (1, 1, 3)): 0,\n", 325 | " ((1, 1, 0), (1, 1, 2)): 0,\n", 326 | " ((1, 1, 3), (1, 2, 3)): 0,\n", 327 | " ((1, 1, 2), (1, 2, 2)): 0,\n", 328 | " ((1, 1, 1), (1, 2, 1)): 0,\n", 329 | " ((1, 1, 0), (1, 2, 0)): 0,\n", 330 | " ((1, 1, 1), (1, 2, 2)): 0,\n", 331 | " ((1, 1, 2), (1, 2, 3)): 0,\n", 332 | " ((1, 2, 1), (1, 2, 2)): 0,\n", 333 | " ((1, 1, 1), (1, 2, 0)): 0,\n", 334 | " ((1, 2, 2), (1, 2, 3)): 0,\n", 335 | " ((1, 2, 0), (1, 2, 1)): 0,\n", 336 | " ((1, 2, 1), (1, 2, 3)): 0,\n", 337 | " ((1, 2, 0), (1, 2, 2)): 0,\n", 338 | " ((1, 0, 3), (1, 0, 3)): 0.0,\n", 339 | " ((1, 0, 1), (1, 0, 1)): 0.0,\n", 340 | " ((1, 0, 2), (1, 0, 2)): 0.0,\n", 341 | " ((1, 1, 3), (1, 1, 3)): 0.0,\n", 342 | " ((1, 1, 1), (1, 1, 1)): 0.0,\n", 343 | " ((1, 1, 2), (1, 1, 2)): 0.0,\n", 344 | " ((1, 0, 0), (1, 0, 0)): 0.0,\n", 345 | " ((1, 1, 0), (1, 1, 0)): 0.0,\n", 346 | " ((1, 2, 3), (1, 2, 3)): 0.0,\n", 347 | " ((1, 2, 1), (1, 2, 1)): 0.0,\n", 348 | " ((1, 2, 2), (1, 2, 2)): 0.0,\n", 349 | " ((1, 2, 0), (1, 2, 0)): 0.0}" 350 | ] 351 | }, 352 | "execution_count": 8, 353 | "metadata": {}, 354 | "output_type": "execute_result" 355 | } 356 | ], 357 | "source": [ 358 | "decoder.get_error_probs(results)" 359 | ] 360 | }, 361 | { 362 | "cell_type": "markdown", 363 | "metadata": {}, 364 | "source": [ 365 | "Here the edges are labelled $((1,t,s),(1,t',s')$. The two sets of coordinates here represent the two syndrome measurements at which the error was detected. For these `t` is the round and `s` is the syndrome.\n", 366 | "\n", 367 | "We can associate differet types of edges with different kinds of noise:\n", 368 | "* $((1,t,s),(1,t+1,s))$ due to measurement errors on auxilliary qubits;\n", 369 | "* $((1,t,s),(1,t,s'))$ due to an error on the code qubit shared by $s$ and $s'$;\n", 370 | "* $((1,t,s),(1,t+1,s'))$ due to an error between the entangling gates while measuring $s$ and $s'$.\n", 371 | "\n", 372 | "In the results above there were no errors, so all probailities are zero. As an example, let's add some measurement errors." 373 | ] 374 | }, 375 | { 376 | "cell_type": "code", 377 | "execution_count": 9, 378 | "metadata": {}, 379 | "outputs": [], 380 | "source": [ 381 | "p_meas = 0.01\n", 382 | "\n", 383 | "def noisy_circuit(qc,p_meas):\n", 384 | " 'adds a bit flip error before every measurememnt'\n", 385 | " error_qc = QuantumCircuit()\n", 386 | " for qreg in qc.qregs:\n", 387 | " error_qc.add_register(qreg)\n", 388 | " for creg in qc.cregs:\n", 389 | " error_qc.add_register(creg)\n", 390 | " depth = len(qc)\n", 391 | " for j in range(depth):\n", 392 | " if qc.data[j][0].name=='measure':\n", 393 | " qubit = qc.data[j][1]\n", 394 | " error_qc.append(pauli_error([('X', p_meas), ('I', 1 - p_meas)]),qubit)\n", 395 | " error_qc.data.append(qc.data[j]) \n", 396 | " return error_qc\n", 397 | "\n", 398 | "circuits = [noisy_circuit(code.circuit[log], p_meas) for log in ['0', '1']]\n", 399 | "\n", 400 | "job = Aer.get_backend('aer_simulator').run(circuits, shots=1024)\n", 401 | "raw_results = {'0':job.result().get_counts(0), '1':job.result().get_counts(1)}\n", 402 | "\n", 403 | "results = code.process_results(raw_results)\n", 404 | "\n", 405 | "error_probs = decoder.get_error_probs(results)" 406 | ] 407 | }, 408 | { 409 | "cell_type": "markdown", 410 | "metadata": {}, 411 | "source": [ 412 | "In these results we have the eight errors associated with error probs in the syndrome measurement. These should all be $~0.01$." 413 | ] 414 | }, 415 | { 416 | "cell_type": "code", 417 | "execution_count": 26, 418 | "metadata": {}, 419 | "outputs": [ 420 | { 421 | "name": "stdout", 422 | "output_type": "stream", 423 | "text": [ 424 | "((1, 0, 0), (1, 1, 0)) 0.008885558692194595\n", 425 | "((1, 0, 1), (1, 1, 1)) 0.008867698578071515\n", 426 | "((1, 0, 2), (1, 1, 2)) 0.010904319809213858\n", 427 | "((1, 0, 3), (1, 1, 3)) 0.011895624339183564\n", 428 | "((1, 1, 0), (1, 2, 0)) 0.010870378947497095\n", 429 | "((1, 1, 1), (1, 2, 1)) 0.007812633341885256\n", 430 | "((1, 1, 2), (1, 2, 2)) 0.013918560239978173\n", 431 | "((1, 1, 3), (1, 2, 3)) 0.0138682643057908\n" 432 | ] 433 | } 434 | ], 435 | "source": [ 436 | "done = []\n", 437 | "for t in range(T):\n", 438 | " for s in range(4):\n", 439 | " edge = ((1,t,s),(1,t+1,s))\n", 440 | " done.append(edge)\n", 441 | " print(edge,error_probs[edge])" 442 | ] 443 | }, 444 | { 445 | "cell_type": "markdown", 446 | "metadata": {}, 447 | "source": [ 448 | "There are also errors that show up as errors on code qubits, during the final effective syndrome measurement round (done by directly measuring code qubits)." 449 | ] 450 | }, 451 | { 452 | "cell_type": "code", 453 | "execution_count": 27, 454 | "metadata": {}, 455 | "outputs": [ 456 | { 457 | "name": "stdout", 458 | "output_type": "stream", 459 | "text": [ 460 | "((1, 2, 0), (1, 2, 0)) 0.005735444986191884\n", 461 | "((1, 2, 1), (1, 2, 1)) 0.017880724864877073\n", 462 | "((1, 2, 2), (1, 2, 2)) 0.021433617657113768\n", 463 | "((1, 2, 3), (1, 2, 3)) 0.007370427480762276\n" 464 | ] 465 | } 466 | ], 467 | "source": [ 468 | "t = T\n", 469 | "for s in range(4):\n", 470 | " edge = ((1,t,s),(1,t,s))\n", 471 | " done.append(edge)\n", 472 | " print(edge,error_probs[edge])" 473 | ] 474 | }, 475 | { 476 | "cell_type": "markdown", 477 | "metadata": {}, 478 | "source": [ 479 | "What remains is either very small (and so just not really an error), or some other final round stuff." 480 | ] 481 | }, 482 | { 483 | "cell_type": "code", 484 | "execution_count": 28, 485 | "metadata": {}, 486 | "outputs": [ 487 | { 488 | "name": "stdout", 489 | "output_type": "stream", 490 | "text": [ 491 | "((1, 1, 1), (1, 1, 3)) 0.001\n", 492 | "((1, 1, 0), (1, 1, 2)) 0.001\n", 493 | "((1, 1, 2), (1, 2, 3)) 0.002\n", 494 | "((1, 2, 1), (1, 2, 2)) 0.01\n", 495 | "((1, 1, 1), (1, 2, 0)) 0.001\n", 496 | "((1, 2, 2), (1, 2, 3)) 0.008\n", 497 | "((1, 2, 0), (1, 2, 1)) 0.01\n" 498 | ] 499 | } 500 | ], 501 | "source": [ 502 | "for edge in error_probs:\n", 503 | " prob = round(error_probs[edge],3)\n", 504 | " if edge not in done and prob>0:\n", 505 | " print(edge,prob)" 506 | ] 507 | }, 508 | { 509 | "cell_type": "code", 510 | "execution_count": null, 511 | "metadata": {}, 512 | "outputs": [], 513 | "source": [] 514 | } 515 | ], 516 | "metadata": { 517 | "kernelspec": { 518 | "display_name": "Python 3", 519 | "language": "python", 520 | "name": "python3" 521 | }, 522 | "language_info": { 523 | "codemirror_mode": { 524 | "name": "ipython", 525 | "version": 3 526 | }, 527 | "file_extension": ".py", 528 | "mimetype": "text/x-python", 529 | "name": "python", 530 | "nbconvert_exporter": "python", 531 | "pygments_lexer": "ipython3", 532 | "version": "3.9.1" 533 | } 534 | }, 535 | "nbformat": 4, 536 | "nbformat_minor": 4 537 | } 538 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hello Quantum - Qiskit Edition [![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/decodoku/Quantum_Programming_Tutorial/master?filepath=.ipynb) 2 | 3 | This folder contains an upgraded version of the [command line Hello Quantum tutorial](https://medium.com/qiskit/visualizing-bits-and-qubits-9af287047b28). It has been made for Jupyter notebooks. 4 | 5 | * [Chapter 1: Bits](1_Bits.ipynb) 6 | * [Chapter 2: Single Qubit Gates](2_Single-Qubit-Gates.ipynb) 7 | * [Chapter 3: Two Qubit Gates](3_Two-Qubit-Gates.ipynb) 8 | * [Chapter 4: Beyond Clifford](4_Beyond-Clifford.ipynb) 9 | * [Chapter 5: Bell Test](5_Bell-Test.ipynb) 10 | 11 | Comments required. The more critical, the better! 12 | -------------------------------------------------------------------------------- /engine.py: -------------------------------------------------------------------------------- 1 | from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit, execute, Aer 2 | 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | from matplotlib.patches import Circle, Rectangle 6 | import copy 7 | from ipywidgets import widgets 8 | from IPython.display import display, clear_output 9 | 10 | try: 11 | import sys 12 | sys.path.append("../") # go to parent dir 13 | import Qconfig 14 | qx_config = { 15 | "APItoken": Qconfig.APItoken, 16 | "url": Qconfig.config['url']} 17 | #set api 18 | register(qx_config['APItoken'], qx_config['url']) 19 | except Exception as e: 20 | pass 21 | 22 | class run_game(): 23 | 24 | def __init__(self,initialize, success_condition, allowed_gates, vi, qubit_names, eps=0.1, backend='qasm_simulator_py',shots=1024,mode='circle',verbose=False): 25 | 26 | def get_total_gate_list(): 27 | # get a text block describing allowed gates 28 | total_gate_list = "" 29 | for qubit in allowed_gates: 30 | gate_list = "" 31 | for gate in allowed_gates[qubit]: 32 | if required_gates[qubit][gate] > 0 : 33 | gate_list += ' ' + gate+" (use "+str(required_gates[qubit][gate])+" time"+"s"*(required_gates[qubit][gate]>1)+")" 34 | elif allowed_gates[qubit][gate]==0: 35 | gate_list += ' '+gate + ',' 36 | if gate_list!="": 37 | if qubit=="both" : 38 | gate_list = "\nAllowed symmetric operations:" + gate_list 39 | else : 40 | gate_list = "\nAllowed operations for " + qubit_names[qubit] + ":\n" + " "*10 + gate_list 41 | total_gate_list += gate_list +"\n" 42 | return total_gate_list 43 | 44 | def get_success(required_gates): 45 | success = True 46 | grid.get_rho() 47 | if verbose: 48 | print(grid.rho) 49 | for pauli in success_condition: 50 | success = success and (abs(success_condition[pauli] - grid.rho[pauli])0: 173 | required_gates[q01][gate.value] -= 1 174 | 175 | grid.update_grid(bloch=bloch[0],hidden=vi[0],qubit=vi[1],corr=vi[2],message=get_total_gate_list()) 176 | 177 | success = get_success(required_gates) 178 | if success: 179 | gate.options = ['Success!'] 180 | qubit.options = ['Success!'] 181 | action.options = ['Success!'] 182 | else: 183 | gate.value = description['gate'][0] 184 | qubit.options = [''] 185 | action.options = [''] 186 | 187 | gate.observe(given_gate) 188 | qubit.observe(given_qubit) 189 | action.observe(given_action) 190 | 191 | 192 | ##################################################################################### 193 | 194 | class pauli_grid(): 195 | 196 | def __init__(self,backend='qasm_simulator_py',shots=1024,mode='circle'): 197 | 198 | self.backend = backend 199 | self.shots = shots 200 | 201 | self.box = {'ZI':(-1, 2),'XI':(-2, 3),'IZ':( 1, 2),'IX':( 2, 3),'ZZ':( 0, 3),'ZX':( 1, 4),'XZ':(-1, 4),'XX':( 0, 5)} 202 | 203 | self.rho = {} 204 | for pauli in self.box: 205 | self.rho[pauli] = 0.0 206 | for pauli in ['ZI','IZ','ZZ']: 207 | self.rho[pauli] = 1.0 208 | 209 | self.qr = QuantumRegister(2) 210 | self.cr = ClassicalRegister(2) 211 | self.qc = QuantumCircuit(self.qr, self.cr) 212 | 213 | self.mode = mode 214 | # colors are background, qubit circles and correlation circles, respectively 215 | if self.mode=='line': 216 | self.colors = [(1.6/255,72/255,138/255),(132/255,177/255,236/255),(33/255,114/255,216/255)] 217 | else: 218 | self.colors = [(1.6/255,72/255,138/255),(132/255,177/255,236/255),(33/255,114/255,216/255)] 219 | 220 | self.fig = plt.figure(figsize=(8,8),facecolor=self.colors[0]) 221 | self.ax = self.fig.add_subplot(111) 222 | plt.axis('off') 223 | 224 | self.bottom = self.ax.text(-3,1,"",size=14,va='top',color='w') 225 | 226 | self.lines = {} 227 | for pauli in self.box: 228 | w = plt.plot( [self.box[pauli][0],self.box[pauli][0]], [self.box[pauli][1],self.box[pauli][1]], color=(1.0,1.0,1.0), lw=0 ) 229 | b = plt.plot( [self.box[pauli][0],self.box[pauli][0]], [self.box[pauli][1],self.box[pauli][1]], color=(0.0,0.0,0.0), lw=0 ) 230 | c = {} 231 | c['w'] = self.ax.add_patch( Circle(self.box[pauli], 0.0, color=(0,0,0), zorder=10) ) 232 | c['b'] = self.ax.add_patch( Circle(self.box[pauli], 0.0, color=(1,1,1), zorder=10) ) 233 | self.lines[pauli] = {'w':w,'b':b,'c':c} 234 | 235 | 236 | def get_rho(self): 237 | 238 | bases = ['ZZ','ZX','XZ','XX'] 239 | results = {} 240 | for basis in bases: 241 | temp_qc = copy.deepcopy(self.qc) 242 | for j in range(2): 243 | if basis[j]=='X': 244 | temp_qc.h(self.qr[j]) 245 | temp_qc.barrier(self.qr) 246 | temp_qc.measure(self.qr,self.cr) 247 | job = execute(temp_qc, backend=Aer.get_backend(self.backend), shots=self.shots) 248 | results[basis] = job.result().get_counts() 249 | for string in results[basis]: 250 | results[basis][string] = results[basis][string]/self.shots 251 | 252 | prob = {} 253 | # prob of expectation value -1 for single qubit observables 254 | for j in range(2): 255 | for p in ['X','Z']: 256 | pauli = {} 257 | for pp in 'IXZ': 258 | pauli[pp] = (j==1)*pp + p + (j==0)*pp 259 | prob[pauli['I']] = 0 260 | for basis in [pauli['X'],pauli['Z']]: 261 | for string in results[basis]: 262 | if string[(j+1)%2]=='1': 263 | prob[pauli['I']] += results[basis][string]/2 264 | # prob of expectation value -1 for two qubit observables 265 | for basis in ['ZZ','ZX','XZ','XX']: 266 | prob[basis] = 0 267 | for string in results[basis]: 268 | if string[0]!=string[1]: 269 | prob[basis] += results[basis][string] 270 | 271 | for pauli in prob: 272 | self.rho[pauli] = 1-2*prob[pauli] 273 | 274 | 275 | def update_grid(self,rho=None,labels=False,bloch=None,hidden=[],qubit=True,corr=True,message=""): 276 | 277 | def see_if_unhidden(pauli): 278 | unhidden = True 279 | # first: does it act non-trivially on a qubit in `hidden` 280 | for j in hidden: 281 | unhidden = unhidden and (pauli[j]=='I') 282 | # second: does it contain something other than 'I' or 'Z' when only bits are shown 283 | if qubit==False: 284 | for j in range(2): 285 | unhidden = unhidden and (pauli[j] in ['I','Z']) 286 | # third: is it a correlation pauli when these are not allowed 287 | if corr==False: 288 | unhidden = unhidden and ((pauli[0]=='I') or (pauli[1]=='I')) 289 | return unhidden 290 | 291 | def add_line(line,pauli_pos,pauli): 292 | # line = the type of line to be drawn (X, Z or the other one) 293 | # pauli = the box where the line is to be drawn 294 | # expect = the expectation value that determines its length 295 | unhidden = see_if_unhidden(pauli) 296 | coord = None 297 | p = (1-self.rho[pauli])/2 # prob of 1 output 298 | # in the following, white lines goes from a to b, and black from b to c 299 | if unhidden: 300 | if line=='Z': 301 | a = ( self.box[pauli_pos][0], self.box[pauli_pos][1]+l/2 ) 302 | c = ( self.box[pauli_pos][0], self.box[pauli_pos][1]-l/2 ) 303 | b = ( (1-p)*a[0] + p*c[0] , (1-p)*a[1] + p*c[1] ) 304 | lw = 12 305 | coord = (b[1] - (a[1]+c[1])/2)*1.2 + (a[1]+c[1])/2 306 | elif line=='X': 307 | a = ( self.box[pauli_pos][0]+l/2, self.box[pauli_pos][1] ) 308 | c = ( self.box[pauli_pos][0]-l/2, self.box[pauli_pos][1] ) 309 | b = ( (1-p)*a[0] + p*c[0] , (1-p)*a[1] + p*c[1] ) 310 | lw = 14 311 | coord = (b[0] - (a[0]+c[0])/2)*1.1 + (a[0]+c[0])/2 312 | else: 313 | a = ( self.box[pauli_pos][0]+l/(2*np.sqrt(2)), self.box[pauli_pos][1]+l/(2*np.sqrt(2)) ) 314 | c = ( self.box[pauli_pos][0]-l/(2*np.sqrt(2)), self.box[pauli_pos][1]-l/(2*np.sqrt(2)) ) 315 | b = ( (1-p)*a[0] + p*c[0] , (1-p)*a[1] + p*c[1] ) 316 | lw = 14 317 | self.lines[pauli]['w'].pop(0).remove() 318 | self.lines[pauli]['b'].pop(0).remove() 319 | self.lines[pauli]['w'] = plt.plot( [a[0],b[0]], [a[1],b[1]], color=(1.0,1.0,1.0), lw=lw ) 320 | self.lines[pauli]['b'] = plt.plot( [b[0],c[0]], [b[1],c[1]], color=(0.0,0.0,0.0), lw=lw ) 321 | return coord 322 | 323 | l = 0.95 # line length 324 | r = 0.6 # circle radius 325 | L = 0.98*np.sqrt(2) # box height and width 326 | 327 | if rho==None: 328 | self.get_rho() 329 | 330 | # draw boxes 331 | for pauli in self.box: 332 | if 'I' in pauli: 333 | color = self.colors[1] 334 | else: 335 | color = self.colors[2] 336 | self.ax.add_patch( Rectangle( (self.box[pauli][0],self.box[pauli][1]-1), L, L, angle=45, color=color) ) 337 | 338 | # draw circles 339 | for pauli in self.box: 340 | unhidden = see_if_unhidden(pauli) 341 | if unhidden: 342 | if self.mode=='line': 343 | self.ax.add_patch( Circle(self.box[pauli], r, color=(0.5,0.5,0.5)) ) 344 | else: 345 | prob = (1-self.rho[pauli])/2 346 | self.ax.add_patch( Circle(self.box[pauli], r, color=(prob,prob,prob)) ) 347 | 348 | # update bars if required 349 | if self.mode=='line': 350 | if bloch in [0,1]: 351 | for other in 'IXZ': 352 | px = other*(bloch==1) + 'X' + other*(bloch==0) 353 | pz = other*(bloch==1) + 'Z' + other*(bloch==0) 354 | z_coord = add_line('Z',pz,pz) 355 | x_coord = add_line('X',pz,px) 356 | for j in self.lines[pz]['c']: 357 | self.lines[pz]['c'][j].center = (x_coord,z_coord) 358 | self.lines[pz]['c'][j].radius = (j=='w')*0.05 + (j=='b')*0.04 359 | px = 'I'*(bloch==0) + 'X' + 'I'*(bloch==1) 360 | pz = 'I'*(bloch==0) + 'Z' + 'I'*(bloch==1) 361 | add_line('Z',pz,pz) 362 | add_line('X',px,px) 363 | else: 364 | for pauli in self.box: 365 | for j in self.lines[pauli]['c']: 366 | self.lines[pauli]['c'][j].radius = 0.0 367 | if pauli in ['ZI','IZ','ZZ']: 368 | add_line('Z',pauli,pauli) 369 | if pauli in ['XI','IX','XX']: 370 | add_line('X',pauli,pauli) 371 | if pauli in ['XZ','ZX']: 372 | add_line('ZX',pauli,pauli) 373 | 374 | self.bottom.set_text(message) 375 | 376 | if labels: 377 | for pauli in box: 378 | plt.text(self.box[pauli][0]-0.05,self.box[pauli][1]-0.85, pauli) 379 | 380 | self.ax.set_xlim([-3,3]) 381 | self.ax.set_ylim([0,6]) 382 | 383 | self.fig.canvas.draw() 384 | 385 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: QISKitenv 2 | dependencies: 3 | - python=3 4 | - pip 5 | - notebook 6 | - pip: 7 | - qiskit 8 | - matplotlib 9 | - networkx --------------------------------------------------------------------------------