├── _config.yml ├── GHZGame ├── GHZ Game.pdf ├── proof1.png ├── shapes.png ├── visualization.png └── ghzGame.py ├── images └── Hardys_paradox.png ├── QuantumTheory-for-QuantumCoinGame.pdf ├── SeriousGames-for-QuantumComputing.pdf ├── postBuild ├── 3sat3-5.cnf ├── environment.yml ├── Readme.ipynb ├── .gitignore ├── QuantumVolume.ipynb ├── 3sat-v2.ipynb ├── README.md ├── LICENSE ├── 3sat.ipynb ├── Mermin–Peres-Game.ipynb ├── Quantum-Coin-Game.ipynb ├── Hardys-Paradox.ipynb └── GHZ-on-Real-Devices.ipynb /_config.yml: -------------------------------------------------------------------------------- 1 | jekyll-theme-leap-day 2 | -------------------------------------------------------------------------------- /GHZGame/GHZ Game.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanLahmann/Fun-with-Quantum/HEAD/GHZGame/GHZ Game.pdf -------------------------------------------------------------------------------- /GHZGame/proof1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanLahmann/Fun-with-Quantum/HEAD/GHZGame/proof1.png -------------------------------------------------------------------------------- /GHZGame/shapes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanLahmann/Fun-with-Quantum/HEAD/GHZGame/shapes.png -------------------------------------------------------------------------------- /GHZGame/visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanLahmann/Fun-with-Quantum/HEAD/GHZGame/visualization.png -------------------------------------------------------------------------------- /images/Hardys_paradox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanLahmann/Fun-with-Quantum/HEAD/images/Hardys_paradox.png -------------------------------------------------------------------------------- /QuantumTheory-for-QuantumCoinGame.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanLahmann/Fun-with-Quantum/HEAD/QuantumTheory-for-QuantumCoinGame.pdf -------------------------------------------------------------------------------- /SeriousGames-for-QuantumComputing.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanLahmann/Fun-with-Quantum/HEAD/SeriousGames-for-QuantumComputing.pdf -------------------------------------------------------------------------------- /postBuild: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Trust notebooks to enable RISE autolaunch 3 | jupyter trust Quantum-Coin-Game.ipynb 4 | jupyter trust Hardys-Paradox.ipynb 5 | -------------------------------------------------------------------------------- /3sat3-5.cnf: -------------------------------------------------------------------------------- 1 | c This is an example DIMACS 3-sat file with 3 satisfying solutions: 1 -2 3 0, -1 -2 -3 0, 1 2 -3 0 2 | p cnf 3 5 3 | -1 -2 -3 0 4 | 1 -2 3 0 5 | 1 2 -3 0 6 | 1 -2 -3 0 7 | -1 2 3 0 8 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: Qiskitenv 2 | dependencies: 3 | - python=3.11 4 | - notebook<7 5 | - matplotlib>=3.8 6 | - ipywidgets 7 | - rise 8 | - graphviz 9 | - python-graphviz 10 | - libblas>=3.9.0 11 | - pip 12 | - pip: 13 | - numpy>=1.26 14 | - qiskit>=2.1 15 | - qiskit-aer>=0.15 16 | - qiskit-algorithms>=0.3 17 | - qiskit-ibm-runtime>=0.30 18 | - qiskit-experiments>=0.7 19 | - wget>=3.2 20 | - tabulate>=0.9 21 | - pylatexenc>=2.10 22 | - seaborn>=0.13 -------------------------------------------------------------------------------- /Readme.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "b0812a08", 6 | "metadata": {}, 7 | "source": [ 8 | "# Fun with Quantum\n", 9 | "\"Fun with Quantum\" is a colletion of Jupyter notebooks that highlight specific aspects of Quantum Computing that are interesting and/or fun.\n", 10 | "\n", 11 | "1. [Quantum Coin Game](./Quantum-Coin-Game.ipynb)\n", 12 | "2. [Simple Quantum Implementation for Boolean satisfiability problems](./3sat.ipynb)\n", 13 | "3. [Even Simpler Quantum Implementation for Boolean satisfiability problems](./3sat-v2.ipynb) (under development) \n", 14 | "4. [GHZ Game](./GHZ-Game.ipynb)\n", 15 | "5. [GHZ Game on real devices](./GHZ-on-Real-Devices.ipynb)\n", 16 | "6. [Hardy's Paradox](./Hardys-Paradox.ipynb) (under development) \n", 17 | "7. [Mermin-Peres Magic Square](./Mermin–Peres-Game.ipynb) (under development)" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "id": "c4d34468", 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [] 27 | } 28 | ], 29 | "metadata": { 30 | "kernelspec": { 31 | "display_name": "Python 3 (ipykernel)", 32 | "language": "python", 33 | "name": "python3" 34 | }, 35 | "language_info": { 36 | "codemirror_mode": { 37 | "name": "ipython", 38 | "version": 3 39 | }, 40 | "file_extension": ".py", 41 | "mimetype": "text/x-python", 42 | "name": "python", 43 | "nbconvert_exporter": "python", 44 | "pygments_lexer": "ipython3", 45 | "version": "3.9.13" 46 | } 47 | }, 48 | "nbformat": 4, 49 | "nbformat_minor": 5 50 | } 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | *.old 106 | .DS_Store 107 | Hardys-Paradox-newqiskit.ipynb 108 | 109 | # Claude Code 110 | .claude/ 111 | -------------------------------------------------------------------------------- /GHZGame/ghzGame.py: -------------------------------------------------------------------------------- 1 | from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit 2 | from qiskit_aer import AerSimulator 3 | from random import randint 4 | 5 | def Alice(AlicesColor, AlicesShape): global a_color; global a_shape; a_color=AlicesColor; a_shape = AlicesShape; 6 | def Bob(BobsColor, BobsShape): global b_color; global b_shape; b_color=BobsColor; b_shape = BobsShape; 7 | def You(YourColor, YourShape): global y_color; global y_shape; y_color=YourColor; y_shape = YourShape; 8 | 9 | def geta_color(): return a_color; 10 | def getb_color(): return b_color; 11 | def gety_color(): return y_color; 12 | def geta_shape(): return a_shape; 13 | def getb_shape(): return b_shape; 14 | def gety_shape(): return y_shape; 15 | 16 | def ghz_state(qc,q): 17 | # create GHZ state 18 | qc.h(q[0]) 19 | qc.cx(q[0],q[1]) 20 | qc.cx(q[0],q[2]) 21 | qc.barrier() 22 | return qc 23 | 24 | def runExperiment(): 25 | for i in range (1,5): 26 | correctEinstein = False; correctSchr = False; 27 | # create the quantum circuit with the chosen coin moves 28 | q = QuantumRegister(3) # create a quantum register with one qubit 29 | # create a classical register that will hold the results of the measurement 30 | c = ClassicalRegister(3) 31 | qc = QuantumCircuit(q, c) # creates the quantum circuit 32 | ghz_state(qc,q) # bring circuit in ghz_state 33 | if (i ==1): # ask all for color 34 | # team Einstein (classical) 35 | if (a_color*b_color*y_color == -1): correctEinstein = True; # if the product of the x values = -1 -> win 36 | # team Schrödinger (quantum) 37 | xxx(qc, q) 38 | counts = simulate(qc, q, c, 1) 39 | if ("000" in counts or "011" in counts or "101" in counts or "110" in counts): correctSchr = True; # if the product of the x values = -1 -> win 40 | else: 41 | if (i==2): # if the random number is 1 make an XYY-measurement 42 | if (a_color*b_shape*y_shape == 1): correctEinstein = True; 43 | xyy(qc, q) 44 | elif (i==3): # YXY-measurement 45 | if (a_shape*b_color*y_shape == 1): correctEinstein = True; 46 | yxy(qc, q) 47 | elif (i==4): # YYX-measurement 48 | if (a_shape*b_shape*y_color == 1): correctEinstein = True; 49 | yyx(qc, q) 50 | counts = simulate(qc, q, c, 1) 51 | if ("001" in counts or "010" in counts or "100" in counts or "111" in counts): correctSchr = True; # if product = 1 -> win 52 | print ("Round ", i, ", Question ", i) 53 | if (correctSchr == True and correctEinstein != True): print ("Team Einstein was wrong, Team Schrödinger was right"); 54 | elif (correctSchr != True and correctEinstein == True): print ("Team Einstein was right, Team Schrödinger was wrong"); 55 | else: print ("Both teams were right") 56 | i = i+1; 57 | 58 | def xxx(qc, q): 59 | qc.h(q[0]) 60 | qc.h(q[1]) 61 | qc.h(q[2]) 62 | return qc 63 | 64 | def xyy(qc, q): 65 | qc.h(q[0]) 66 | qc.sdg(q[1]) 67 | qc.h(q[1]) 68 | qc.sdg (q[2]) 69 | qc.h(q[2]) 70 | return qc 71 | 72 | def yxy(qc, q): 73 | qc.sdg(q[0]) 74 | qc.h(q[0]) 75 | qc.h(q[1]) 76 | qc.sdg (q[2]) 77 | qc.h(q[2]) 78 | return qc 79 | 80 | def yyx(qc, q): 81 | qc.sdg(q[0]) 82 | qc.h(q[0]) 83 | qc.h(q[1]) 84 | qc.sdg(q[2]) 85 | qc.h(q[2]) 86 | return qc 87 | 88 | def simulate(qc, q, c, s): 89 | backend = AerSimulator() # define the backend 90 | qc.measure(q,c) 91 | job = backend.run(qc, shots=s) # run the job simulation 92 | result = job.result() # grab the result 93 | counts = result.get_counts(qc) # results for the number of runs 94 | return counts 95 | 96 | def randomQuestion(): 97 | print ("The question your team is getting asked is:") 98 | x = randint (1,4) 99 | if (x == 1): print("Color, color, color"); 100 | if (x == 2): print("Color, shape, shape"); 101 | if (x == 3): print("Shape, shape, color"); 102 | if (x == 4): print("Shape, color, shape"); 103 | return x; 104 | 105 | def correctAnswer(x): # prints out the 106 | print ("Copy the following code in the cell above:\n\n") 107 | if (x==1): 108 | print ("qc.h(q[0])\nqc.h(q[1])\nqc.h(q[2])") 109 | if (x==2): 110 | print ("qc.h(q[0])\nqc.sdg(q[1])\nqc.h(q[1])\nqc.sdg(q[2])\nqc.h(q[2])") 111 | if (x==3): 112 | print ("qc.sdg(q[0])\nqc.h(q[0])\nqc.sdg(q[1])\nqc.h(q[1])\nqc.h(q[2])") 113 | if (x==4): 114 | print ("qc.sdg(q[0])\nqc.h(q[0])\nqc.h(q[1])\nqc.sdg(q[2])\nqc.h(q[2])") 115 | 116 | 117 | def circuitCheck(qc,q,c,x): 118 | counts = simulate(qc, q, c, 100) 119 | if (x==1): # ask all for color 120 | if (("000" in counts or "011" in counts or "101" in counts or "110" in counts) and ("001" not in counts and "010" not in counts and "100" not in counts and "111" not in counts)): 121 | print ("Perfect! Your team won!") 122 | else: print ("Hmmm... There might still be a mistake.") 123 | else: 124 | if (("001" in counts or "010" in counts or "100" in counts or "111" in counts) and ("000" not in counts and "011" not in counts and "101" not in counts and "110" not in counts)): 125 | print ("Perfect! Your team won!") 126 | else: print ("Hmmm... There might still be a mistake.") 127 | 128 | def quiz(): 129 | print ("(a) All 8 possible states equally mixed (|000>, |001>, |010>, ..., |110>, |111>)\n(b) A random distribution across all 8 states (|000>, |001>, |010>, ..., |110>, |111>)\n(c) Measurement result in 50% is state |000> and in 50% is state |100>\n(d) Measurement result in 50% is state |000> and in 50% is state |111>") 130 | answer = input() 131 | if answer == "d" or answer == "D": 132 | print ("Your answer was correct!") 133 | else: 134 | print ("No, try again!") 135 | 136 | -------------------------------------------------------------------------------- /QuantumVolume.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "pycharm": { 7 | "name": "#%% md\n" 8 | }, 9 | "slideshow": { 10 | "slide_type": "slide" 11 | } 12 | }, 13 | "source": "# Simple Notebook to run Quantum Volume Experiments\n\nThis notebook explores basic aspects of Quantum Volume, which can be used to quantify the computational power of a quantum device. It can be used to measure the Quantum Volume of a specific device or backend.\n\nIt is based on the [Qiskit Textbook](https://qiskit.org/textbook/ch-quantum-hardware/measuring-quantum-volume.html), a [Qiskit tutorial](https://qiskit.org/documentation/tutorials/noise/5_quantum_volume.html) and in particular the following [tutorial](https://qiskit-community.github.io/qiskit-experiments/manuals/verification/quantum_volume.html).\n\n**Note:** This notebook requires compatible versions of qiskit-experiments with Qiskit 2.x. If you encounter errors, check version compatibility at [qiskit-experiments releases](https://github.com/qiskit-community/qiskit-experiments/releases).\n\nImplemented by [Jan-R. Lahmann](http://twitter.com/JanLahmann) using Qiskit." 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": 1, 18 | "metadata": { 19 | "pycharm": { 20 | "name": "#%%\n" 21 | }, 22 | "scrolled": true 23 | }, 24 | "outputs": [], 25 | "source": [ 26 | "# Based on: https://qiskit-community.github.io/qiskit-experiments/manuals/verification/quantum_volume.html\n", 27 | "\n", 28 | "from qiskit_experiments.library import QuantumVolume\n", 29 | "from qiskit_aer import AerSimulator\n", 30 | "from qiskit.primitives import BackendSamplerV2 as Sampler # Local sampler, not IBM Runtime\n", 31 | "import datetime\n", 32 | "\n", 33 | "# For simulation\n", 34 | "backend = AerSimulator(seed_simulator=42)\n", 35 | "sampler = Sampler(backend=backend)" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 2, 41 | "metadata": { 42 | "pycharm": { 43 | "name": "#%%\n" 44 | }, 45 | "scrolled": false 46 | }, 47 | "outputs": [ 48 | { 49 | "name": "stdout", 50 | "output_type": "stream", 51 | "text": [ 52 | "2022-09-15 11:53:33.412783\n", 53 | "Number of Qubits: 1\n", 54 | "Quantum Volume 2 verified\n", 55 | "\n", 56 | "2022-09-15 11:53:34.113093\n", 57 | "Number of Qubits: 2\n", 58 | "Quantum Volume 4 verified\n", 59 | "\n", 60 | "2022-09-15 11:53:35.138585\n", 61 | "Number of Qubits: 3\n", 62 | "Quantum Volume 8 verified\n", 63 | "\n", 64 | "2022-09-15 11:53:36.269130\n", 65 | "Number of Qubits: 4\n", 66 | "Quantum Volume 16 verified\n", 67 | "\n", 68 | "2022-09-15 11:53:37.567512\n", 69 | "Number of Qubits: 5\n", 70 | "Quantum Volume 32 verified\n", 71 | "\n", 72 | "AnalysisResult\n", 73 | "- name: mean_HOP\n", 74 | "- value: 0.864+/-0.034\n", 75 | "- quality: good\n", 76 | "- extra: <4 items>\n", 77 | "- device_components: ['Q0', 'Q1', 'Q2', 'Q3', 'Q4']\n", 78 | "- verified: False\n", 79 | "\n", 80 | "AnalysisResult\n", 81 | "- name: quantum_volume\n", 82 | "- value: 32\n", 83 | "- quality: good\n", 84 | "- extra: <4 items>\n", 85 | "- device_components: ['Q0', 'Q1', 'Q2', 'Q3', 'Q4']\n", 86 | "- verified: False\n", 87 | "\n", 88 | "\n", 89 | "mean_HOP extra:\n", 90 | "- HOPs: [0.8515625, 0.8857421875, 0.861328125, 0.865234375, 0.7978515625, 0.8330078125, 0.8662109375, 0.896484375, 0.892578125, 0.8828125, 0.888671875, 0.931640625, 0.8740234375, 0.8369140625, 0.931640625, 0.8896484375, 0.8310546875, 0.9287109375, 0.83984375, 0.78515625, 0.84765625, 0.8330078125, 0.8857421875, 0.90625, 0.8134765625, 0.853515625, 0.9189453125, 0.87109375, 0.8720703125, 0.9111328125, 0.8330078125, 0.87890625, 0.8701171875, 0.8505859375, 0.904296875, 0.8818359375, 0.8095703125, 0.9306640625, 0.8525390625, 0.849609375, 0.92578125, 0.8125, 0.8994140625, 0.8994140625, 0.8916015625, 0.8603515625, 0.8740234375, 0.8369140625, 0.833984375, 0.8828125, 0.873046875, 0.853515625, 0.84375, 0.8759765625, 0.8359375, 0.92578125, 0.837890625, 0.84765625, 0.8076171875, 0.8251953125, 0.9091796875, 0.8515625, 0.8251953125, 0.8583984375, 0.7900390625, 0.8486328125, 0.837890625, 0.8828125, 0.880859375, 0.865234375, 0.9404296875, 0.86328125, 0.8369140625, 0.8759765625, 0.8681640625, 0.8359375, 0.88671875, 0.8798828125, 0.896484375, 0.90625, 0.8193359375, 0.8037109375, 0.9189453125, 0.892578125, 0.8623046875, 0.84765625, 0.8388671875, 0.8271484375, 0.9130859375, 0.8779296875, 0.8251953125, 0.83203125, 0.8681640625, 0.8759765625, 0.8369140625, 0.8525390625, 0.853515625, 0.8203125, 0.8720703125, 0.8388671875]\n", 91 | "- two_sigma: 0.06854858624470282\n", 92 | "- depth: 5\n", 93 | "- trials: 100\n", 94 | "\n", 95 | "quantum_volume extra:\n", 96 | "- success: True\n", 97 | "- confidence: 0.9999999957624716\n", 98 | "- depth: 5\n", 99 | "- trials: 100\n" 100 | ] 101 | } 102 | ], 103 | "source": [ 104 | "max_qubits = 5 #maximum number of Qubits to use. QV can be up to 2^max_qubits\n", 105 | "for i in range(1, max_qubits+1):\n", 106 | " print(datetime.datetime.now())\n", 107 | " print(\"Number of Qubits:\", i)\n", 108 | " qubits = tuple(range(i))\n", 109 | " exp = QuantumVolume(qubits, trials=100, seed=42)\n", 110 | " exp.set_transpile_options(optimization_level=3)\n", 111 | " # Pass local sampler to avoid IBM Runtime interception\n", 112 | " result = exp.run(backend, sampler=sampler).block_for_results()\n", 113 | " qv = result.analysis_results(\"quantum_volume\", dataframe=True).value.iloc[0]\n", 114 | " print(f\"Quantum Volume {qv} verified\")\n", 115 | " print(\"\")\n", 116 | "\n", 117 | "\n", 118 | "# View result data\n", 119 | "for res in result.analysis_results(dataframe=False):\n", 120 | " print(res)\n", 121 | " print()\n", 122 | "\n", 123 | "# Print extra data\n", 124 | "for res in result.analysis_results(dataframe=False):\n", 125 | " print(f\"\\n{res.name} extra:\")\n", 126 | " for key, val in res.extra.items():\n", 127 | " print(f\"- {key}: {val}\")" 128 | ] 129 | } 130 | ], 131 | "metadata": { 132 | "anaconda-cloud": {}, 133 | "celltoolbar": "Raw Cell Format", 134 | "kernelspec": { 135 | "display_name": "Python 3 (ipykernel)", 136 | "language": "python", 137 | "name": "python3" 138 | }, 139 | "language_info": { 140 | "codemirror_mode": { 141 | "name": "ipython", 142 | "version": 3 143 | }, 144 | "file_extension": ".py", 145 | "mimetype": "text/x-python", 146 | "name": "python", 147 | "nbconvert_exporter": "python", 148 | "pygments_lexer": "ipython3", 149 | "version": "3.9.13" 150 | }, 151 | "latex_envs": { 152 | "bibliofile": "biblio.bib", 153 | "cite_by": "apalike", 154 | "current_citInitial": 1, 155 | "eqLabelWithNumbers": true, 156 | "eqNumInitial": 0 157 | }, 158 | "nav_menu": {}, 159 | "toc": { 160 | "navigate_menu": true, 161 | "number_sections": true, 162 | "sideBar": true, 163 | "threshold": 6, 164 | "toc_cell": false, 165 | "toc_section_display": "block", 166 | "toc_window_display": false 167 | } 168 | }, 169 | "nbformat": 4, 170 | "nbformat_minor": 1 171 | } -------------------------------------------------------------------------------- /3sat-v2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "pycharm": { 7 | "name": "#%% md\n" 8 | }, 9 | "slideshow": { 10 | "slide_type": "slide" 11 | } 12 | }, 13 | "source": [ 14 | "# Simple Quantum Implementation using Qiskit Aqua for Boolean satisfiability problems\n", 15 | "\n", 16 | "\n", 17 | "\n", 18 | "This Jupyter notebook demonstrates how easy it is to use quantum algorithms from [Qiskit Aqua](https://qiskit.org/aqua) to solve Boolean satisfiability problems [(SAT)](https://en.wikipedia.org/wiki/Boolean_satisfiability_problem). \n", 19 | "\n", 20 | "It is based on the Qiskit tutorial [\n", 21 | "Using Grover search for boolean satisfiability](https://github.com/JavaFXpert/qiskit4devs-workshop-notebooks/blob/master/grover_search_party.ipynb) by [James Weaver](http://twitter.com/JavaFXpert).\n", 22 | "\n", 23 | "Implemented by [Jan-R. Lahmann](http://twitter.com/JanLahmann) using Qiskit, binder and RISE." 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": { 29 | "pycharm": { 30 | "name": "#%% md\n" 31 | }, 32 | "slideshow": { 33 | "slide_type": "slide" 34 | } 35 | }, 36 | "source": [ 37 | "## Boolean Satisfiabilty problems (SAT)\n", 38 | "\n", 39 | "The Boolean satisfiability problem [(SAT)](https://en.wikipedia.org/wiki/Boolean_satisfiability_problem) considers a Boolean expression with N boolean variables involving negation (NOT, $\\neg$), conjunction (AND, $\\wedge$) and disjunction (OR, $\\vee$), as in the following (simple) example: \n", 40 | "$$ f(x_1, x_2) = (x_1 \\vee x_2) \\wedge (x_1 \\vee \\neg x_2) . $$\n", 41 | "\n", 42 | "The problem is to determine whether there is any assignment of values (TRUE, FALSE) to the Boolean variables which makes the formula true. \n", 43 | "\n", 44 | "It's something like trying to flip a bunch of switches to find the setting that makes a light bulb turn on. \n", 45 | "SAT is of central importance in many areas of computer science, including complexity theory, algorithmics, cryptography, artificial intelligence, circuit design, and automatic theorem proving." 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "metadata": { 51 | "pycharm": { 52 | "name": "#%% md\n" 53 | }, 54 | "slideshow": { 55 | "slide_type": "subslide" 56 | } 57 | }, 58 | "source": [ 59 | "SAT was the first problem proven to be NP-complete. \n", 60 | "This means that all problems in the [complexity class NP](https://en.wikipedia.org/wiki/NP_(complexity)) are at most as difficult to solve as SAT. \n", 61 | "\n", 62 | "There is no known classical algorithm that efficiently solves each SAT problem, and it is generally believed that no such algorithm exists. \n", 63 | "Whether Boolean satisfiability problems have a classical algorithm that is polynomial in time is equivalent to the [P vs. NP problem](https://en.wikipedia.org/wiki/P_versus_NP_problem). \n", 64 | "\n", 65 | "\n", 66 | "While [Grover's quantum search algorithm](https://en.wikipedia.org/wiki/Grover's_algorithm) does not provide exponential speed-up to this problem, it may nevertheless provide some speed-up in contrast to classical black-box search strategies.\n", 67 | "\n" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": { 73 | "pycharm": { 74 | "name": "#%% md\n" 75 | }, 76 | "slideshow": { 77 | "slide_type": "slide" 78 | } 79 | }, 80 | "source": [ 81 | "## Solving 3-SAT using Qiskit Aqua and Grover's search algorithm\n", 82 | "### Throwing a party while avoiding the drama\n", 83 | "\n", 84 | "Imagine you are inviting some friends to a party, some who are couples, and some who are not on speaking terms. \n", 85 | "\n", 86 | "Specifically, **Alice** and **Bob** are in a relationship, as are **Carol** and **David**. However, **Alice** and **David** had a bad breakup a while ago and haven't been civil with each other since. \n", 87 | "\n", 88 | "Armed with a quantum computer and Qiskit Aqua, how can you leverage Grover's quantum search algorithm to identify friendly combinations of people to invite?\n", 89 | "\n", 90 | "The constraints for our party planning problem may be formulated with the following boolean expression:\n", 91 | "\n", 92 | " ((A and B) or (C and D)) and not (A and D)\n" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": { 98 | "pycharm": { 99 | "name": "#%% md\n" 100 | }, 101 | "slideshow": { 102 | "slide_type": "slide" 103 | } 104 | }, 105 | "source": [ 106 | "To apply a quantum algorithm from Qiskit Aqua to this problem, we simply need to import the Qiskit libraries and run the algorithm with the appropriate parameters." 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": null, 112 | "metadata": { 113 | "slideshow": { 114 | "slide_type": "fragment" 115 | } 116 | }, 117 | "outputs": [], 118 | "source": [ 119 | "# Import the necessary functions from Qiskit\n", 120 | "import qiskit\n", 121 | "from qiskit.visualization import plot_histogram\n", 122 | "from qiskit.circuit.library import PhaseOracle\n", 123 | "from qiskit.primitives import StatevectorSampler\n", 124 | "from qiskit_algorithms import Grover, AmplificationProblem" 125 | ] 126 | }, 127 | { 128 | "cell_type": "markdown", 129 | "metadata": { 130 | "pycharm": { 131 | "name": "#%% md\n" 132 | }, 133 | "slideshow": { 134 | "slide_type": "slide" 135 | } 136 | }, 137 | "source": [ 138 | "Let's go ahead and use our expression in a Grover quantum search to find out compatible combinations of people to invite.\n", 139 | "\n", 140 | "Note: We'll represent and with \"&\", or with \"|\", not with \"~\" in our expression." 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": 2, 146 | "metadata": { 147 | "pycharm": { 148 | "name": "#%%\n" 149 | }, 150 | "slideshow": { 151 | "slide_type": "fragment" 152 | } 153 | }, 154 | "outputs": [], 155 | "source": [ 156 | "# Define the formal logical expression corresponding to our \"party problem\"\n", 157 | "log_expr = \"((A & B) | (C & D)) & ~(A & D)\"" 158 | ] 159 | }, 160 | { 161 | "cell_type": "markdown", 162 | "metadata": { 163 | "pycharm": { 164 | "name": "#%% md\n" 165 | }, 166 | "slideshow": { 167 | "slide_type": "slide" 168 | } 169 | }, 170 | "source": [ 171 | "Let's run the algorithm on a quantum simulator and plot the result.\n", 172 | "\n", 173 | "Each basis state represents our four friends, with the least significant bit representing Alice. If a bit is 1, then the advice is to invite the person that the bit represents. If the bit is 0, then Grover advises not to send an invitation." 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": null, 179 | "metadata": { 180 | "slideshow": { 181 | "slide_type": "fragment" 182 | } 183 | }, 184 | "outputs": [], 185 | "source": "# Run the algorithm on a simulator, and print a histogram of the result\n# The logical expression can automatically be transformed into a form suitable for Grover's algorithm\noracle = PhaseOracle(log_expr)\nsampler = StatevectorSampler()\ngrover = Grover(sampler=sampler)\nproblem = AmplificationProblem(oracle)\nresult = grover.amplify(problem)\nplot_histogram(result.circuit_results)" 186 | }, 187 | { 188 | "cell_type": "markdown", 189 | "metadata": { 190 | "pycharm": { 191 | "name": "#%% md\n" 192 | }, 193 | "slideshow": { 194 | "slide_type": "fragment" 195 | } 196 | }, 197 | "source": [ 198 | "The result shows that the assignments 0011, 0111, 1100, 1110 for David-Carol-Bob-Alice are potential solutions to the problem. \n", 199 | "Whether or not these are correct solutions to the problem can be verified efficiently, as 3-SAT is in NP. \n", 200 | "Note that the variables in the histogram are in reverse order: David-Carol-Bob-Alice instead of Alice-Bob-Carol-David.\n" 201 | ] 202 | }, 203 | { 204 | "cell_type": "markdown", 205 | "metadata": { 206 | "pycharm": { 207 | "name": "#%% md\n" 208 | }, 209 | "slideshow": { 210 | "slide_type": "slide" 211 | } 212 | }, 213 | "source": [ 214 | "### Now it's your turn to play!\n", 215 | "\n", 216 | "Create and implement your own scenario that can be modeled as a boolean satisfiability problem using Grover search. Have fun with it, and carry on with your quantum computing journey!" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 4, 222 | "metadata": { 223 | "pycharm": { 224 | "name": "#%%\n" 225 | }, 226 | "slideshow": { 227 | "slide_type": "fragment" 228 | } 229 | }, 230 | "outputs": [], 231 | "source": [ 232 | "# Define the formal logical expression corresponding to our \"party problem\"\n", 233 | "log_expr = '((A & C) | (B & D)) & ~(A & D)'" 234 | ] 235 | }, 236 | { 237 | "cell_type": "code", 238 | "execution_count": null, 239 | "metadata": { 240 | "pycharm": { 241 | "name": "#%%\n" 242 | }, 243 | "slideshow": { 244 | "slide_type": "fragment" 245 | } 246 | }, 247 | "outputs": [], 248 | "source": "# Run the algorithm \noracle = PhaseOracle(log_expr)\nsampler = StatevectorSampler()\ngrover = Grover(sampler=sampler)\nproblem = AmplificationProblem(oracle)\nresult = grover.amplify(problem)\nplot_histogram(result.circuit_results)" 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": null, 253 | "metadata": { 254 | "pycharm": { 255 | "name": "#%%\n" 256 | }, 257 | "slideshow": { 258 | "slide_type": "fragment" 259 | } 260 | }, 261 | "outputs": [], 262 | "source": "# Print Qiskit version \nprint(f\"Qiskit version: {qiskit.__version__}\")" 263 | } 264 | ], 265 | "metadata": { 266 | "anaconda-cloud": {}, 267 | "celltoolbar": "Slideshow", 268 | "kernelspec": { 269 | "display_name": "Python 3 (ipykernel)", 270 | "language": "python", 271 | "name": "python3" 272 | }, 273 | "language_info": { 274 | "codemirror_mode": { 275 | "name": "ipython", 276 | "version": 3 277 | }, 278 | "file_extension": ".py", 279 | "mimetype": "text/x-python", 280 | "name": "python", 281 | "nbconvert_exporter": "python", 282 | "pygments_lexer": "ipython3", 283 | "version": "3.9.13" 284 | }, 285 | "latex_envs": { 286 | "bibliofile": "biblio.bib", 287 | "cite_by": "apalike", 288 | "current_citInitial": 1, 289 | "eqLabelWithNumbers": true, 290 | "eqNumInitial": 0 291 | }, 292 | "livereveal": { 293 | "autolaunch": true 294 | }, 295 | "nav_menu": {}, 296 | "toc": { 297 | "navigate_menu": true, 298 | "number_sections": true, 299 | "sideBar": true, 300 | "threshold": 6, 301 | "toc_cell": false, 302 | "toc_section_display": "block", 303 | "toc_window_display": false 304 | } 305 | }, 306 | "nbformat": 4, 307 | "nbformat_minor": 1 308 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fun with Quantum 2 | 3 | "Fun with Quantum" is a collection of Jupyter notebooks that highlight specific aspects of Quantum Computing that are interesting and/or fun. 4 | 5 | 1. Quantum Coin Game (superposition & interference)) 6 | 2. Simple Quantum Implementation for Boolean satisfiability problems 7 | 3. Even Simpler Quantum Implementation for Boolean satisfiability problems (under development) 8 | 4. Hardy's Paradox (complementary observables & the problem with classical logic) 9 | 5. GHZ Game (entanglement) 10 | 6. GHZ Game on real devices 11 | 7. Mermin-Peres Magic Square (under development) 12 | 13 | 14 | --- 15 | 16 | ## Came here from Linux Magazine? 17 | 18 | Find the notebook in the repository under the name [Codebeispiele-Linux-Magazin.ipynb](https://github.com/JanLahmann/Fun-with-Quantum/blob/master/Codebeispiele-Linux-Magazin.ipynb). 19 | 20 | View - and play the game online, without any install - in Binder: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/JanLahmann/Fun-with-Quantum/master?filepath=Codebeispiele-Linux-Magazin.ipynb) 21 | 22 | --- 23 | 24 | ### How to execute the Examples in the IBM Quantum Lab 25 | You can run the jupyter notebooks with the different Serious Games in the IBM Quantum Lab without the need for a local software install. 26 | 1. Open the Quantum Lab in your browser: https://quantum-computing.ibm.com/ 27 | 2. If you already have an ID sign in to IBM Quantum.
28 | Otherwise, please create an IBMid by clicking `Create an IBM account`, or use one of the other ID options. 29 | 3. Launch the Quantum Lab. 30 | 4. Create a new file by clicking on the blue button `New file +`. 31 | 5. Delete the code from the first cell and enter 32 | ```python 33 | !git clone https://github.com/JanLahmann/Fun-with-Quantum 34 | ``` 35 | 6. Execute the cell with `Shift + Enter`. The Fun-with-Quantum Repository will now be cloned in your IBM Quantum Lab.
36 | To see the new folder `Fun-with-Quantum` in the directory-tree on the left side, close and open the Lab files side menu. 37 | 7. Click `Fun-with-Quantum` to enter the new directory. 38 | 8. Open the `Readme.ipynb`-file and follow the links to the different examples. 39 | 40 | --- 41 | ### RasQberry: Exploring Quantum Computing and Qiskit with a Raspberry Pi and a 3D Printer 42 | 43 | Please have a look at the RasQberry project at https://github.com/JanLahmann/RasQberry. 44 | 45 | RasQberry integrates Qiskit, a Raspberry Pi (the full range from Pi 4 down to a Pi Zero) and a 3D printed model of IBM Q System One to explore various state-of-the-art technologies and create a tool that can be used in meetings, meetups, demo booths, etc. A spectrum of Quantum Computing demos and Serious Games for Quantum Computing (that illustrate superposition, interference and entanglement) will being made available on this device for an engaging introduction to Quantum Computing. 46 | 47 | --- 48 | ### 1. Quantum Coin Game 49 | A quantum coin game that illustrates the power of quantum superposition and interference - implemented by Jan-R. Lahmann using [Qiskit](https://qiskit.org), [binder](https://mybinder.org) and [RISE](https://rise.readthedocs.io/en/stable/). 50 | 51 | Inspired by the TED talk of Shohini Ghose ["Quantum computing explained in 10 minutes"](https://www.ted.com/talks/shohini_ghose_quantum_computing_explained_in_10_minutes) 52 | 53 | View - and play the game online, without any install - in Binder: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/JanLahmann/Fun-with-Quantum/master?filepath=Quantum-Coin-Game.ipynb) 54 | 55 | A slightly more current version of this Quantum Coin Game is now part of the official Qiskit Community Tutorials and can be played at http://ibm.biz/QiskitCoinGame 56 | 57 | ---- 58 | ### 2. Simple Quantum Implementation for Boolean satisfiability problems 59 | 60 | A simple implementation to solve Boolean satisfiability problems ("3SAT") using Qiskit and Grover's Quantum Search Algorithm. The aim is to show how easy such a problem can be solved on a Quantum Computer using Qiskit. To keep it as simple as possible, the theory is not explained in this notebook. 61 | 62 | Walk through this demo (and change it if you like) in Binder: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/JanLahmann/Fun-with-Quantum/master?filepath=3sat.ipynb) 63 | 64 | --- 65 | ### 3. Even Simpler Quantum Implementation for Boolean satisfiability problems (under development) 66 | 67 | An even simpler implementation to solve Boolean satisfiability problems ("3SAT") using Qiskit and Grover's Quantum Search Algorithm. 68 | 69 | Walk through this demo (and change it if you like) in Binder: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/JanLahmann/Fun-with-Quantum/master?filepath=3sat-v2.ipynb) 70 | 71 | --- 72 | ### 4. Hardy's Paradox 73 | 74 | Hardy's Paradox nicely illustrates the fundamental difference of Quantum Mechanics and classical physics. Learn about complementary oberservables and why classical logic is not applicable to quantum mechanics. 75 | 76 | A tutorial that discusses a specific version of the Einstein-Podolsky-Rosen (EPR) Paradox - implemented by Jan-R. Lahmann & Bengt Wegner using [Qiskit](https://qiskit.org), [binder](https://mybinder.org) and [RISE](https://rise.readthedocs.io/en/stable/), based on an idea in a former version of the [Qiskit Textbook](https://qiskit.org/textbook) 77 | 78 | View - and play the game online, without any install - in Binder: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/JanLahmann/Fun-with-Quantum/master?filepath=Hardys-Paradox.ipynb) 79 | 80 | --- 81 | ### 5. GHZ Game 82 | 83 | A quantum game that illustrates the power of quantum entanglement - implemented by Isabell Heider using [Qiskit](https://qiskit.org) and [binder](https://mybinder.org). 84 | 85 | For an introductions to the GHZ Game, please take a look at the following [presentation](https://github.com/JanLahmann/Fun-with-Quantum/blob/master/GHZGame/GHZ%20Game.pdf) by Jana Foehlisch. 86 | 87 | View - and play the game online, without any install - in Binder: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/JanLahmann/Fun-with-Quantum/master?filepath=GHZ-Game.ipynb) 88 | 89 | --- 90 | ### 6. GHZ Game on real devices 91 | 92 | Analyzing different techniques how to improve the results of playing the GHZ game on real quantum devices - implemented by Lennart Schulze using [Qiskit](https://qiskit.org) and [binder](https://mybinder.org). 93 | 94 | This notebook compares several IBM Quantum devices, explains how to (manually) optimize a circuit for a specific device, how to use the Qiskit transpiler and its optimizations, and discusses Measurement Error Mitigation. 95 | 96 | View - and play the game online on real quantum devices, without any install - in Binder: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/JanLahmann/Fun-with-Quantum/master?filepath=GHZ-on-Real-Devices.ipynb) 97 | 98 | --- 99 | ### 7. Mermin-Peres Magic Square (under development) 100 | A base version of the Mermin-Peres magic square game demonstrating the computational power of quantum computers, which can be further extended to outperform classical machines - extended by Jan-R. Lahmann & David Drexlin with [Qiskit](https://qiskit.org). Based on the Medium article [This Proof Demonstrates a Quantum Advantage, Even for Noisy Quantum Computers](https://medium.com/qiskit/this-proof-demonstrates-a-quantum-advantage-even-for-noisy-quantum-computers-b44a738801ad). 101 | 102 | View - and play the game online, without any install - in Binder: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/JanLahmann/Fun-with-Quantum/master?filepath=Mermin–Peres-Game.ipynb) 103 | 104 | --- 105 | ## Usage instructions for the RISE Slideshow Extension 106 | Some binder images in this repository automatically launch RISE, a Jupyter/IPython Slideshow Extension. 107 | Navigation is easy: 108 | * "Ctrl -" and "Ctrl +" (or "command -", "command +") adjust the zoom level to fit the text to the browser window 109 | * Use "Space" and "Shift Space" to navigate through the slides (right & left arrow keys also work, but might skip some slides) 110 | * "Shift + Enter" executes the interactive cells (might need to click the cell, first) 111 | * Execute the interactive cells on each slide ("In [1]:", etc) 112 | * In case a cell is not formatted correctly, try to double-click and then "Shift Enter" to re-execute 113 | * Interactive cells can be modified, if needed 114 | * "X" at the top left exits the slideshow and enters the jupyter notebook interface 115 | 116 | --- 117 | ### THINK 2021 Lab - Explore Quantum Computing with Serious Games 118 | 119 | In this lab, we will give an overview of serious games for quantum computing and use several Quantum Games that make the fundamental concepts of Superposition, Interference and Entanglement tangible and understandable for beginners. In addition, we will use real quantum computers and explore how to mitigate noise and errors. 120 | 121 | 122 | As a first introduction look at [these slides](https://github.com/JanLahmann/Fun-with-Quantum/blob/master/SeriousGames-for-QuantumComputing.pdf) which provide an Overview about the topic of Serious Games in the area of Quantum Computing. 123 | Then you can go through some of the Serious Games to experience the differences and possibilities that Quantum Computing provides and get a first understanding on how to program today's Quantum Computers and Quantum Simulators: 124 | 125 | 126 | 1. **Quantum Coin Game:** this Game provides an introduction to the concepts of Superposition and Interference. Go ahead and try it out for yourself: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/JanLahmann/Fun-with-Quantum/master?filepath=Quantum-Coin-Game.ipynb) (opens as interactive slide show) 127 | 2. **GHZ-Game:** in this game you will learn the basics about the concept of Entanglement and experience the effect that it can have based on a simple riddle: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/JanLahmann/Fun-with-Quantum/master?filepath=GHZ-Game.ipynb) (opens as Jupyter Notebook) 128 | 3. **GHZ on real devices:** to go one step further you can now try the GHZ-Game on a real quantum computer device. In this notebook you can experience the influence noise has on today's Quantum Computers and learn the fundamentals about error mitigation: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/JanLahmann/Fun-with-Quantum/master?filepath=GHZ-on-Real-Devices.ipynb) (opens as Jupyter Notebook) 129 | 130 | 131 | --- 132 | ### IEEE QCE20 Tutorial on "Serious Games for Quantum Computing" 133 | 134 | For the [tutorial "Serious Games for Quantum Computing"](https://qce.quantum.ieee.org/tutorials/#tut-lahmann-heider) as part of the IEEE QCE20 conference, please use the following URLs to launch the tutorial notebooks: 135 | 136 | Part 2 "Quantum Coin Game": [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/JanLahmann/Fun-with-Quantum/master?filepath=Quantum-Coin-Game.ipynb) 137 | 138 | Part 3.1 "GHZ Game": [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/JanLahmann/Fun-with-Quantum/master?filepath=GHZ-Game.ipynb) 139 | 140 | Part 3.2 "GHZ Game on real devices": [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/JanLahmann/Fun-with-Quantum/master?filepath=GHZ-on-Real-Devices.ipynb) 141 | 142 | A recording of the three 1h tutorial sessions ["Serious Games for Quantum Computing"](https://qce.quantum.ieee.org/tutorials/#tut-lahmann-heider) from the [IEEE International Conference on Quantum Computing and Engineering (QCE20)](https://qce.quantum.ieee.org/) are available here: [part 1](https://ibm.box.com/v/IEEE-QCE20-QSeriousGames-1), [part 2](https://ibm.box.com/v/IEEE-QCE20-QSeriousGames-2), [part 3](https://ibm.box.com/v/IEEE-QCE20-QSeriousGames-3). The agenda for the three parts is [here](https://ibm.box.com/v/IEEE-QCE20-QSeriousGames-0). 143 | 144 | --- 145 | Jan-R. Lahmann, http://twitter.com/JanLahmann 146 | 147 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /3sat.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "pycharm": { 7 | "name": "#%% md\n" 8 | }, 9 | "slideshow": { 10 | "slide_type": "slide" 11 | } 12 | }, 13 | "source": [ 14 | "# Simple Quantum Implementation using Qiskit Aqua for Boolean satisfiability problems\n", 15 | "\n", 16 | "\n", 17 | "\n", 18 | "This Jupyter notebook demonstrates how easy it is to use quantum algorithms from [Qiskit Aqua](https://qiskit.org/aqua) to solve Boolean satisfiability problems [(SAT)](https://en.wikipedia.org/wiki/Boolean_satisfiability_problem). \n", 19 | "\n", 20 | "It is based on the Qiskit tutorial [Using Grover search for 3-SAT problems](https://github.com/Qiskit/qiskit-tutorials/blob/master/community/aqua/optimization/grover.ipynb) by [Jay Gambetta](https://github.com/jaygambetta) and [Richard Chen](https://github.com/chunfuchen) and a hands-on workshop by David Mesterhazy.\n", 21 | "\n", 22 | "Implemented by [Jan-R. Lahmann](http://twitter.com/JanLahmann) using Qiskit, binder and RISE." 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": { 28 | "pycharm": { 29 | "name": "#%% md\n" 30 | }, 31 | "slideshow": { 32 | "slide_type": "slide" 33 | } 34 | }, 35 | "source": [ 36 | "## Boolean Satisfiabilty problems (SAT)\n", 37 | "\n", 38 | "The Boolean satisfiability problem [(SAT)](https://en.wikipedia.org/wiki/Boolean_satisfiability_problem) considers a Boolean expression with N boolean variables involving negation (NOT, $\\neg$), conjunction (AND, $\\wedge$) and disjunction (OR, $\\vee$), as in the following (simple) example: \n", 39 | "$$ f(x_1, x_2) = (x_1 \\vee x_2) \\wedge (x_1 \\vee \\neg x_2) . $$\n", 40 | "\n", 41 | "The problem is to determine whether there is any assignment of values (TRUE, FALSE) to the Boolean variables which makes the formula true. \n", 42 | "\n", 43 | "It's something like trying to flip a bunch of switches to find the setting that makes a light bulb turn on. \n", 44 | "SAT is of central importance in many areas of computer science, including complexity theory, algorithmics, cryptography, artificial intelligence, circuit design, and automatic theorem proving." 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": { 50 | "pycharm": { 51 | "name": "#%% md\n" 52 | }, 53 | "slideshow": { 54 | "slide_type": "subslide" 55 | } 56 | }, 57 | "source": [ 58 | "SAT was the first problem proven to be NP-complete. \n", 59 | "This means that all problems in the [complexity class NP](https://en.wikipedia.org/wiki/NP_(complexity)) are at most as difficult to solve as SAT. \n", 60 | "\n", 61 | "There is no known classical algorithm that efficiently solves each SAT problem, and it is generally believed that no such algorithm exists. \n", 62 | "Whether Boolean satisfiability problems have a classical algorithm that is polynomial in time is equivalent to the [P vs. NP problem](https://en.wikipedia.org/wiki/P_versus_NP_problem). \n", 63 | "\n", 64 | "\n", 65 | "While [Grover's quantum search algorithm](https://en.wikipedia.org/wiki/Grover's_algorithm) does not provide exponential speed-up to this problem, it may nevertheless provide some speed-up in contrast to classical black-box search strategies.\n", 66 | "\n" 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": { 72 | "pycharm": { 73 | "name": "#%% md\n" 74 | }, 75 | "slideshow": { 76 | "slide_type": "subslide" 77 | } 78 | }, 79 | "source": [ 80 | "### Basic definitions and terminology\n", 81 | "\n", 82 | "\n", 83 | "A *literal* is either a variable, or the negation of a variable. \n", 84 | "A *clause* is a disjunction (OR, $\\vee$) of literals, or a single literal. \n", 85 | "A formula is in *conjunctive normal form* [(CNF)](https://en.wikipedia.org/wiki/Conjunctive_normal_form) if it is a conjunction (AND, $\\wedge$) of clauses, or a single clause. \n", 86 | "\n", 87 | "A problem in conjunctive normal form is called *3-SAT* if each clause is limited to at most three literals. \n", 88 | "3-SAT is also NP-complete.\n", 89 | "\n", 90 | "Example for 3-SAT: $ (x_1 \u2228 \u00acx_2) \u2227 (\u00acx_1 \u2228 x_2 \u2228 x_3) \u2227 \u00acx_1 $.\n" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": { 96 | "pycharm": { 97 | "name": "#%% md\n" 98 | }, 99 | "slideshow": { 100 | "slide_type": "slide" 101 | } 102 | }, 103 | "source": [ 104 | "## Solving 3-SAT using Qiskit Aqua \n", 105 | "\n", 106 | "We will show how to solve a 3-SAT problem using quantum algorithms from [Qiskit Aqua](https://qiskit.org/aqua).\n", 107 | "\n", 108 | "Let us consider three Boolean variables $x_1, x_2, x_3$ and a Boolean function $f$ given by:\n", 109 | "\n", 110 | "\\begin{align*} \n", 111 | "f(x_1, x_2, x_3) \\;= &\\;\\;\\;\\;\n", 112 | "\\;(\\neg x_1 \\vee \\neg x_2 \\vee \\neg x_3) \\\\\n", 113 | "&\\;\\; \\wedge \\; ( x_1 \\vee \\neg x_2 \\vee x_3) \\\\\n", 114 | "&\\;\\; \\wedge \\;( x_1 \\vee x_2 \\vee \\neg x_3) \\\\\n", 115 | "&\\;\\; \\wedge \\;( x_1 \\vee \\neg x_2 \\vee \\neg x_3) \\\\\n", 116 | "&\\;\\; \\wedge \\;(\\neg x_1 \\vee x_2 \\vee x_3) \n", 117 | "\\end{align*}\n" 118 | ] 119 | }, 120 | { 121 | "cell_type": "markdown", 122 | "metadata": { 123 | "pycharm": { 124 | "name": "#%% md\n" 125 | }, 126 | "slideshow": { 127 | "slide_type": "subslide" 128 | } 129 | }, 130 | "source": [ 131 | "It is common, to state 3-SAT problems in [DIMACS CNF format](https://people.sc.fsu.edu/~jburkardt/data/cnf/cnf.html):\n", 132 | "\n", 133 | "1. The file may begin with comment lines. \n", 134 | "* The \"problem\" line begins with \"p\", followed by the problem type \"cnf\", the number of variables and the number of clauses.\n", 135 | "* The remainder of the file contains lines defining the clauses.\n", 136 | "* A clause is defined by listing the index of each positive literal, and the negative index of each negative literal. \n", 137 | "\n" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": null, 143 | "metadata": { 144 | "pycharm": { 145 | "name": "#%%\n" 146 | } 147 | }, 148 | "outputs": [], 149 | "source": [ 150 | "# import Qiskit quantum libraries\n", 151 | "from qiskit.visualization import plot_histogram\n", 152 | "from qiskit.circuit.library import PhaseOracle\n", 153 | "from qiskit.primitives import StatevectorSampler\n", 154 | "from qiskit_algorithms import Grover, AmplificationProblem" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": 2, 160 | "metadata": { 161 | "pycharm": { 162 | "name": "#%%\n" 163 | }, 164 | "slideshow": { 165 | "slide_type": "fragment" 166 | } 167 | }, 168 | "outputs": [ 169 | { 170 | "name": "stdout", 171 | "output_type": "stream", 172 | "text": [ 173 | "c This is an example DIMACS 3-sat file with 3 satisfying solutions: 1 -2 3 0, -1 -2 -3 0, 1 2 -3 0\n", 174 | "p cnf 3 5\n", 175 | "-1 -2 -3 0\n", 176 | "1 -2 3 0\n", 177 | "1 2 -3 0\n", 178 | "1 -2 -3 0\n", 179 | "-1 2 3 0\n", 180 | "\n" 181 | ] 182 | } 183 | ], 184 | "source": [ 185 | "# import the problem in DIMACS CNF format\n", 186 | "import os\n", 187 | "import wget\n", 188 | "if not '3sat3-5.cnf' in os.listdir():\n", 189 | " wget.download('https://raw.githubusercontent.com/Qiskit/qiskit-tutorials/master/community/aqua/optimization/3sat3-5.cnf')\n", 190 | "with open('3sat3-5.cnf', 'r') as f:\n", 191 | " sat_cnf = f.read()\n", 192 | " oracle = PhaseOracle.from_dimacs_file(\"3sat3-5.cnf\")\n", 193 | "print(sat_cnf)" 194 | ] 195 | }, 196 | { 197 | "cell_type": "markdown", 198 | "metadata": { 199 | "pycharm": { 200 | "name": "#%% md\n" 201 | }, 202 | "slideshow": { 203 | "slide_type": "slide" 204 | } 205 | }, 206 | "source": [ 207 | "To apply a quantum algorithm from Qiskit Aqua to this problem, we simply need to import the Qiskit libraries and run the algorithm with the appropriate parameters." 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "execution_count": null, 213 | "metadata": { 214 | "slideshow": { 215 | "slide_type": "subslide" 216 | } 217 | }, 218 | "outputs": [], 219 | "source": "sampler = StatevectorSampler()\ngrover = Grover(sampler=sampler)\nproblem = AmplificationProblem(oracle)\nresult = grover.amplify(problem)\nplot_histogram(result.circuit_results)" 220 | }, 221 | { 222 | "cell_type": "markdown", 223 | "metadata": { 224 | "pycharm": { 225 | "name": "#%% md\n" 226 | }, 227 | "slideshow": { 228 | "slide_type": "fragment" 229 | } 230 | }, 231 | "source": [ 232 | "The result shows that the assignments $000, 101, 110$ for $x_1 x_2 x_3$ are potential solutions to the problem. \n", 233 | "Whether these are correct solutions to the problem can be verified efficiently, as 3-SAT is in NP.\n", 234 | "Note that the variables in the histogram are in reverse order: $x_3, x_2, x_1$ instead of $x_1, x_2, x_3$.\n" 235 | ] 236 | }, 237 | { 238 | "cell_type": "markdown", 239 | "metadata": { 240 | "pycharm": { 241 | "name": "#%% md\n" 242 | }, 243 | "slideshow": { 244 | "slide_type": "slide" 245 | } 246 | }, 247 | "source": [ 248 | "## Classical brute force algorithm\n", 249 | "\n", 250 | "The solutions to the problem can also be derived with a classical (non-quantum) algorithm by simply trying every possible combination of input values $x_1, x_2, x_3$of $f$.\n", 251 | "\n", 252 | "We find again, that the solutions for the given 3-SAT problem are the assignments $000, 101, 110$ for $x_1 x_2 x_3$. " 253 | ] 254 | }, 255 | { 256 | "cell_type": "code", 257 | "execution_count": 4, 258 | "metadata": { 259 | "pycharm": { 260 | "name": "#%%\n" 261 | }, 262 | "scrolled": false, 263 | "slideshow": { 264 | "slide_type": "subslide" 265 | } 266 | }, 267 | "outputs": [ 268 | { 269 | "data": { 270 | "text/html": [ 271 | "\n", 272 | "\n", 273 | "\n", 274 | "\n", 275 | "\n", 276 | "\n", 277 | "\n", 278 | "\n", 279 | "\n", 280 | "\n", 281 | "\n", 282 | "\n", 283 | "\n", 284 | "\n", 285 | "
$x_1$ $x_2$ $x_3$$f$
0 0 0True
0 0 1False
0 1 0False
0 1 1False
1 0 0False
1 0 1True
1 1 0True
1 1 1False
" 286 | ], 287 | "text/plain": [ 288 | "" 289 | ] 290 | }, 291 | "metadata": {}, 292 | "output_type": "display_data" 293 | } 294 | ], 295 | "source": [ 296 | "from IPython.display import HTML, display\n", 297 | "import tabulate\n", 298 | "\n", 299 | "nbr = 3 # number of Boolean variables in Boolean function\n", 300 | "\n", 301 | "table = []\n", 302 | "for i in range(2**nbr):\n", 303 | " x1, x2, x3 = [int(x) for x in '{0:03b}'.format(i)] # Boolean variables\n", 304 | " \n", 305 | " # define clauses \n", 306 | " c1 = [not x1, not x2, not x3] # -1 -2 -3\n", 307 | " c2 = [ x1, not x2, x3] # 1 -2 3\n", 308 | " c3 = [ x1, x2, not x3] # 1 2 -3\n", 309 | " c4 = [ x1, not x2, not x3] # 1 -2 -3\n", 310 | " c5 = [not x1, x2, x3] # -1 2 3\n", 311 | " \n", 312 | " f = all([any(c1), any(c2), any(c3), any(c4), any(c5)]) # Boolean function\n", 313 | " table.append([x1, x2, x3, f])\n", 314 | " \n", 315 | "display(HTML(tabulate.tabulate(table, tablefmt = 'html', \n", 316 | " headers = ['$x_1$', '$x_2$', '$x_3$', '$f$'])))" 317 | ] 318 | }, 319 | { 320 | "cell_type": "markdown", 321 | "metadata": { 322 | "pycharm": { 323 | "name": "#%% md\n" 324 | }, 325 | "slideshow": { 326 | "slide_type": "fragment" 327 | } 328 | }, 329 | "source": [ 330 | "Remark: this is obviously not the most efficient classical algorithm that exists. Heuristic SAT-algorithms are able to solve problem instances involving tens of thousands of variables and formulas consisting of millions of symbols, which is sufficient for many practical SAT problems." 331 | ] 332 | } 333 | ], 334 | "metadata": { 335 | "anaconda-cloud": {}, 336 | "celltoolbar": "Slideshow", 337 | "kernelspec": { 338 | "display_name": "Python 3 (ipykernel)", 339 | "language": "python", 340 | "name": "python3" 341 | }, 342 | "language_info": { 343 | "codemirror_mode": { 344 | "name": "ipython", 345 | "version": 3 346 | }, 347 | "file_extension": ".py", 348 | "mimetype": "text/x-python", 349 | "name": "python", 350 | "nbconvert_exporter": "python", 351 | "pygments_lexer": "ipython3", 352 | "version": "3.9.13" 353 | }, 354 | "latex_envs": { 355 | "bibliofile": "biblio.bib", 356 | "cite_by": "apalike", 357 | "current_citInitial": 1, 358 | "eqLabelWithNumbers": true, 359 | "eqNumInitial": 0 360 | }, 361 | "livereveal": { 362 | "autolaunch": true 363 | }, 364 | "nav_menu": {}, 365 | "toc": { 366 | "navigate_menu": true, 367 | "number_sections": true, 368 | "sideBar": true, 369 | "threshold": 6, 370 | "toc_cell": false, 371 | "toc_section_display": "block", 372 | "toc_window_display": false 373 | } 374 | }, 375 | "nbformat": 4, 376 | "nbformat_minor": 1 377 | } -------------------------------------------------------------------------------- /Mermin–Peres-Game.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Mermin Peres Magic Square Game" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 109, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister\n", 17 | "from qiskit_aer import AerSimulator\n", 18 | "import numpy as np" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 110, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "def check_result(results, col, row):\n", 28 | " print_tut = True\n", 29 | " for i in results: \n", 30 | " \n", 31 | " if print_tut:\n", 32 | " explain(i, col, row)\n", 33 | " print_tut = False\n", 34 | "\n", 35 | " print(\"Quantum circuit solution\")\n", 36 | " print(i+\"\\n\")\n", 37 | " i = np.array(list(i.split())).astype(int)[::-1]\n", 38 | " # bring array in correct i.e. from incoming y2,y1,x2,x1 -> x1,x2,y1,y2\n", 39 | " a = i[0:2]\n", 40 | " a_sum = np.sum(a)\n", 41 | " a3 = int(np.mod(a_sum+1,2))\n", 42 | " a = np.append(a,a3)\n", 43 | " winning_alice = np.mod(a_sum + a3, 2) == 1\n", 44 | " # compute x3 by x1+x2+1 mod 2 and save to array\n", 45 | "\n", 46 | " b = i[2:]\n", 47 | " b_sum = np.sum(b)\n", 48 | " b3 = int(np.mod(b_sum,2))\n", 49 | " b = np.append(b,b3)\n", 50 | " winning_bob = np.mod(b_sum + b3, 2) == 0 \n", 51 | " # compute y3 by y1+y2 mod 2 and save to array\n", 52 | "\n", 53 | " # compute overall winning condition -> alice at index of bob's row and vice versa\n", 54 | " print(\"Alice's selection. Column:\"+str(col)+\"\\n\"+str(a.reshape(3,1))+\"\\n\")\n", 55 | " print(\"Bob's circuit solution. Row:\"+str(row)+\"\\n\"+str(b)+\"\\n\")\n", 56 | " \n", 57 | " if(winning_bob & winning_alice & (a[row-1]==b[col-1])):\n", 58 | " print(\"Victory!\") \n", 59 | " print(\"The following magic square wins the game!\")\n", 60 | " k = np.empty((3,3,))\n", 61 | " k[:] = np.nan\n", 62 | " k[:,col-1] = a\n", 63 | " k[row-1,:] = b\n", 64 | " print(k)\n", 65 | " else:\n", 66 | " print(\"The Game is lost!\")\n", 67 | " print(\"------------------------------------------\") \n", 68 | " print(\"\\n\")\n" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": 123, 74 | "metadata": {}, 75 | "outputs": [], 76 | "source": [ 77 | "def explain(result, col, row):\n", 78 | " print(\"Resultant quantum circuit solution:\"+\"\\n\")\n", 79 | " print(result+\"\\n\")\n", 80 | " example = np.array(list(result.split())).astype(int)[::-1]\n", 81 | " print(\"Bring array in correct bit order i.e. from incoming y2,y1,x2,x1 -> x1,x2,y1,y2\"+\"\\n\")\n", 82 | " print(str(example)+\"\\n\")\n", 83 | " print(\"Compute missing x3 with x1+x2+1 mod 2 for Alice and save to array for further processing\")\n", 84 | " print(\"Compute missing y3 with y1+y2 mod 2 for Bob and save to array for further processing\"+\"\\n\")\n", 85 | " \n", 86 | " a = example[0:2]\n", 87 | " a_sum = np.sum(a)\n", 88 | " a3 = int(np.mod(a_sum+1,2))\n", 89 | " a = np.append(a,a3)\n", 90 | " winning_alice = np.mod(a_sum + a3, 2) == 1\n", 91 | "\n", 92 | " b = example[2:]\n", 93 | " b_sum = np.sum(b)\n", 94 | " b3 = int(np.mod(b_sum,2))\n", 95 | " b = np.append(b,b3)\n", 96 | " winning_bob = np.mod(b_sum + b3, 2) == 0 \n", 97 | "\n", 98 | " print(\"Alice's column solution\"+\"\\n\"+str(a.reshape(3,1))+\"\\n\" + \"Winning conditions fullfilled (Odd quantitude of 1)?: \"+str(winning_alice)+\"\\n\")\n", 99 | " print(\"Bob's row solution\"+\"\\n\"+str(b)+\"\\n\"+ \"Winning conditions fullfilled (Even quantitude of 1)?: \"+str(winning_bob)+\"\\n\")\n", 100 | " print(\"Create matrix to show results\"+\"\\n\")\n", 101 | " k = np.empty((3,3,))\n", 102 | " k[:] = np.nan\n", 103 | " k[:,col-1] = a\n", 104 | " k[row-1,:] = b\n", 105 | " print(k)\n", 106 | " print(\"------------------------------------------\") \n", 107 | " print(\"------------------------------------------\") " 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": 124, 113 | "metadata": {}, 114 | "outputs": [], 115 | "source": [ 116 | "\n", 117 | "def get_state_accuracy(counts): # funciton to calculate state accuracy\n", 118 | " expected_counts = 0\n", 119 | " for state in counts.keys():\n", 120 | " if state in expected_states:\n", 121 | " expected_counts = expected_counts + counts[state]\n", 122 | " state_accuracy = expected_counts / shots\n", 123 | " return str(state_accuracy*100)+\"%\"" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": 125, 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": "#The following function sets up the initial entanglement between Alice and Bob\ndef share_bell_state(qc,a,b,c,d): \n qc.h(a)\n qc.h(b)\n qc.cx(a,c)\n qc.cx(b,d)\n \n# The following functions represent the U(gamma) and V(gamma) controlled Cliffords.\ndef U(qc,gamma,a,b):\n if gamma==1:\n qc.h(a)\n qc.id(b)\n elif gamma==2:\n qc.swap(a,b)\n qc.h(a)\n qc.id(b)\n\n elif gamma==3:\n qc.cx(a,b)\n qc.h(a)\n qc.id(b)\n\ndef V(qc,gamma,a,b):\n if gamma==1:\n qc.h(a)\n qc.h(b)\n if gamma==2:\n qc.swap(a,b)\n elif gamma==3:\n qc.z(a)\n qc.z(b)\n qc.cz(a,b)\n qc.h(a)\n qc.h(b)" 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": 127, 136 | "metadata": {}, 137 | "outputs": [ 138 | { 139 | "name": "stdout", 140 | "output_type": "stream", 141 | "text": [ 142 | "Alice and Bob, select a column or row value of 1, 2, or 3\n", 143 | "Alice choice (column):\n", 144 | "Bob choice (row):\n", 145 | " \u250c\u2500\u2500\u2500\u2510 \u2591 \u250c\u2500\u2500\u2500\u2510 \u2591 \u250c\u2500\u2510 \n", 146 | "q534_0: \u2524 H \u251c\u2500\u2500\u25a0\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2591\u2500\u2524 H \u251c\u2500\u2591\u2500\u2524M\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", 147 | " \u251c\u2500\u2500\u2500\u2524 \u2502 \u2591 \u251c\u2500\u2500\u2500\u2524 \u2591 \u2514\u2565\u2518\u250c\u2500\u2510 \n", 148 | "q534_1: \u2524 H \u251c\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u25a0\u2500\u2500\u2500\u2591\u2500\u2524 I \u251c\u2500\u2591\u2500\u2500\u256b\u2500\u2524M\u251c\u2500\u2500\u2500\u2500\u2500\u2500\n", 149 | " \u2514\u2500\u2500\u2500\u2518\u250c\u2500\u2534\u2500\u2510 \u2502 \u2591 \u251c\u2500\u2500\u2500\u2524 \u2591 \u2551 \u2514\u2565\u2518\u250c\u2500\u2510 \n", 150 | "q535_0: \u2500\u2500\u2500\u2500\u2500\u2524 X \u251c\u2500\u2500\u253c\u2500\u2500\u2500\u2591\u2500\u2524 H \u251c\u2500\u2591\u2500\u2500\u256b\u2500\u2500\u256b\u2500\u2524M\u251c\u2500\u2500\u2500\n", 151 | " \u2514\u2500\u2500\u2500\u2518\u250c\u2500\u2534\u2500\u2510 \u2591 \u251c\u2500\u2500\u2500\u2524 \u2591 \u2551 \u2551 \u2514\u2565\u2518\u250c\u2500\u2510\n", 152 | "q535_1: \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 X \u251c\u2500\u2591\u2500\u2524 H \u251c\u2500\u2591\u2500\u2500\u256b\u2500\u2500\u256b\u2500\u2500\u256b\u2500\u2524M\u251c\n", 153 | " \u2514\u2500\u2500\u2500\u2518 \u2591 \u2514\u2500\u2500\u2500\u2518 \u2591 \u2551 \u2551 \u2551 \u2514\u2565\u2518\n", 154 | "c160: 1/\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u256c\u2550\u2550\u256c\u2550\u2550\u256c\u2550\n", 155 | " 0 \u2551 \u2551 \u2551 \n", 156 | " \u2551 \u2551 \u2551 \n", 157 | "c161: 1/\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u256c\u2550\u2550\u256c\u2550\n", 158 | " 0 \u2551 \u2551 \n", 159 | " \u2551 \u2551 \n", 160 | "c162: 1/\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u256c\u2550\n", 161 | " 0 \u2551 \n", 162 | " \u2551 \n", 163 | "c163: 1/\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\n", 164 | " 0 \n", 165 | "Resultant quantum circuit solution:\n", 166 | "\n", 167 | "0 0 1 0\n", 168 | "\n", 169 | "Bring array in correct bit order i.e. from incoming y2,y1,x2,x1 -> x1,x2,y1,y2\n", 170 | "\n", 171 | "[0 1 0 0]\n", 172 | "\n", 173 | "Compute missing x3 with x1+x2+1 mod 2 and save to array for further processing\n", 174 | "Compute missing y3 with y1+y2 mod 2 and save to array for further processing\n", 175 | "\n", 176 | "Alice's column solution\n", 177 | "[[0]\n", 178 | " [1]\n", 179 | " [0]]\n", 180 | "Winning conditions fullfilled (Odd quantitude of 1)? :True\n", 181 | "\n", 182 | "Bob's row solution\n", 183 | "[0 0 0]\n", 184 | "Winning conditions fullfilled (Even quantitude of 1)? :True\n", 185 | "\n", 186 | "Create matrix to show results\n", 187 | "\n", 188 | "[[ 0. 0. 0.]\n", 189 | " [ 1. nan nan]\n", 190 | " [ 0. nan nan]]\n", 191 | "------------------------------------------\n", 192 | "------------------------------------------\n", 193 | "Quantum circuit solution\n", 194 | "0 0 1 0\n", 195 | "\n", 196 | "Alice's selection. Column:1\n", 197 | "[[0]\n", 198 | " [1]\n", 199 | " [0]]\n", 200 | "\n", 201 | "Bob's circuit solution. Row:1\n", 202 | "[0 0 0]\n", 203 | "\n", 204 | "Victory!\n", 205 | "The following magic square wins the game!\n", 206 | "[[ 0. 0. 0.]\n", 207 | " [ 1. nan nan]\n", 208 | " [ 0. nan nan]]\n", 209 | "------------------------------------------\n", 210 | "\n", 211 | "\n", 212 | "Quantum circuit solution\n", 213 | "0 1 1 1\n", 214 | "\n", 215 | "Alice's selection. Column:1\n", 216 | "[[1]\n", 217 | " [1]\n", 218 | " [1]]\n", 219 | "\n", 220 | "Bob's circuit solution. Row:1\n", 221 | "[1 0 1]\n", 222 | "\n", 223 | "Victory!\n", 224 | "The following magic square wins the game!\n", 225 | "[[ 1. 0. 1.]\n", 226 | " [ 1. nan nan]\n", 227 | " [ 1. nan nan]]\n", 228 | "------------------------------------------\n", 229 | "\n", 230 | "\n", 231 | "Quantum circuit solution\n", 232 | "0 1 0 1\n", 233 | "\n", 234 | "Alice's selection. Column:1\n", 235 | "[[1]\n", 236 | " [0]\n", 237 | " [0]]\n", 238 | "\n", 239 | "Bob's circuit solution. Row:1\n", 240 | "[1 0 1]\n", 241 | "\n", 242 | "Victory!\n", 243 | "The following magic square wins the game!\n", 244 | "[[ 1. 0. 1.]\n", 245 | " [ 0. nan nan]\n", 246 | " [ 0. nan nan]]\n", 247 | "------------------------------------------\n", 248 | "\n", 249 | "\n", 250 | "Quantum circuit solution\n", 251 | "1 0 1 0\n", 252 | "\n", 253 | "Alice's selection. Column:1\n", 254 | "[[0]\n", 255 | " [1]\n", 256 | " [0]]\n", 257 | "\n", 258 | "Bob's circuit solution. Row:1\n", 259 | "[0 1 1]\n", 260 | "\n", 261 | "Victory!\n", 262 | "The following magic square wins the game!\n", 263 | "[[ 0. 1. 1.]\n", 264 | " [ 1. nan nan]\n", 265 | " [ 0. nan nan]]\n", 266 | "------------------------------------------\n", 267 | "\n", 268 | "\n", 269 | "Quantum circuit solution\n", 270 | "1 1 0 1\n", 271 | "\n", 272 | "Alice's selection. Column:1\n", 273 | "[[1]\n", 274 | " [0]\n", 275 | " [0]]\n", 276 | "\n", 277 | "Bob's circuit solution. Row:1\n", 278 | "[1 1 0]\n", 279 | "\n", 280 | "Victory!\n", 281 | "The following magic square wins the game!\n", 282 | "[[ 1. 1. 0.]\n", 283 | " [ 0. nan nan]\n", 284 | " [ 0. nan nan]]\n", 285 | "------------------------------------------\n", 286 | "\n", 287 | "\n", 288 | "Quantum circuit solution\n", 289 | "0 0 0 0\n", 290 | "\n", 291 | "Alice's selection. Column:1\n", 292 | "[[0]\n", 293 | " [0]\n", 294 | " [1]]\n", 295 | "\n", 296 | "Bob's circuit solution. Row:1\n", 297 | "[0 0 0]\n", 298 | "\n", 299 | "Victory!\n", 300 | "The following magic square wins the game!\n", 301 | "[[ 0. 0. 0.]\n", 302 | " [ 0. nan nan]\n", 303 | " [ 1. nan nan]]\n", 304 | "------------------------------------------\n", 305 | "\n", 306 | "\n", 307 | "Quantum circuit solution\n", 308 | "1 1 1 1\n", 309 | "\n", 310 | "Alice's selection. Column:1\n", 311 | "[[1]\n", 312 | " [1]\n", 313 | " [1]]\n", 314 | "\n", 315 | "Bob's circuit solution. Row:1\n", 316 | "[1 1 0]\n", 317 | "\n", 318 | "Victory!\n", 319 | "The following magic square wins the game!\n", 320 | "[[ 1. 1. 0.]\n", 321 | " [ 1. nan nan]\n", 322 | " [ 1. nan nan]]\n", 323 | "------------------------------------------\n", 324 | "\n", 325 | "\n", 326 | "Quantum circuit solution\n", 327 | "1 0 0 0\n", 328 | "\n", 329 | "Alice's selection. Column:1\n", 330 | "[[0]\n", 331 | " [0]\n", 332 | " [1]]\n", 333 | "\n", 334 | "Bob's circuit solution. Row:1\n", 335 | "[0 1 1]\n", 336 | "\n", 337 | "Victory!\n", 338 | "The following magic square wins the game!\n", 339 | "[[ 0. 1. 1.]\n", 340 | " [ 0. nan nan]\n", 341 | " [ 1. nan nan]]\n", 342 | "------------------------------------------\n", 343 | "\n", 344 | "\n", 345 | "{'0 0 1 0': 1041, '0 1 1 1': 1007, '0 1 0 1': 1033, '1 0 1 0': 1014, '1 1 0 1': 1039, '0 0 0 0': 1036, '1 1 1 1': 1028, '1 0 0 0': 994}\n" 346 | ] 347 | } 348 | ], 349 | "source": [ 350 | "#We will ask the user to select the row and column on Alice and Bob's behalf.\n", 351 | "print(\"Alice and Bob, select a column or row value of 1, 2, or 3\") \n", 352 | "print(\"Alice choice (column):\")\n", 353 | "alpha = int(input())\n", 354 | "print(\"Bob choice (row):\")\n", 355 | "beta = int(input())\n", 356 | "\n", 357 | "#Create the quantum register and the classical register to store our final bit values\n", 358 | "aliceQR = QuantumRegister(2)\n", 359 | "x1CR = ClassicalRegister(1)\n", 360 | "x2CR = ClassicalRegister(1)\n", 361 | "bobQR = QuantumRegister(2)\n", 362 | "y1CR = ClassicalRegister(1)\n", 363 | "y2CR = ClassicalRegister(1)\n", 364 | "\n", 365 | "#Create the circuit\n", 366 | "magicsquare_circuit = QuantumCircuit(aliceQR,bobQR,x1CR,x2CR,y1CR,y2CR)\n", 367 | "\n", 368 | "#Generate the Bell state on the circuit\n", 369 | "share_bell_state(magicsquare_circuit,0,1,2,3)\n", 370 | "\n", 371 | "magicsquare_circuit.barrier()\n", 372 | "\n", 373 | "#Draw the rest of the circuit based on Alice and Bob's selection \n", 374 | "if 4>alpha>0 and 4>beta>0:\n", 375 | " U(magicsquare_circuit,alpha,0,1) \n", 376 | " V(magicsquare_circuit,beta,2,3)\n", 377 | " magicsquare_circuit.barrier()\n", 378 | " magicsquare_circuit.measure(0,0)\n", 379 | " magicsquare_circuit.measure(1,1)\n", 380 | " magicsquare_circuit.measure(2,2)\n", 381 | " magicsquare_circuit.measure(3,3)\n", 382 | " \n", 383 | " magicsquare_circuit.draw(output='mpl')\n", 384 | " print(magicsquare_circuit)\n", 385 | "\n", 386 | " backend = AerSimulator() # define the backend\n", 387 | " job = backend.run(magicsquare_circuit, shots=8192) # run the job simulation\n", 388 | " results = job.result().get_counts()\n", 389 | " check_result(results, alpha, beta)\n", 390 | " print(results)\n", 391 | "\n", 392 | "else:\n", 393 | " print(\"please enter values of 1, 2, or 3\")" 394 | ] 395 | }, 396 | { 397 | "cell_type": "code", 398 | "execution_count": null, 399 | "metadata": {}, 400 | "outputs": [], 401 | "source": [] 402 | } 403 | ], 404 | "metadata": { 405 | "kernelspec": { 406 | "display_name": "Python 3 (ipykernel)", 407 | "language": "python", 408 | "name": "python3" 409 | }, 410 | "language_info": { 411 | "codemirror_mode": { 412 | "name": "ipython", 413 | "version": 3 414 | }, 415 | "file_extension": ".py", 416 | "mimetype": "text/x-python", 417 | "name": "python", 418 | "nbconvert_exporter": "python", 419 | "pygments_lexer": "ipython3", 420 | "version": "3.9.13" 421 | } 422 | }, 423 | "nbformat": 4, 424 | "nbformat_minor": 2 425 | } -------------------------------------------------------------------------------- /Quantum-Coin-Game.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "pycharm": { 7 | "name": "#%% md\n" 8 | }, 9 | "slideshow": { 10 | "slide_type": "slide" 11 | } 12 | }, 13 | "source": [ 14 | "# How Quantum Power Helps to Win a Coin Game\n", 15 | "\n", 16 | "A quantum coin game to illustrate the power of quantum superposition and interference - implemented by [Jan-R. Lahmann](https://twitter.com/JanLahmann) using [Qiskit](http://qiskit.org), [binder](https://mybinder.org) and [RISE](https://rise.readthedocs.io/).\n", 17 | "\n", 18 | "Inspired by the TED talk of Shohini Ghose \n", 19 | "[\"Quantum computing explained in 10 minutes\"](https://www.ted.com/talks/shohini_ghose_quantum_computing_explained_in_10_minutes) \n", 20 | "\n", 21 | "(hit space or right arrow to move to next slide)" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": { 27 | "pycharm": { 28 | "name": "#%% md\n" 29 | }, 30 | "slideshow": { 31 | "slide_type": "skip" 32 | } 33 | }, 34 | "source": [ 35 | "https://www.ted.com/talks/shohini_ghose_quantum_computing_explained_in_10_minutes" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": { 41 | "pycharm": { 42 | "name": "#%% md\n" 43 | }, 44 | "slideshow": { 45 | "slide_type": "slide" 46 | } 47 | }, 48 | "source": [ 49 | "## Usage instructions for the user interface\n", 50 | "\n", 51 | "1. \"Ctrl -\" and \"Ctrl +\" (or \"command -\", \"command +\") adjust the zoom level to fit the text to the browser window\n", 52 | "* Use \"space\" and \"shift space\" to navigate through the slides \n", 53 | "* \"Shift Enter\" executes the interactive cells (might need to click the cell, first)\n", 54 | "* Execute the interactive cells on each slide (\"In [1]:\", etc)\n", 55 | "* In case a cell is not formatted correctly, try to double-click and then \"Shift Enter\" to re-execute\n", 56 | "* Interactive cells can be modified, if needed\n", 57 | "* \"X\" at the top left exits the slideshow and enters the jupyter notebook interface" 58 | ] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "metadata": { 63 | "pycharm": { 64 | "name": "#%% md\n" 65 | }, 66 | "slideshow": { 67 | "slide_type": "slide" 68 | } 69 | }, 70 | "source": [ 71 | "## The Coin Game\n", 72 | "\n", 73 | "Two players, A(lice) and B(ob), play a coin game. \n", 74 | "\n", 75 | "1. The game starts with the coin showing Heads.\n", 76 | "* Player A starts and may either turn the coin or leave it as is. \n", 77 | "* The moves are hidden, i.e. not revealed to the other player.\n", 78 | "* B may now also turn the coin or leave it as is.\n", 79 | "* A then has the third and final move.\n", 80 | "* Now the coin gets revealed. \n", 81 | "* If it shows Heads, A wins; if it shows Tails, B wins." 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": { 87 | "pycharm": { 88 | "name": "#%% md\n" 89 | }, 90 | "slideshow": { 91 | "slide_type": "slide" 92 | } 93 | }, 94 | "source": [ 95 | "This notebook implements the coin game using Qiskit, an SDK by IBM to program quantum computers. \n", 96 | "\n", 97 | "On three separate slides, the players can make their choice to either turn the coin (apply an \"X-Gate\") or leave it as is (apply an \"id-Gate\" / identity-Gate). \n", 98 | "Heads is encoded by \"0\", Tails encoded by \"1\". \n", 99 | "At the end, the quantum program evaluates the moves and declares the winner. \n", 100 | "\n", 101 | "In a second phase, one player will be allowed to use an additional gate (\"coin move\"), which is only available in the Quantum world: the \"Hadamard Gate\". \n", 102 | "Will this change the game?" 103 | ] 104 | }, 105 | { 106 | "cell_type": "markdown", 107 | "metadata": { 108 | "pycharm": { 109 | "name": "#%% md\n" 110 | }, 111 | "slideshow": { 112 | "slide_type": "skip" 113 | } 114 | }, 115 | "source": [ 116 | "The game can also be played using the IBM \"Quantum Composer\" graphical UI https://quantumexperience.ng.bluemix.net/qx/editor" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "metadata": { 123 | "pycharm": { 124 | "name": "#%%\n" 125 | }, 126 | "scrolled": true, 127 | "slideshow": { 128 | "slide_type": "slide" 129 | } 130 | }, 131 | "outputs": [], 132 | "source": "# hit \"shift + Enter\" to execute this cell\n# loading some basic functions needed for the game\nfrom qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit\nfrom qiskit.visualization import plot_histogram, circuit_drawer\nfrom qiskit_aer import AerSimulator\nfrom ipywidgets import interact" 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": 2, 137 | "metadata": { 138 | "pycharm": { 139 | "name": "#%%\n" 140 | }, 141 | "slideshow": { 142 | "slide_type": "slide" 143 | } 144 | }, 145 | "outputs": [], 146 | "source": [ 147 | "# hit \"shift + Enter\" to execute this cell\n", 148 | "# auxillary functions for the coin moves\n", 149 | "\n", 150 | "def MoveA1(move_A1): global moveA1; moveA1=move_A1;\n", 151 | "def MoveB1(move_B1): global moveB1; moveB1=move_B1;\n", 152 | "def MoveA2(move_A2): global moveA2; moveA2=move_A2;" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": 3, 158 | "metadata": { 159 | "pycharm": { 160 | "name": "#%%\n" 161 | }, 162 | "slideshow": { 163 | "slide_type": "slide" 164 | } 165 | }, 166 | "outputs": [], 167 | "source": [ 168 | "# hit \"shift + Enter\" to execute this cell\n", 169 | "# auxillary function to identify the winner\n", 170 | "\n", 171 | "def who_wins(counts):\n", 172 | " if len(counts)==1 :\n", 173 | " print('The winner is', 'A' if (\"0\" in counts) else 'B')\n", 174 | " else:\n", 175 | " count0=counts[\"0\"]\n", 176 | " count1=counts[\"1\"]\n", 177 | " print('The coin is in superposition of |0\u27e9 and |1\u27e9')\n", 178 | " print('A wins with probability', \"%.1f%%\" % (100.*count0/(count0+count1)))\n", 179 | " print('B wins with probability', \"%.1f%%\" % (100.*count1/(count0+count1)))\n", 180 | " return()" 181 | ] 182 | }, 183 | { 184 | "cell_type": "markdown", 185 | "metadata": { 186 | "pycharm": { 187 | "name": "#%% md\n" 188 | }, 189 | "slideshow": { 190 | "slide_type": "slide" 191 | } 192 | }, 193 | "source": [ 194 | "## Ready to play?" 195 | ] 196 | }, 197 | { 198 | "cell_type": "markdown", 199 | "metadata": { 200 | "pycharm": { 201 | "name": "#%% md\n" 202 | }, 203 | "slideshow": { 204 | "slide_type": "fragment" 205 | } 206 | }, 207 | "source": [ 208 | "## Player A makes the first move and applies an X-Gate (turn the coin) or id-Gate (leave it is)" 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": null, 214 | "metadata": { 215 | "pycharm": { 216 | "name": "#%%\n" 217 | }, 218 | "slideshow": { 219 | "slide_type": "fragment" 220 | } 221 | }, 222 | "outputs": [], 223 | "source": [ 224 | "# hit \"shift + Enter\" to execute this cell\n", 225 | "# then make your choice using the drop-down widget\n", 226 | "interact(MoveA1, move_A1={'id Gate':0,'X Gate':1});" 227 | ] 228 | }, 229 | { 230 | "cell_type": "markdown", 231 | "metadata": { 232 | "pycharm": { 233 | "name": "#%% md\n" 234 | }, 235 | "slideshow": { 236 | "slide_type": "slide" 237 | } 238 | }, 239 | "source": [ 240 | "## Player B makes the his move and applies an X-Gate (turn the coin) or id-Gate (leave it is)" 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": null, 246 | "metadata": { 247 | "pycharm": { 248 | "name": "#%%\n" 249 | }, 250 | "slideshow": { 251 | "slide_type": "fragment" 252 | } 253 | }, 254 | "outputs": [], 255 | "source": [ 256 | "# hit \"shift + Enter\" to execute this cell \n", 257 | "# you might need to click the cell, first, to activate it\n", 258 | "# then make your choice using the drop-down widget\n", 259 | "interact(MoveB1, move_B1={'id Gate':0,'X Gate':1});" 260 | ] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "metadata": { 265 | "pycharm": { 266 | "name": "#%% md\n" 267 | }, 268 | "slideshow": { 269 | "slide_type": "slide" 270 | } 271 | }, 272 | "source": [ 273 | "## Player A makes his second move and applies an X-Gate (turn the coin) or id-Gate (leave it is)" 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": null, 279 | "metadata": { 280 | "pycharm": { 281 | "is_executing": true, 282 | "name": "#%%\n" 283 | }, 284 | "slideshow": { 285 | "slide_type": "fragment" 286 | } 287 | }, 288 | "outputs": [], 289 | "source": [ 290 | "# hit \"shift + Enter\" to execute this cell\n", 291 | "# you might need to click the cell, first, to activate it\n", 292 | "# then make your choice using the drop-down widget\n", 293 | "interact(MoveA2, move_A2={'id Gate':0,'X Gate':1});" 294 | ] 295 | }, 296 | { 297 | "cell_type": "code", 298 | "execution_count": 7, 299 | "metadata": { 300 | "pycharm": { 301 | "name": "#%%\n" 302 | }, 303 | "slideshow": { 304 | "slide_type": "slide" 305 | } 306 | }, 307 | "outputs": [ 308 | { 309 | "name": "stdout", 310 | "output_type": "stream", 311 | "text": [ 312 | "0 0 0\n" 313 | ] 314 | } 315 | ], 316 | "source": [ 317 | "# optional: print the three moves. \n", 318 | "# 0: id Gate (leave the coin unchanged), 1: X Gate (turn the coin)\n", 319 | "print(moveA1, moveB1, moveA2)" 320 | ] 321 | }, 322 | { 323 | "cell_type": "code", 324 | "execution_count": null, 325 | "metadata": { 326 | "pycharm": { 327 | "name": "#%%\n" 328 | }, 329 | "slideshow": { 330 | "slide_type": "slide" 331 | } 332 | }, 333 | "outputs": [], 334 | "source": "# create the quantum circuit with the chosen coin moves\nq = QuantumRegister(1) # create a quantum register with one qubit\n# create a classical register that will hold the results of the measurement\nc = ClassicalRegister(1) \nqc = QuantumCircuit(q, c) # creates the quantum circuit\nbackend = AerSimulator() # define the backend\n\n# 1. move of A\nqc.id(q[0]) if (moveA1 == 0) else qc.x(q[0]) \n \n# 1. move of B \nqc.id(q[0]) if (moveB1 == 0) else qc.x(q[0]) \n\n# 2. move of A\nqc.id(q[0]) if (moveA2 == 0) else qc.x(q[0]) \n \nqc.measure(q, c) # Measure the qubits\nqc.draw(output='mpl') # plot the circuit" 335 | }, 336 | { 337 | "cell_type": "code", 338 | "execution_count": null, 339 | "metadata": { 340 | "pycharm": { 341 | "name": "#%%\n" 342 | }, 343 | "scrolled": false, 344 | "slideshow": { 345 | "slide_type": "slide" 346 | } 347 | }, 348 | "outputs": [], 349 | "source": "# execute the quantum circiut (coin moves) and identify the winner\n\njob = backend.run(qc, shots=200) # run the job simulation\n\nresult = job.result() # grab the result\n\ncounts = result.get_counts(qc) # results for the number of runs\n\nprint(counts); # print the results of the runs\nwho_wins(counts); # celebrate the winner" 350 | }, 351 | { 352 | "cell_type": "markdown", 353 | "metadata": { 354 | "pycharm": { 355 | "name": "#%% md\n" 356 | }, 357 | "slideshow": { 358 | "slide_type": "slide" 359 | } 360 | }, 361 | "source": [ 362 | "## Is there a safe strategy for A to always win the game? (or for B)\n" 363 | ] 364 | }, 365 | { 366 | "cell_type": "markdown", 367 | "metadata": { 368 | "pycharm": { 369 | "name": "#%% md\n" 370 | }, 371 | "slideshow": { 372 | "slide_type": "fragment" 373 | } 374 | }, 375 | "source": [ 376 | "It is easy to see that A and B will win with the same probability. \n", 377 | "There is no strategy for A (or B) to increase this probability - and certainly no strategy to always win." 378 | ] 379 | }, 380 | { 381 | "cell_type": "markdown", 382 | "metadata": { 383 | "pycharm": { 384 | "name": "#%% md\n" 385 | }, 386 | "slideshow": { 387 | "slide_type": "fragment" 388 | } 389 | }, 390 | "source": [ 391 | "Does this change if A has \"Quantum Power\" and may use \"H-Gates\" in addition to id and X?\n", 392 | "(B may still only use id- and X-Gates)\n", 393 | "\n", 394 | "Is there a safe strategy for A (or B) to always win using the new power? \n", 395 | "How do you interpret this? Is it a \"sleight of hand\" or is there more behind it?" 396 | ] 397 | }, 398 | { 399 | "cell_type": "markdown", 400 | "metadata": { 401 | "pycharm": { 402 | "name": "#%% md\n" 403 | }, 404 | "slideshow": { 405 | "slide_type": "slide" 406 | } 407 | }, 408 | "source": [ 409 | "## Ready to play with Quantum Power?\n", 410 | "A may now use id-, X- and H-gates. \n", 411 | "B may use only id- and X-gates." 412 | ] 413 | }, 414 | { 415 | "cell_type": "markdown", 416 | "metadata": { 417 | "pycharm": { 418 | "name": "#%% md\n" 419 | }, 420 | "slideshow": { 421 | "slide_type": "fragment" 422 | } 423 | }, 424 | "source": [ 425 | "## Player A makes the first move and applies an id-, X- or H-Gate" 426 | ] 427 | }, 428 | { 429 | "cell_type": "code", 430 | "execution_count": null, 431 | "metadata": { 432 | "pycharm": { 433 | "name": "#%%\n" 434 | }, 435 | "slideshow": { 436 | "slide_type": "fragment" 437 | } 438 | }, 439 | "outputs": [], 440 | "source": [ 441 | "interact(MoveA1, move_A1={'id Gate':0,'X Gate':1, 'H Gate':2});" 442 | ] 443 | }, 444 | { 445 | "cell_type": "markdown", 446 | "metadata": { 447 | "pycharm": { 448 | "name": "#%% md\n" 449 | }, 450 | "slideshow": { 451 | "slide_type": "slide" 452 | } 453 | }, 454 | "source": [ 455 | "## Player B makes his first move and applies an id- or X-Gate" 456 | ] 457 | }, 458 | { 459 | "cell_type": "code", 460 | "execution_count": null, 461 | "metadata": { 462 | "pycharm": { 463 | "is_executing": true, 464 | "name": "#%%\n" 465 | }, 466 | "slideshow": { 467 | "slide_type": "fragment" 468 | } 469 | }, 470 | "outputs": [], 471 | "source": [ 472 | "interact(MoveB1, move_B1={'id Gate':0,'X Gate':1});" 473 | ] 474 | }, 475 | { 476 | "cell_type": "markdown", 477 | "metadata": { 478 | "pycharm": { 479 | "name": "#%% md\n" 480 | }, 481 | "slideshow": { 482 | "slide_type": "slide" 483 | } 484 | }, 485 | "source": [ 486 | "## Player A makes her second move and applies an id-, X- or H-Gate" 487 | ] 488 | }, 489 | { 490 | "cell_type": "code", 491 | "execution_count": null, 492 | "metadata": { 493 | "pycharm": { 494 | "name": "#%%\n" 495 | }, 496 | "slideshow": { 497 | "slide_type": "fragment" 498 | } 499 | }, 500 | "outputs": [], 501 | "source": [ 502 | "interact(MoveA2, move_A2={'id Gate':0,'X Gate':1, 'H Gate':2});" 503 | ] 504 | }, 505 | { 506 | "cell_type": "code", 507 | "execution_count": 13, 508 | "metadata": { 509 | "pycharm": { 510 | "name": "#%%\n" 511 | }, 512 | "scrolled": false, 513 | "slideshow": { 514 | "slide_type": "slide" 515 | } 516 | }, 517 | "outputs": [ 518 | { 519 | "name": "stdout", 520 | "output_type": "stream", 521 | "text": [ 522 | "0 0 0\n" 523 | ] 524 | } 525 | ], 526 | "source": [ 527 | "# optional: print the three moves. \n", 528 | "# 0: id Gate (leave the coin unchanged), 1: X Gate (turn the coin), 2: H Gate\n", 529 | "print(moveA1, moveB1, moveA2)" 530 | ] 531 | }, 532 | { 533 | "cell_type": "code", 534 | "execution_count": null, 535 | "metadata": { 536 | "pycharm": { 537 | "name": "#%%\n" 538 | }, 539 | "scrolled": true, 540 | "slideshow": { 541 | "slide_type": "slide" 542 | } 543 | }, 544 | "outputs": [], 545 | "source": "# create the quantum circuit with the chosen coin moves\nq = QuantumRegister(1) # create a quantum register with one qubit\n# create a classical register that will hold the results of the measurement\nc = ClassicalRegister(1) \nqc = QuantumCircuit(q, c) # creates the quantum circuit\nbackend = AerSimulator() # define the backend\n\n# 1. move of A\nif moveA1 == 0 : qc.id(q[0])\nelif moveA1 == 1 : qc.x(q[0]) \nelif moveA1 == 2 : qc.h(q[0]) \n \n# 1. move of B \nif moveB1 == 0 : qc.id(q[0])\nelif moveB1 == 1 : qc.x(q[0]) \n\n# 2. move of A\nif moveA2 == 0 : qc.id(q[0])\nelif moveA2 == 1 : qc.x(q[0]) \nelif moveA2 == 2 : qc.h(q[0]) \n\nqc.measure(q, c) # Measure the qubits\nqc.draw(output='mpl') # plot the circuit" 546 | }, 547 | { 548 | "cell_type": "code", 549 | "execution_count": null, 550 | "metadata": { 551 | "pycharm": { 552 | "name": "#%%\n" 553 | }, 554 | "slideshow": { 555 | "slide_type": "slide" 556 | } 557 | }, 558 | "outputs": [], 559 | "source": "# execute the quantum circiut (coin moves) and identify the winner\n\njob = backend.run(qc, shots=200) # run the job simulation\n\nresult = job.result() # grab the result\n\ncounts = result.get_counts(qc) # results for the number of runs\n\nprint(counts); # print the results of the runs\nwho_wins(counts); # celebrate the winner\n\nplot_histogram(counts) # Visualise the results" 560 | }, 561 | { 562 | "cell_type": "markdown", 563 | "metadata": { 564 | "pycharm": { 565 | "name": "#%% md\n" 566 | }, 567 | "slideshow": { 568 | "slide_type": "slide" 569 | } 570 | }, 571 | "source": [ 572 | "## Want to make some own experiments to understand the state of the qubit (\"quantum coin\")?" 573 | ] 574 | }, 575 | { 576 | "cell_type": "code", 577 | "execution_count": null, 578 | "metadata": { 579 | "pycharm": { 580 | "name": "#%%\n" 581 | }, 582 | "slideshow": { 583 | "slide_type": "slide" 584 | } 585 | }, 586 | "outputs": [], 587 | "source": "# create the quantum circuit with the chosen coin moves\nq = QuantumRegister(1) # create a quantum register with one qubit\n# create a classical register that will hold the results of the measurement\nc = ClassicalRegister(1) \nqc = QuantumCircuit(q, c) # creates the quantum circuit\nbackend = AerSimulator() # define the backend\n\n\n# define the quantum gates (=coin moves) as you like\n# you could also try change the number of moves to just 1 or 2\n#qc.id(q[0])\n#qc.x(q[0]) \n#qc.h(q[0]) \n\nqc.id(q[0])\nqc.x(q[0]) \nqc.h(q[0])\n\nqc.measure(q, c) # Measure the qubits\nqc.draw(output='mpl') # plot the circuit" 588 | }, 589 | { 590 | "cell_type": "code", 591 | "execution_count": null, 592 | "metadata": { 593 | "pycharm": { 594 | "name": "#%%\n" 595 | }, 596 | "slideshow": { 597 | "slide_type": "slide" 598 | } 599 | }, 600 | "outputs": [], 601 | "source": "# execute the quantum circiut (coin moves) and identify the winner\n\njob = backend.run(qc, shots=200) # run the job simulation\n\nresult = job.result() # grab the result\n\ncounts = result.get_counts(qc) # results for the number of runs\n\nprint(counts); # print the results of the runs\n\nplot_histogram(counts) # Visualise the results" 602 | }, 603 | { 604 | "cell_type": "markdown", 605 | "metadata": { 606 | "pycharm": { 607 | "name": "#%% md\n" 608 | }, 609 | "slideshow": { 610 | "slide_type": "slide" 611 | } 612 | }, 613 | "source": [ 614 | "## Some Quantum Theory related to this Coin Game\n", 615 | "\n", 616 | "(hint: In case a cell is not formatted correctly, try to double-click and then \"Shift Enter\" to re-execute) \n", 617 | "A (general) quantum state can be written as $\\;\\; \\alpha |0\\rangle + \\beta |1\\rangle $. \n", 618 | "It is called a superposition of $\\;|0\\rangle\\;$ and $\\;|1\\rangle\\;$. \n", 619 | "$ \\alpha , \\beta $ are generalized probabilities (for measuring 0 or 1) with $\\; \\alpha , \\beta \\in \\mathbb{C},\\; |\\alpha|^2 + |\\beta|^2 = 1$." 620 | ] 621 | }, 622 | { 623 | "cell_type": "markdown", 624 | "metadata": { 625 | "pycharm": { 626 | "name": "#%% md\n" 627 | }, 628 | "slideshow": { 629 | "slide_type": "fragment" 630 | } 631 | }, 632 | "source": [ 633 | "A quantum gate acting on a single qubit can be defined by its action on the basis vectors $|0\\rangle$ and $|1\\rangle$." 634 | ] 635 | }, 636 | { 637 | "cell_type": "markdown", 638 | "metadata": { 639 | "pycharm": { 640 | "name": "#%% md\n" 641 | }, 642 | "slideshow": { 643 | "slide_type": "fragment" 644 | } 645 | }, 646 | "source": [ 647 | "X-Gate maps $\\;|0\\rangle$ to $|1\\rangle\\;$ and $\\;|1\\rangle$ to $|0\\rangle$. " 648 | ] 649 | }, 650 | { 651 | "cell_type": "markdown", 652 | "metadata": { 653 | "pycharm": { 654 | "name": "#%% md\n" 655 | }, 656 | "slideshow": { 657 | "slide_type": "fragment" 658 | } 659 | }, 660 | "source": [ 661 | "Hadamard-Gate maps $\\;|0\\rangle\\;$ to $\\;\\frac{|0\\rangle + |1\\rangle}{\\sqrt{2}}\\;\\;$ and $\\;\\;|1\\rangle\\;$ to $\\;\\frac{|0\\rangle - |1\\rangle}{\\sqrt{2}}$." 662 | ] 663 | }, 664 | { 665 | "cell_type": "markdown", 666 | "metadata": { 667 | "pycharm": { 668 | "name": "#%% md\n" 669 | }, 670 | "slideshow": { 671 | "slide_type": "slide" 672 | } 673 | }, 674 | "source": [ 675 | "If we can show that \n", 676 | "$$ H(\\; id( H(|0\\rangle) ) \\;) = |0\\rangle\\, $$\n", 677 | "and \n", 678 | "$$ H(\\;\\, X( H(|0\\rangle) ) \\;) = |0\\rangle, $$ \n", 679 | "it becomes clear that if A applies an H-Gate in both of her moves, she wins the game - independent of the move of B (X or id).\n", 680 | "\n", 681 | "Remember: Heads is encoded by $|0\\rangle$, Tails encoded by $|1\\rangle$. " 682 | ] 683 | }, 684 | { 685 | "cell_type": "markdown", 686 | "metadata": { 687 | "pycharm": { 688 | "name": "#%% md\n" 689 | }, 690 | "slideshow": { 691 | "slide_type": "slide" 692 | } 693 | }, 694 | "source": [ 695 | "The first equation holds because: \n", 696 | "\n", 697 | "\\begin{align*} \n", 698 | "H(\\; id(\\; H(|0\\rangle) \\;)\\; ) \n", 699 | " = &\\;\\; H(\\; H(|0\\rangle)\\; ) \\\\\n", 700 | " = &\\;\\; H(\\; \\frac{|0\\rangle + |1\\rangle}{\\sqrt{2}}\\;) \\\\\n", 701 | " = &\\;\\; \\frac{1}{\\sqrt{2}}\\;(\\; H(|0\\rangle) + H(|1\\rangle) \\;) \\\\\n", 702 | " = &\\;\\; \\frac{1}{\\sqrt{2}}\\;(\\;\\frac{|0\\rangle + |1\\rangle}{\\sqrt{2}} + \\frac{|0\\rangle - |1\\rangle}{\\sqrt{2}}\\;) \\\\\n", 703 | " = &\\;\\; \\frac{1}{{2}}\\; (\\;|0\\rangle + |1\\rangle + |0\\rangle - |1\\rangle\\; )\\\\\n", 704 | " = &\\;\\; |0\\rangle\n", 705 | "\\end{align*}" 706 | ] 707 | }, 708 | { 709 | "cell_type": "markdown", 710 | "metadata": { 711 | "pycharm": { 712 | "name": "#%% md\n" 713 | }, 714 | "slideshow": { 715 | "slide_type": "fragment" 716 | } 717 | }, 718 | "source": [ 719 | "This fundamentaly uses superposition (created by the H-Gate) and in the last equality (destructive) interference of the amplitudes (generalized probabilities) of $|1\\rangle$." 720 | ] 721 | }, 722 | { 723 | "cell_type": "markdown", 724 | "metadata": { 725 | "pycharm": { 726 | "name": "#%% md\n" 727 | }, 728 | "slideshow": { 729 | "slide_type": "slide" 730 | } 731 | }, 732 | "source": [ 733 | "In case B choses to use an X-Gate instead of id, the following identity\n", 734 | "\n", 735 | "$$ X(\\; H(|0\\rangle) \\;) = X\\; (\\;\\frac{|0\\rangle + |1\\rangle}{\\sqrt{2}}\\; ) = \\frac{|1\\rangle + |0\\rangle}{\\sqrt{2}} = H(|0\\rangle) $$\n", 736 | "\n", 737 | "can be used to show that the final state is $ |0\\rangle$:\n", 738 | "\n", 739 | "$$ H(\\; X( H(|0\\rangle) ) \\;) = H(\\; H(|0\\rangle)\\; ) = |0\\rangle $$\n", 740 | "\n", 741 | "[These charts](https://github.com/JanLahmann/Fun-with-Quantum/raw/master/QuantumTheory-for-QuantumCoinGame.pdf) explain a bit more of the quantum theory and formlism required to prove the above identities, in case you are interested." 742 | ] 743 | }, 744 | { 745 | "cell_type": "markdown", 746 | "metadata": { 747 | "pycharm": { 748 | "name": "#%% md\n" 749 | }, 750 | "slideshow": { 751 | "slide_type": "slide" 752 | } 753 | }, 754 | "source": [ 755 | "We now have shown that the final state is always $ |0\\rangle$, independent of the move of B. \n", 756 | "\n", 757 | "Thus, A wins the game with certainty, if she applies an H-Gate in both of her moves." 758 | ] 759 | }, 760 | { 761 | "cell_type": "markdown", 762 | "metadata": { 763 | "pycharm": { 764 | "name": "#%% md\n" 765 | }, 766 | "slideshow": { 767 | "slide_type": "slide" 768 | } 769 | }, 770 | "source": [ 771 | "How do you interpret this? \n", 772 | "Is it a \"sleight of hand\" or is there more to it? " 773 | ] 774 | }, 775 | { 776 | "cell_type": "markdown", 777 | "metadata": { 778 | "pycharm": { 779 | "name": "#%% md\n" 780 | }, 781 | "slideshow": { 782 | "slide_type": "fragment" 783 | } 784 | }, 785 | "source": [ 786 | "Hint: \n", 787 | "What if an \"algorithm\" could use the \"extra moves\" to calculate answers that cannot be calculated (or take much longer to be calculated) with \"classical moves\"?" 788 | ] 789 | } 790 | ], 791 | "metadata": { 792 | "anaconda-cloud": {}, 793 | "celltoolbar": "Slideshow", 794 | "kernelspec": { 795 | "display_name": "Python 3 (ipykernel)", 796 | "language": "python", 797 | "name": "python3" 798 | }, 799 | "language_info": { 800 | "codemirror_mode": { 801 | "name": "ipython", 802 | "version": 3 803 | }, 804 | "file_extension": ".py", 805 | "mimetype": "text/x-python", 806 | "name": "python", 807 | "nbconvert_exporter": "python", 808 | "pygments_lexer": "ipython3", 809 | "version": "3.9.13" 810 | }, 811 | "latex_envs": { 812 | "bibliofile": "biblio.bib", 813 | "cite_by": "apalike", 814 | "current_citInitial": 1, 815 | "eqLabelWithNumbers": true, 816 | "eqNumInitial": 0 817 | }, 818 | "nav_menu": {}, 819 | "toc": { 820 | "navigate_menu": true, 821 | "number_sections": true, 822 | "sideBar": true, 823 | "threshold": 6, 824 | "toc_cell": false, 825 | "toc_section_display": "block", 826 | "toc_window_display": false 827 | }, 828 | "livereveal": { 829 | "autolaunch": true, 830 | "scroll": true 831 | } 832 | }, 833 | "nbformat": 4, 834 | "nbformat_minor": 4 835 | } -------------------------------------------------------------------------------- /Hardys-Paradox.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "slide" 8 | } 9 | }, 10 | "source": [ 11 | "# Hardy's Paradox\n", 12 | "\n", 13 | "This variant of Hardy's Paradox is a relatively simple example for an entangled qubit state that could not be reproduced by a few classical bits and a random number generator. It shows that quantum variables aren't just classical variables with some randomness bundled in. There are no \"hidden, local variables\".\n", 14 | "Especially, this paradox shows that classical reasoning is not applicable in quantum systems or in D. Mermins words: \"What didn't happen, didn't happen\". Meaning: We cannot infer the previous state of qubits by looking at the non-deterministic results of measurements. This also means that we cannot conclude hypothetical circuits that _never happened_ from our measurement. _We cannot infer something that didn't happen from something that happened._\n", 15 | "\n", 16 | "See D. Mermins book [Quantum Computer Science: An Introduction](https://library.uoh.edu.iq/admin/ebooks/22831-quantum_computer_science.pdf) for more information.\n", 17 | "\n", 18 | "Hardy's Paradox nicely illustrates the fundamental difference of Quantum Mechanics and classical physics. In particular, it can be used to discuss the claim made by Einstein, Podolsky and Rosen (\"EPR\") back in 1935. They objected to the uncertainty seen in quantum mechanics, and thought it meant that the theory was incomplete. They thought that a qubit should always have a determined output (regardless of the measurement basis), and that it only seems random because some information is hidden from us. In this case, all axes are [complementary observables](https://doi.org/10.1007/s10701-019-00261-3). They describe a property of quantum mechanics where each observable can be observed on its own but when trying to measure multiple at once, uncertainties arise. This essentially dates back to Heisenberg uncertainty principle.\n", 19 | "As Einstein did not believe this, he said: \"God does not play dice with the universe\".\n", 20 | "\n", 21 | "The idea and part of the source code for this tutorial was published in a previous version of the [Qiskit Textbook](https://qiskit.org/textbook/), in the (now removed) chapter [The Unique Properties of Qubits](https://github.com/Qiskit/qiskit-textbook/blob/master/content/ch-states/old-unique-properties-qubits.ipynb).\n", 22 | "\n", 23 | "(hit space or right arrow to move to next slide)" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": { 29 | "slideshow": { 30 | "slide_type": "slide" 31 | } 32 | }, 33 | "source": [ 34 | "## Usage instructions for the user interface\n", 35 | "* \"Ctrl -\" and \"Ctrl +\" (or \"command -\", \"command +\") adjust the zoom level to fit the text to the browser window\n", 36 | "* Use \"space\" and \"shift space\" to navigate through the slides \n", 37 | "* \"Shift Enter\" executes the interactive cells (might need to click the cell, first)\n", 38 | "* Execute the interactive cells on each slide (\"In [1]:\", etc)\n", 39 | "* In case a cell is not formatted correctly, try to double-click and then \"Shift Enter\" to re-execute\n", 40 | "* Interactive cells can be modified, if needed\n", 41 | "* \"X\" at the top left exits the slideshow and enters the jupyter notebook interface" 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": { 47 | "slideshow": { 48 | "slide_type": "slide" 49 | } 50 | }, 51 | "source": [ 52 | "## Manufacturing Quantum-Cars\n", 53 | "\n", 54 | "Let's assume we build cars.\n", 55 | "The cars have a color (red or blue) and an engine type (gasoline or diesel).\n", 56 | "\n", 57 | "The director of the production plant ensures us that the following is always true for the first two cars that leave the plant each morning:\n", 58 | "\n", 59 | "1. If we look at the colors of both cars, it never happens that both are red.\n", 60 | "2. If we measure the engine type of one car to be diesel, then the other car is red.\n", 61 | "\n", 62 | "Take a moment to think about this: Can both cars be diesel?\n", 63 | "You probably came to the conclusion that this case is impossible. Using classical argumentation this seems obvious. Later we will discuss why this conclusion does not apply here in the quantum world. _Did you maybe infer something that didn't happen?_\n", 64 | "\n", 65 | "Let's encode the two cars with two qubits. The colors and the engine type will be encoded by a measurement in the (standard) Z Basis and the X Basis, where 0 relates to red or gasoline and 1 relates to blue or diesel.\n", 66 | "\n", 67 | "Or in short:\n", 68 | "\n", 69 | "Z color: 0 red, 1 blue\n", 70 | "\n", 71 | "X engine type: 0 gasoline, 1: diesel\n", 72 | "\n", 73 | "We will create a quantum state which meets the two conditions above, but still allows two diesel cars to be the first to leave the plant which obviously would be impossible in the classical world." 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "metadata": { 79 | "slideshow": { 80 | "slide_type": "slide" 81 | } 82 | }, 83 | "source": [ 84 | "We now initialize the quantum circuit and create a specific state of the two qubits.\n", 85 | "\n", 86 | "We will show that this state satisfies the two conditions mentioned before. \n", 87 | "\n", 88 | "We will then analyze the question if both cars can be diesel." 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": null, 94 | "metadata": { 95 | "slideshow": { 96 | "slide_type": "fragment" 97 | } 98 | }, 99 | "outputs": [], 100 | "source": "from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit\nfrom qiskit.visualization import plot_histogram\nfrom qiskit_aer import AerSimulator\nimport math" 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": { 105 | "slideshow": { 106 | "slide_type": "slide" 107 | } 108 | }, 109 | "source": [ 110 | "### Circuit creation\n", 111 | "\n", 112 | "In the following, a specific entangled state of the two qubits will be created.\n", 113 | "\n", 114 | "At first, a quantum register and circuit with two qubits is created." 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": 2, 120 | "metadata": { 121 | "slideshow": { 122 | "slide_type": "fragment" 123 | } 124 | }, 125 | "outputs": [], 126 | "source": [ 127 | "# hit \"shift + Enter\" to execute this cell\n", 128 | "q = QuantumRegister(2) # create a quantum register with two qubits\n", 129 | "# create a classical register that will hold the results of the measurement\n", 130 | "c = ClassicalRegister(2) \n", 131 | "qc_hardy = QuantumCircuit(q, c)" 132 | ] 133 | }, 134 | { 135 | "cell_type": "markdown", 136 | "metadata": { 137 | "slideshow": { 138 | "slide_type": "subslide" 139 | } 140 | }, 141 | "source": [ 142 | "The initial $ |00\\rangle $ state is now modified with several gates. We will explain the related maths in detail later (see \"Calculating the Statevector $h_{end}$\" at the end of this notebook)." 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": 3, 148 | "metadata": { 149 | "slideshow": { 150 | "slide_type": "fragment" 151 | } 152 | }, 153 | "outputs": [ 154 | { 155 | "name": "stdout", 156 | "output_type": "stream", 157 | "text": [ 158 | "alpha = 1.9106332362490186\n", 159 | "Rounded alpha = 1.911\n" 160 | ] 161 | }, 162 | { 163 | "data": { 164 | "image/png": "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\n", 165 | "text/plain": [ 166 | "
" 167 | ] 168 | }, 169 | "execution_count": 3, 170 | "metadata": {}, 171 | "output_type": "execute_result" 172 | } 173 | ], 174 | "source": [ 175 | "#STEP 1\n", 176 | "alpha = 2*math.acos(math.sqrt(1/3))\n", 177 | "print(f\"alpha = {alpha}\")\n", 178 | "print(f\"Rounded alpha = {round(alpha, 3)}\")\n", 179 | "qc_hardy.ry(alpha,q[1])\n", 180 | "#STEP 2\n", 181 | "qc_hardy.cx(q[1],q[0])\n", 182 | "#STEP 3\n", 183 | "qc_hardy.ry((1/4)*math.pi,q[0])\n", 184 | "qc_hardy.cx(q[1],q[0])\n", 185 | "qc_hardy.ry((3/4)*math.pi,q[0])\n", 186 | "\n", 187 | "qc_hardy.draw(output='mpl')" 188 | ] 189 | }, 190 | { 191 | "cell_type": "markdown", 192 | "metadata": { 193 | "slideshow": { 194 | "slide_type": "subslide" 195 | } 196 | }, 197 | "source": [ 198 | "Alpha $\\alpha$ is chosen in a way that the quantum state becomes an **equal** distribution between the states BLUE/BLUE, BLUE/RED and RED/BLUE.\n", 199 | "\n", 200 | "After the circuit we end up with the statevector: $ h_{end} = \\begin{pmatrix} 0 \\\\ \\sqrt{\\frac{1}{3}} \\\\ \\sqrt{\\frac{1}{3}} \\\\ \\sqrt{\\frac{1}{3}} \\end{pmatrix}$\n", 201 | "\n", 202 | "_(Reminder: This should be read as $\\begin{pmatrix} \"00\" \\\\ \"01\" \\\\ \"10\" \\\\ \"11\" \\end{pmatrix}$ with $q_1$ being the left and $q_0$ the right qubit. This is Qiskits normal way of ordering qubits. See more information [here](https://qiskit.org/textbook/ch-gates/multiple-qubits-entangled-states.html).)_\n", 203 | "\n", 204 | "Which gates exactly lead to property #2 being fulfilled is not obvious. There is no simple way of calculating the necessary steps. Therefore we will just accept this circuit as given." 205 | ] 206 | }, 207 | { 208 | "cell_type": "markdown", 209 | "metadata": { 210 | "slideshow": { 211 | "slide_type": "slide" 212 | } 213 | }, 214 | "source": [ 215 | "### Circuit interpretation\n", 216 | "\n", 217 | "Let's see what happens if we look at the color of both cars, i.e. if we make an Z measurement on each of the qubits.\n", 218 | "\n", 219 | "A result of 00 would indicate that both cars are red, which is not allowed by property #1." 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": null, 225 | "metadata": { 226 | "slideshow": { 227 | "slide_type": "fragment" 228 | } 229 | }, 230 | "outputs": [], 231 | "source": "measurementsZZ = QuantumCircuit(q,c)\n# z measurement on both qubits\nmeasurementsZZ.measure(q[0],c[0])\nmeasurementsZZ.measure(q[1],c[1])\nqc = qc_hardy.compose(measurementsZZ, inplace=False)\nprint('Results for two z (=color) measurements:')\nsimulator = AerSimulator()\nplot_histogram(simulator.run(qc).result().get_counts())" 232 | }, 233 | { 234 | "cell_type": "markdown", 235 | "metadata": { 236 | "slideshow": { 237 | "slide_type": "fragment" 238 | } 239 | }, 240 | "source": [ 241 | "The count of \"00\" is zero, and so these qubits do indeed satisfy property #1." 242 | ] 243 | }, 244 | { 245 | "cell_type": "markdown", 246 | "metadata": { 247 | "slideshow": { 248 | "slide_type": "slide" 249 | } 250 | }, 251 | "source": [ 252 | "Next, let's see the results of an X-basis (engine type) measurement of $q_O$ and a Z-basis (color) measurement of $q_1$.\n", 253 | "See the [Qiskit Textbook](https://qiskit.org/textbook/ch-labs/Lab02_QuantumMeasurement.html) for doing measurements in the X Basis, instead of the standard Z Basis.\n", 254 | "\n", 255 | "A result of 11 would indicate that car 1 is a diesel and car two is blue, which is not allowed by property #2." 256 | ] 257 | }, 258 | { 259 | "cell_type": "code", 260 | "execution_count": null, 261 | "metadata": { 262 | "slideshow": { 263 | "slide_type": "fragment" 264 | } 265 | }, 266 | "outputs": [], 267 | "source": "measurementsXZ = QuantumCircuit(q,c)\n# x measurement on qubit 0\nmeasurementsXZ.h(q[0])\nmeasurementsXZ.measure(q[0],c[0])\n# z measurement on qubit 1\nmeasurementsXZ.measure(q[1],c[1])\nqc = qc_hardy.compose(measurementsXZ, inplace=False)\nprint('Results for an x (engine type) measurement on qubit 0 and a z (color) measurement on qubit 1:')\nplot_histogram(simulator.run(qc).result().get_counts())" 268 | }, 269 | { 270 | "cell_type": "markdown", 271 | "metadata": { 272 | "slideshow": { 273 | "slide_type": "subslide" 274 | } 275 | }, 276 | "source": [ 277 | "The count of \"11\" is zero. This means that property #2 is indeed true.\n", 278 | "\n", 279 | "Let's prove this mathematically:\n", 280 | "\n", 281 | "The result of a X Basis measurement on $q_0$ and a Z Basis measurement on $q_1$ can be calculated with:\n", 282 | "\n", 283 | "$$ \n", 284 | "\\left( I \\otimes H \\right) \\cdot h_{end}=\n", 285 | "\\left( I \\otimes H \\right) \\cdot\n", 286 | "\\begin{pmatrix} 0 \\\\ \\sqrt{\\frac{1}{3}} \\\\ \\sqrt{\\frac{1}{3}} \\\\ \\sqrt{\\frac{1}{3}} \\end{pmatrix} =\n", 287 | "\\left( \\begin{pmatrix} 1 & 0 \\\\ 0 & 1 \\end{pmatrix} \\otimes \\frac{1}{\\sqrt{2}} \\begin{pmatrix} 1 & 1 \\\\ 1 & -1 \\end{pmatrix} \\right) \\cdot\n", 288 | "\\begin{pmatrix} 0 \\\\ \\sqrt{\\frac{1}{3}} \\\\ \\sqrt{\\frac{1}{3}} \\\\ \\sqrt{\\frac{1}{3}} \\end{pmatrix} \\\\ =\n", 289 | "\\frac{1}{\\sqrt{2}} \\begin{pmatrix} 1 & 1 & 0 & 0 \\\\ 1 & -1 & 0 & 0 \\\\ 0 & 0 & 1 & 1 \\\\ 0 & 0 & 1 & -1 \\end{pmatrix} \\cdot\n", 290 | "\\begin{pmatrix} 0 \\\\ \\sqrt{\\frac{1}{3}} \\\\ \\sqrt{\\frac{1}{3}} \\\\ \\sqrt{\\frac{1}{3}} \\end{pmatrix} =\n", 291 | "\\frac{1}{\\sqrt{2}} \\begin{pmatrix} \\sqrt{\\frac{1}{3}} \\\\ -\\sqrt{\\frac{1}{3}} \\\\ 2\\sqrt{\\frac{1}{3}} \\\\ 0 \\end{pmatrix} =\n", 292 | "\\begin{pmatrix} \\sqrt{\\frac{1}{6}} \\\\ \\sqrt{\\frac{1}{6}} \\\\ \\sqrt{\\frac{2}{3}} \\\\ 0 \\end{pmatrix} $$" 293 | ] 294 | }, 295 | { 296 | "cell_type": "markdown", 297 | "metadata": { 298 | "slideshow": { 299 | "slide_type": "slide" 300 | } 301 | }, 302 | "source": [ 303 | "If we also show that the same is true if we measure the other way around (so measuring the X-basis of $q_1$), we have shown that the cars (qubits) satisfy property #2." 304 | ] 305 | }, 306 | { 307 | "cell_type": "code", 308 | "execution_count": null, 309 | "metadata": { 310 | "slideshow": { 311 | "slide_type": "fragment" 312 | } 313 | }, 314 | "outputs": [], 315 | "source": "measurementsZX = QuantumCircuit(q,c)\n# z measurement on qubit 0\nmeasurementsZX.measure(q[0],c[0])\n# x measurement on qubit 1\nmeasurementsZX.h(q[1])\nmeasurementsZX.measure(q[1],c[1])\nqc = qc_hardy.compose(measurementsZX, inplace=False)\nprint('Results for an z (color) measurement on qubit 0 and a x (engine type) measurement on qubit 1:')\nplot_histogram(simulator.run(qc).result().get_counts())" 316 | }, 317 | { 318 | "cell_type": "markdown", 319 | "metadata": { 320 | "slideshow": { 321 | "slide_type": "fragment" 322 | } 323 | }, 324 | "source": [ 325 | "As result \"11\" never occurs, property #2 also holds true. " 326 | ] 327 | }, 328 | { 329 | "cell_type": "markdown", 330 | "metadata": { 331 | "slideshow": { 332 | "slide_type": "slide" 333 | } 334 | }, 335 | "source": [ 336 | "What can we now infer **classically** about the engine types of both cars?\n", 337 | "\n", 338 | "Let's first recall the properties we have confirmed:\n", 339 | "\n", 340 | "1. If we look at the colors of the cars, it never happens that both are red.\n", 341 | "2. If the engine type of one car is diesel, then the other car is red.\n", 342 | "\n", 343 | "Let's assume we measure the engine type for both cars and both would be diesel. Then by applying property #2, we can (classically) deduce what the result would have been if we had made color measurements instead: We would have gotten an output of red for both.\n", 344 | "\n", 345 | "However, this result is impossible according to property #1. Classically, we can therefore conclude that it must be impossible that both cars are diesel." 346 | ] 347 | }, 348 | { 349 | "cell_type": "markdown", 350 | "metadata": { 351 | "slideshow": { 352 | "slide_type": "slide" 353 | } 354 | }, 355 | "source": [ 356 | "But now let's do a measurement of the engine type for both cars, i.e. a measurement in the x basis for both qubits. Now we should see that it is impossible that both cars are diesel, right? " 357 | ] 358 | }, 359 | { 360 | "cell_type": "code", 361 | "execution_count": null, 362 | "metadata": { 363 | "slideshow": { 364 | "slide_type": "fragment" 365 | } 366 | }, 367 | "outputs": [], 368 | "source": "measurementsXX = QuantumCircuit(q,c)\nmeasurementsXX.h(q[0])\nmeasurementsXX.measure(q[0],c[0])\nmeasurementsXX.h(q[1])\nmeasurementsXX.measure(q[1],c[1])\nqc = qc_hardy.compose(measurementsXX, inplace=False)\nprint('Results for two x (engine type) measurement on both qubits:')\nplot_histogram(simulator.run(qc).result().get_counts())" 369 | }, 370 | { 371 | "cell_type": "markdown", 372 | "metadata": { 373 | "slideshow": { 374 | "slide_type": "fragment" 375 | } 376 | }, 377 | "source": [ 378 | "The results prove our **classical** reasoning wrong. \n", 379 | "To understand what happened let us first take a look at the background of the paradox." 380 | ] 381 | }, 382 | { 383 | "cell_type": "markdown", 384 | "metadata": { 385 | "slideshow": { 386 | "slide_type": "slide" 387 | } 388 | }, 389 | "source": [ 390 | "## Background on Hardy's original Paradox\n", 391 | "\n", 392 | "\n", 393 | "In their famous paper in 1935, EPR (Einstein, Podolsky, Rosen) essentially claimed that qubits can indeed be described by some form of classical variable. They did not know how to do it, but they were sure it could be done. Then quantum mechanics could be replaced by a theory much closer to our classical world.\n", 394 | "\n", 395 | "It took until 1964 to show that they were wrong. J. S. Bell proved that quantum variables behaved in a way that was fundamentally unique. Since then, many new ways have been found to prove this experimentally, and extensive experiments have been done to show that this is exactly the way the universe works on a quantum mechanical level. We'll now consider a simple demonstration, using a variant of Hardy’s paradox." 396 | ] 397 | }, 398 | { 399 | "cell_type": "markdown", 400 | "metadata": { 401 | "slideshow": { 402 | "slide_type": "subslide" 403 | } 404 | }, 405 | "source": [ 406 | "### A thought experiment in quantum mechanics\n", 407 | "\n", 408 | "\n", 409 | "Originally, Hardy's paradox described a thought experiment by Lucien Hardy in the area of quantum mechanics.\n", 410 | "\n", 411 | "![experiment setup](images/Hardys_paradox.png)\n", 412 | "\n", 413 | "_Image Source: https://en.wikipedia.org/wiki/Hardy%27s_paradox (23.09.2022)_\n", 414 | "\n", 415 | "The experiment focuses on a particle (positron $e^+$) and its anti-particle (electron $e^-$) which interact without annihilating each other. The two particles interfere with each other by two interferometers which are arranged in a way so that the paths of both particles overlap (point $P$). Our equivalent to the two particles are the two cars.\n", 416 | "\n", 417 | "The points marked $BS$ on the picture are beam splitters which make the passing particle go either the path $w$ or $v$ (at least in classical physics). But as these particles are quantum objects, the truth is that the particle is in a superposition between the two paths. The particle now has a $w$ and a $v$ amplitude (learn more [in the Qiskit Textbook](https://qiskit.org/textbook/ch-states/representing-qubit-states.html)).\n", 418 | "\n" 419 | ] 420 | }, 421 | { 422 | "cell_type": "markdown", 423 | "metadata": { 424 | "slideshow": { 425 | "slide_type": "subslide" 426 | } 427 | }, 428 | "source": [ 429 | "The experiment is now set up in such a way that if each interferometer operates on its own (no overlapping paths), the measurement results at the points marked $c$ or $d$ are constant and reproducible. In particular: The positron $e^+$ always interferes constructively with itself and therefore gets detected at $c^+$. The electron $e^-$ always interferes constructively with itself and therefore gets detected at $c^-$.\n", 430 | "\n", 431 | "If now the experiment is set up as shown in the image so that there are two paths which overlap, it is possible that two particles obstruct each other in the point $P$.\n", 432 | "**For that, we measure the particles directly after point $P$ (detect if they are present in $u^+$ or $u^-$) and in the detectors $c$ and $d$.**\n", 433 | "\n", 434 | "In the case that this obstruction takes place e.g. when the $w^-$ amplitude of $e^-$ obstructs the $w^+$ amplitude of $e^+$, the presence of a particle in $u^-$ but **not** in $u^+$ is detected.\n", 435 | "\n", 436 | "It is measured that the detectors $d^+$ and $c^+$ detect the particle $e^+$ equally (in contrast to our default case that the particle is detected only in $c^+$) because only the $v^+$ amplitude reached the second beam splitter and was split equally. The other particle $e^-$ was not affected and is therefore **only** detected in $c^-$.\n", 437 | "\n", 438 | "Meaning: If our detectors $d^+$ or $d^-$ detect the particle, then an obstruction took place at the point $P$.\n", 439 | "\n", 440 | "> - If $d^+$ detects a particle, the other particle is present in $u^-$ and therefore the amplitude $w^+$ of $e^+$ was obstructed.\n", 441 | "> - If $d^-$ detects a particle, the other particle is present in $u^+$ and therefore the amplitude $w^-$ of $e^-$ was obstructed." 442 | ] 443 | }, 444 | { 445 | "cell_type": "markdown", 446 | "metadata": { 447 | "slideshow": { 448 | "slide_type": "subslide" 449 | } 450 | }, 451 | "source": [ 452 | "All of this also means that\n", 453 | "\n", 454 | "1. It is not possible to detect the particles in $u^+$ and $u^-$ at the same time. (Because in this case one must have obstructed the other)\n", 455 | "2. If you detect a particle in $d^+$ (or $d^-$) the other particle has to be detected in $u^-$ (or $u^+$).\n", 456 | "\n", 457 | "These two properties directly compare to the two properties from our car plant example:\n", 458 | "\n", 459 | "> 1. If we look at the colors of both cars, it never happens that both are red.\n", 460 | "> 2. If we measure the engine type of one car to be diesel, then the other car is red.\n", 461 | "\n", 462 | "\n", 463 | "Therefore, the two measurement points also directly compare to our car plant example:\n", 464 | "\n", 465 | "| **Hardys Paradox** \t| **Car Plant Example** \t| **Qubit** \t|\n", 466 | "|:---------------------------:\t|:---------------------:\t|:---------------------:|\n", 467 | "| Measurement after point $P$ \t| Color Measurement \t| Z Basis Measurement \t|\n", 468 | "| Measurements at $c$ and $d$ \t| Engine Measurement \t| X Basis Measurement \t|\n", 469 | "\n", 470 | "The two properties from this example also imply **by classical logic** that it is not possible to detect particles at $d^+$ and $d^-$ at the same time.\n", 471 | "\n", 472 | "**But the experiments prove us wrong: This case is indeed possible if we do not measure directly after point $P$ but only in $d^+$ and $d^-$.**\n", 473 | "\n", 474 | "But shouldn't then both particles annihilate each other at $P$ ? Yes, indeed classical physics would imply that but as the particle was in the superposition between both paths, this did not happen. Again, this proofs EPR wrong in their claim that there have to be _\"local hidden variables\"_ which indicate which path exactly ($w$ **or** $v$) the particle took in the end. The particle did not take any discrete path. The particle was in a superposition between both paths.\n", 475 | "\n", 476 | "To learn more about Hardy's paradox in quantum mechanics have a look at [Wikipedia: Hardy's Paradox](https://en.wikipedia.org/wiki/Hardy%27s_paradox)." 477 | ] 478 | }, 479 | { 480 | "cell_type": "markdown", 481 | "metadata": { 482 | "slideshow": { 483 | "slide_type": "slide" 484 | } 485 | }, 486 | "source": [ 487 | "## What went wrong in the plant?\n", 488 | "\n", 489 | "Our mistake when trying to apply classical logic to a quantum mechanical system was in the following piece of reasoning.\n", 490 | "\n", 491 | "* By applying property 2 we thought we could deduce what the result will be if we had made z measurements instead.\n", 492 | "\n", 493 | "We used our knowledge of the x (color) outputs to work out what the z (engine type) outputs were. Once we’d done that, we assumed that we were certain about the value of both.\n", 494 | "\n", 495 | "To underline this paradox, let's have a look at the calculations regarding the measurement of the X Basis on both qubits (which is not allowed to return \"11\" following our classical reasoning). \n", 496 | "\n", 497 | "H-Gate on $q_0$ and $q_1$:\n", 498 | "$$ \n", 499 | "\\left( H \\otimes H \\right) \\cdot h_{end}=\n", 500 | "\\left( H \\otimes H \\right) \\cdot\n", 501 | "\\begin{pmatrix} 0 \\\\ \\sqrt{\\frac{1}{3}} \\\\ \\sqrt{\\frac{1}{3}} \\\\ \\sqrt{\\frac{1}{3}} \\end{pmatrix} =\n", 502 | "\\left( \\frac{1}{\\sqrt{2}} \\begin{pmatrix} 1 & 1 \\\\ 1 & -1 \\end{pmatrix} \\otimes \\frac{1}{\\sqrt{2}} \\begin{pmatrix} 1 & 1 \\\\ 1 & -1 \\end{pmatrix} \\right) \\cdot\n", 503 | "\\begin{pmatrix} 0 \\\\ \\sqrt{\\frac{1}{3}} \\\\ \\sqrt{\\frac{1}{3}} \\\\ \\sqrt{\\frac{1}{3}} \\end{pmatrix} \\\\ =\n", 504 | "\\frac{1}{2} \\begin{pmatrix} 1 & 1 & 1 & 1 \\\\ 1 & -1 & 1 & -1 \\\\ 1 & 1 & -1 & -1 \\\\ 1 & -1 & -1 & 1 \\end{pmatrix} \\cdot\n", 505 | "\\begin{pmatrix} 0 \\\\ \\sqrt{\\frac{1}{3}} \\\\ \\sqrt{\\frac{1}{3}} \\\\ \\sqrt{\\frac{1}{3}} \\end{pmatrix} =\n", 506 | "\\frac{1}{2} \\begin{pmatrix} 3 \\cdot \\sqrt{\\frac{1}{3}} \\\\ -\\sqrt{\\frac{1}{3}} \\\\ -\\sqrt{\\frac{1}{3}} \\\\ -\\sqrt{\\frac{1}{3}} \\end{pmatrix} =\n", 507 | "\\begin{pmatrix} \\sqrt{\\frac{3}{4}} \\\\ \\sqrt{\\frac{1}{12}} \\\\ \\sqrt{\\frac{1}{12}} \\\\ \\sqrt{\\frac{1}{12}} \\end{pmatrix} $$\n" 508 | ] 509 | }, 510 | { 511 | "cell_type": "markdown", 512 | "metadata": { 513 | "slideshow": { 514 | "slide_type": "subslide" 515 | } 516 | }, 517 | "source": [ 518 | "This shows that the basis state \"11\" indeed has non-zero probability, in contrast to what we deduced classically.\n", 519 | "\n", 520 | "Our logic would be completely valid if we were not reasoning about quantum objects.\n", 521 | "But as D.Mermin concludes at the end of his excellent book [Quantum Computer Science: An Introduction](https://library.uoh.edu.iq/admin/ebooks/22831-quantum_computer_science.pdf), for quantum objects you have to accept \"what didn't happen didn't happen\", i.e. we cannot make an assumptions about a measurement that wasn't done.\n", 522 | "\n", 523 | "The idea that measuring something is a completely neutral action is a common misconception. You always have to keep in mind that in quantum physics the measurement changes the system.\n", 524 | "\n", 525 | "This is (part of) what makes quantum computers able to outperform classical computers. It leads to effects that allow programs made with quantum variables to solve problems in ways that those with normal variables cannot. But just because qubits don’t follow the same logic as normal computers, it does not mean they defy logic entirely. They obey the definite rules laid out by quantum mechanics." 526 | ] 527 | }, 528 | { 529 | "cell_type": "markdown", 530 | "metadata": { 531 | "slideshow": { 532 | "slide_type": "slide" 533 | } 534 | }, 535 | "source": [ 536 | "## Calculating the Statevector $h_{end}$\n", 537 | "#### Step 1: The RY-Gate $ RY(\\theta)$\n", 538 | "\n", 539 | "At first, the second qubit $q_1$ is rotated around the Y-Axis by a specific value $ \\theta $, in this case \n", 540 | "\n", 541 | "$$ \n", 542 | "\\theta = \\alpha =\n", 543 | "2 \\cdot arccos\\left(\\sqrt{\\frac{1}{3}}\\right)\n", 544 | "$$\n", 545 | "\n", 546 | "*(Reminder: A rotation by $ \\theta = \\pi$ equals the Y-Gate.)*\n", 547 | "\n", 548 | "All following mathematical representations follow the [Qiskit Documentation](https://qiskit.org/documentation/index.html).\n", 549 | "Find the RY-Gate [here](https://qiskit.org/documentation/stubs/qiskit.circuit.library.RYGate.html).\n", 550 | "\n", 551 | "Mathematically the RY-Gate is given by:\n", 552 | "\n", 553 | "$$ RY\\left(\\theta\\right) = exp\\left(-i\\frac{\\theta}{2}Y\\right) =\n", 554 | "\\begin{pmatrix}\n", 555 | "cos\\left(\\frac{\\theta}{2}\\right) & -sin\\left(\\frac{\\theta}{2}\\right)\\\\\n", 556 | "sin\\left(\\frac{\\theta}{2}\\right) & cos\\left(\\frac{\\theta}{2}\\right)\n", 557 | "\\end{pmatrix} $$" 558 | ] 559 | }, 560 | { 561 | "cell_type": "markdown", 562 | "metadata": { 563 | "slideshow": { 564 | "slide_type": "subslide" 565 | } 566 | }, 567 | "source": [ 568 | "The matrix representation for this case is therefore given by:\n", 569 | "\n", 570 | "$$ RY\\left(\\alpha\\right) \\cdot |0\\rangle =\n", 571 | "exp\\left(-i\\frac{\\alpha}{2}Y\\right) \\cdot |0\\rangle = \\\\\n", 572 | "\\begin{pmatrix}\n", 573 | "cos\\left(\\frac{\\alpha}{2}\\right) & -sin\\left(\\frac{\\alpha}{2}\\right)\\\\\n", 574 | "sin\\left(\\frac{\\alpha}{2}\\right) & cos\\left(\\frac{\\alpha}{2}\\right)\n", 575 | "\\end{pmatrix} \\cdot \\begin{pmatrix} 1 \\\\ 0 \\end{pmatrix}=\n", 576 | "\\begin{pmatrix} cos\\left(\\frac{\\alpha}{2}\\right) \\\\ sin\\left(\\frac{\\alpha}{2}\\right) \\end{pmatrix} =\n", 577 | "\\begin{pmatrix} \\sqrt{\\frac{1}{3}} \\\\ \\sqrt{\\frac{2}{3}} \\end{pmatrix} $$\n", 578 | "\n", 579 | "So now Z-measurements (also the standard measurement basis) of the second qubit $q_1$ should approximately result in 1 two thirds of the time and in 0 one third of the time.\n", 580 | "\n", 581 | "To go on with the calculations we have to create the state-vector which combines both qubits. We achieve this by using the tensor-product:\n", 582 | "\n", 583 | "$$ \\begin{pmatrix} cos\\left(\\frac{\\alpha}{2}\\right) \\\\ sin\\left(\\frac{\\alpha}{2}\\right) \\end{pmatrix} \\otimes\n", 584 | "\\begin{pmatrix} 1 \\\\ 0 \\end{pmatrix} =\n", 585 | "\\begin{pmatrix} cos\\left(\\frac{\\alpha}{2}\\right) \\\\ 0 \\\\ sin\\left(\\frac{\\alpha}{2}\\right) \\\\ 0 \\end{pmatrix} $$\n", 586 | "\n", 587 | "We now have a state vector describing this two qubit system." 588 | ] 589 | }, 590 | { 591 | "cell_type": "markdown", 592 | "metadata": { 593 | "slideshow": { 594 | "slide_type": "slide" 595 | } 596 | }, 597 | "source": [ 598 | "#### Step 2: Creating an entangled state\n", 599 | "\n", 600 | "Now both qubits will be entangled which means that they cannot be described independently of each other. They are now **one system**.\n", 601 | "\n", 602 | "This is done by using the CNOT-Gate on the qubit $q_0$ as target- and $q_1$ as control-qubit.\n", 603 | " *Reminder: The CNOT-Gate $CX$ can be described as a conditional X-Gate. Only if the control-qubit is 1, the X-Gate is performed on the target-qubit. But rather than trying to understand everything via this explanation, try to focus on the maths and everything will make more sense. Find more information [here](https://qiskit.org/textbook/ch-gates/multiple-qubits-entangled-states.html#3.1-The-CNOT-Gate-).*\n", 604 | "\n", 605 | "The property that every following action is now performed on one and only one system (which contains two entangled qubits) is much more visible when we have a look at the matrix representation of the CNOT-Gate:\n", 606 | "\n", 607 | "$$ CX = \\begin{pmatrix} 1 & 0 & 0 & 0 \\\\ 0 & 1 & 0 & 0 \\\\ 0 & 0 & 0 & 1 \\\\ 0 & 0 & 1 & 0 \\end{pmatrix} $$\n", 608 | "\n", 609 | "(But careful: The CNOT-Gates matrix representation differs according to the chosen control qubit!)" 610 | ] 611 | }, 612 | { 613 | "cell_type": "markdown", 614 | "metadata": { 615 | "slideshow": { 616 | "slide_type": "subslide" 617 | } 618 | }, 619 | "source": [ 620 | "Now we can use this to calculate our state after step 2.\n", 621 | "\n", 622 | "$$ \\begin{pmatrix} 1 & 0 & 0 & 0 \\\\ 0 & 1 & 0 & 0 \\\\ 0 & 0 & 0 & 1 \\\\ 0 & 0 & 1 & 0 \\end{pmatrix} \\cdot\n", 623 | "\\begin{pmatrix} cos\\left(\\frac{\\alpha}{2}\\right) \\\\ 0 \\\\ sin\\left(\\frac{\\alpha}{2}\\right) \\\\ 0 \\end{pmatrix} =\n", 624 | "\\begin{pmatrix} cos\\left(\\frac{\\alpha}{2}\\right) \\\\ 0 \\\\ 0 \\\\ sin\\left(\\frac{\\alpha}{2}\\right) \\end{pmatrix} =\n", 625 | "\\begin{pmatrix} \\sqrt{\\frac{1}{3}} \\\\ 0 \\\\ 0 \\\\ \\sqrt{\\frac{2}{3}} \\end{pmatrix} $$\n", 626 | "\n", 627 | "As always you can now conclude the probabilities from the vector by looking at the radicands. By now there is no obvious connection to the two properties. Later we will understand that these steps are necessary to fulfill our properties.\n", 628 | "\n", 629 | "_(Reminder: This should be read as $\\begin{pmatrix} \"00\" \\\\ \"01\" \\\\ \"10\" \\\\ \"11\" \\end{pmatrix}$ with $q_1$ being the left and $q_0$ the right qubit.)_\n", 630 | "\n", 631 | "As we see now, it is not intuitive how to extract the independent single states of both qubits from this entangled state-vector or whether this is even possible. And that's the clue: Because they are now one system this is not possible. Therefore, we are now left with a vector which describes the entangled quantum system of both qubits.\n", 632 | "\n", 633 | "Multi-qubit states which **can be separated** are called product states, see D. Mermins [Book](https://library.uoh.edu.iq/admin/ebooks/22831-quantum_computer_science.pdf) for more information." 634 | ] 635 | }, 636 | { 637 | "cell_type": "markdown", 638 | "metadata": { 639 | "slideshow": { 640 | "slide_type": "slide" 641 | } 642 | }, 643 | "source": [ 644 | "#### Step 3\n", 645 | "\n", 646 | "The first property requires our quantum circuit to exclude the 00-state (aka RED/RED) or in other words: The probability of measuring \"00\" in the Z-basis has to be 0.\n", 647 | "For this we use two RY-Gates and one CNOT. To explain what happens, let us first have a look on the $\\theta$-Values of the RY-Gates:\n", 648 | "\n", 649 | "$$ RY_0\\left(\\theta_0\\right) = RY_0\\left(\\frac{\\pi}{4}\\right)$$\n", 650 | "$$ RY_1\\left(\\theta_1\\right) = RY_1\\left(\\frac{3\\pi}{4}\\right)$$\n", 651 | "$$ \\theta_0 + \\theta_1 = \\frac{\\pi}{4}+\\frac{3\\pi}{4} = \\pi$$\n", 652 | "\n", 653 | "This means that both rotations together are equivalent to a Y-Gate. So which role do the CNOT-Gates play?\n", 654 | "To understand the effect of this, let us make a case distinction:\n", 655 | "\n", 656 | "1. In approximately one third of the time, a measurement after step 1 would result in the second qubit $q_1$ being \"0\".\n", 657 | "In this case, **none** of the conditions of the CNOT-Gates (the ones from step 2 and step 3) are met and therefore no Operation on the $q_0$ Qubit is performed (except for the RY-Gates). So all step 3 does is transferring the $\\frac{1}{3}$ probability to the \"01\"-state.\n", 658 | "2. In approximately two thirds of the time, a measurement after step 1 would result in the second qubit $q_1$ being \"1\".\n", 659 | "In this case, **both** of the condition of the CNOT-Gates are met and therefore both times the X-Gate is performed on the $q_0$ Qubit. The outcome of this will be visible after the calculations." 660 | ] 661 | }, 662 | { 663 | "cell_type": "markdown", 664 | "metadata": { 665 | "slideshow": { 666 | "slide_type": "subslide" 667 | } 668 | }, 669 | "source": [ 670 | "Again, I want to stress something here: It should be clear that the CNOT-Gate is not doing \"nothing\" when its condition isn't met. We are still looking at an entangled system and there is one 4×1-vector describing the system. There are no two different calculations for each case above. Its one vector and one calculation (as you will see below) and every vector component describes the probability for one of the basis states. \n", 671 | "\n", 672 | "This is also sometimes referred to as the property of quantum states to have multiple bit states encoded at once which is **simply wrong**. \n", 673 | "The is one **unambigously defined quantum state**. After a measurement, this quantum state falls back in a classical state (e.g. bit state). This measurement sometimes results in different classical states when one circuit is measured multiple times independently." 674 | ] 675 | }, 676 | { 677 | "cell_type": "markdown", 678 | "metadata": { 679 | "slideshow": { 680 | "slide_type": "subslide" 681 | } 682 | }, 683 | "source": [ 684 | "_Reminder: As per definition the Parameter $\\theta$ of the RY-Gate is multitplied by $\\frac{1}{2}$ (see step 1)._\n", 685 | "\n", 686 | "$$ \n", 687 | "RY_0: \\theta_0 = \\frac{\\pi}{4}\\\\\n", 688 | "\\left( I \\otimes RY_0\\left(\\frac{\\pi}{4}\\right) \\right) \\cdot \\begin{pmatrix} cos\\left(\\frac{\\alpha}{2}\\right) \\\\ 0 \\\\ 0 \\\\ sin\\left(\\frac{\\alpha}{2}\\right) \\end{pmatrix} \\\\ =\n", 689 | "\\left(\n", 690 | "\\begin{pmatrix} 1 & 0 \\\\ 0 & 1 \\end{pmatrix}\n", 691 | "\\otimes\n", 692 | "\\begin{pmatrix} cos\\left(\\frac{\\pi}{8}\\right) & -sin\\left(\\frac{\\pi}{8}\\right)\\\\\n", 693 | "sin\\left(\\frac{\\pi}{8}\\right) & cos\\left(\\frac{\\pi}{8}\\right) \\end{pmatrix} \\right)\n", 694 | "\\cdot\n", 695 | "\\begin{pmatrix} cos\\left(\\frac{\\alpha}{2}\\right) \\\\ 0 \\\\ 0 \\\\ sin\\left(\\frac{\\alpha}{2}\\right) \\end{pmatrix} \\\\ =\n", 696 | "\\begin{pmatrix}\n", 697 | "cos\\left(\\frac{\\pi}{8}\\right) & -sin\\left(\\frac{\\pi}{8}\\right) & 0 & 0 \\\\\n", 698 | "sin\\left(\\frac{\\pi}{8}\\right) & cos\\left(\\frac{\\pi}{8}\\right) & 0 & 0 \\\\\n", 699 | "0 & 0 & cos\\left(\\frac{\\pi}{8}\\right) & -sin\\left(\\frac{\\pi}{8}\\right) \\\\\n", 700 | "0 & 0 & sin\\left(\\frac{\\pi}{8}\\right) & cos\\left(\\frac{\\pi}{8}\\right)\n", 701 | "\\end{pmatrix}\n", 702 | "\\cdot\n", 703 | "\\begin{pmatrix} cos\\left(\\frac{\\alpha}{2}\\right) \\\\ 0 \\\\ 0 \\\\ sin\\left(\\frac{\\alpha}{2}\\right) \\end{pmatrix} \\\\ =\n", 704 | "\\begin{pmatrix} \n", 705 | "cos\\left(\\frac{\\pi}{8}\\right) \\cdot cos\\left(\\frac{\\alpha}{2}\\right) \\\\ \n", 706 | "sin\\left(\\frac{\\pi}{8}\\right) \\cdot cos\\left(\\frac{\\alpha}{2}\\right) \\\\ \n", 707 | "-sin\\left(\\frac{\\pi}{8}\\right) \\cdot sin\\left(\\frac{\\alpha}{2}\\right) \\\\ \n", 708 | "cos\\left(\\frac{\\pi}{8}\\right) \\cdot sin\\left(\\frac{\\alpha}{2}\\right)\n", 709 | "\\end{pmatrix}\n", 710 | "\\approx\n", 711 | "\\begin{pmatrix} 0.534 \\\\ 0.221 \\\\ -0.312 \\\\ 0.754 \\end{pmatrix} \\approx\n", 712 | "\\begin{pmatrix} \\sqrt{0.285} \\\\ \\sqrt{0.049} \\\\ \\sqrt{0.097} \\\\ \\sqrt{0.569} \\end{pmatrix} $$\n", 713 | "\n", 714 | "By taking the numbers to the square one can find out the radicands and thereby the probabilities." 715 | ] 716 | }, 717 | { 718 | "cell_type": "markdown", 719 | "metadata": { 720 | "slideshow": { 721 | "slide_type": "subslide" 722 | } 723 | }, 724 | "source": [ 725 | "Now the CNOT-Gate is applied.\n", 726 | "\n", 727 | "$$ CX\\\\\n", 728 | "CX \\cdot\n", 729 | "\\begin{pmatrix}\n", 730 | "cos\\left(\\frac{\\pi}{8}\\right) \\cdot cos\\left(\\frac{\\alpha}{2}\\right) \\\\\n", 731 | "sin\\left(\\frac{\\pi}{8}\\right) \\cdot cos\\left(\\frac{\\alpha}{2}\\right) \\\\\n", 732 | "-sin\\left(\\frac{\\pi}{8}\\right) \\cdot sin\\left(\\frac{\\alpha}{2}\\right) \\\\\n", 733 | "cos\\left(\\frac{\\pi}{8}\\right) \\cdot sin\\left(\\frac{\\alpha}{2}\\right)\n", 734 | "\\end{pmatrix} =\n", 735 | "\\begin{pmatrix} 1 & 0 & 0 & 0 \\\\ 0 & 1 & 0 & 0 \\\\ 0 & 0 & 0 & 1 \\\\ 0 & 0 & 1 & 0 \\end{pmatrix}\n", 736 | "\\cdot\n", 737 | "\\begin{pmatrix}\n", 738 | "cos\\left(\\frac{\\pi}{8}\\right) \\cdot cos\\left(\\frac{\\alpha}{2}\\right) \\\\\n", 739 | "sin\\left(\\frac{\\pi}{8}\\right) \\cdot cos\\left(\\frac{\\alpha}{2}\\right) \\\\\n", 740 | "-sin\\left(\\frac{\\pi}{8}\\right) \\cdot sin\\left(\\frac{\\alpha}{2}\\right) \\\\\n", 741 | "cos\\left(\\frac{\\pi}{8}\\right) \\cdot sin\\left(\\frac{\\alpha}{2}\\right)\n", 742 | "\\end{pmatrix} \\\\ =\n", 743 | "\\begin{pmatrix}\n", 744 | "cos\\left(\\frac{\\pi}{8}\\right) \\cdot cos\\left(\\frac{\\alpha}{2}\\right) \\\\\n", 745 | "sin\\left(\\frac{\\pi}{8}\\right) \\cdot cos\\left(\\frac{\\alpha}{2}\\right) \\\\\n", 746 | "cos\\left(\\frac{\\pi}{8}\\right) \\cdot sin\\left(\\frac{\\alpha}{2}\\right) \\\\\n", 747 | "-sin\\left(\\frac{\\pi}{8}\\right) \\cdot sin\\left(\\frac{\\alpha}{2}\\right)\n", 748 | "\\end{pmatrix}\n", 749 | "\\approx\n", 750 | "\\begin{pmatrix} 0.534 \\\\ 0.221 \\\\ 0.754 \\\\ -0.312 \\end{pmatrix} \\approx\n", 751 | "\\begin{pmatrix} \\sqrt{0.285} \\\\ \\sqrt{0.049} \\\\ \\sqrt{0.569} \\\\ \\sqrt{0.097} \\end{pmatrix} $$" 752 | ] 753 | }, 754 | { 755 | "cell_type": "markdown", 756 | "metadata": { 757 | "slideshow": { 758 | "slide_type": "subslide" 759 | } 760 | }, 761 | "source": [ 762 | "At last, we apply the last RY-Gate.\n", 763 | "\n", 764 | "$$ RY_1: \\theta_1 = \\frac{3\\pi}{4}\\\\\n", 765 | "\\left( I \\otimes RY_1\\left(\\frac{3\\pi}{4}\\right) \\right) \\cdot\n", 766 | "\\begin{pmatrix}\n", 767 | "cos\\left(\\frac{\\pi}{8}\\right) \\cdot cos\\left(\\frac{\\alpha}{2}\\right) \\\\\n", 768 | "sin\\left(\\frac{\\pi}{8}\\right) \\cdot cos\\left(\\frac{\\alpha}{2}\\right) \\\\\n", 769 | "cos\\left(\\frac{\\pi}{8}\\right) \\cdot sin\\left(\\frac{\\alpha}{2}\\right) \\\\\n", 770 | "-sin\\left(\\frac{\\pi}{8}\\right) \\cdot sin\\left(\\frac{\\alpha}{2}\\right)\n", 771 | "\\end{pmatrix} \\\\ =\n", 772 | "\\begin{pmatrix}\n", 773 | "cos\\left(\\frac{3\\pi}{8}\\right) & -sin\\left(\\frac{3\\pi}{8}\\right) & 0 & 0 \\\\\n", 774 | "sin\\left(\\frac{3\\pi}{8}\\right) & cos\\left(\\frac{3\\pi}{8}\\right) & 0 & 0 \\\\\n", 775 | "0 & 0 & cos\\left(\\frac{3\\pi}{8}\\right) & -sin\\left(\\frac{3\\pi}{8}\\right) \\\\\n", 776 | "0 & 0 & sin\\left(\\frac{3\\pi}{8}\\right) & cos\\left(\\frac{3\\pi}{8}\\right)\n", 777 | "\\end{pmatrix}\n", 778 | "\\cdot\n", 779 | "\\begin{pmatrix}\n", 780 | "cos\\left(\\frac{\\pi}{8}\\right) \\cdot cos\\left(\\frac{\\alpha}{2}\\right) \\\\\n", 781 | "sin\\left(\\frac{\\pi}{8}\\right) \\cdot cos\\left(\\frac{\\alpha}{2}\\right) \\\\\n", 782 | "cos\\left(\\frac{\\pi}{8}\\right) \\cdot sin\\left(\\frac{\\alpha}{2}\\right) \\\\\n", 783 | "-sin\\left(\\frac{\\pi}{8}\\right) \\cdot sin\\left(\\frac{\\alpha}{2}\\right)\n", 784 | "\\end{pmatrix} \\\\ = h_{end} =\n", 785 | "\\begin{pmatrix} 0 \\\\ \\sqrt{\\frac{1}{3}} \\\\ \\sqrt{\\frac{1}{3}} \\\\ \\sqrt{\\frac{1}{3}} \\end{pmatrix}$$\n", 786 | "\n", 787 | "As we see now, we have nullified the probability of 00. Therefore, our first property is encoded i.e. RED/RED is not possible while all other combinations occur one-thirds of the time.\n", 788 | "We will continue the calculations with the last state vector which would have been the result if we had not used rounded numbers." 789 | ] 790 | }, 791 | { 792 | "cell_type": "code", 793 | "execution_count": null, 794 | "metadata": { 795 | "slideshow": { 796 | "slide_type": "skip" 797 | } 798 | }, 799 | "outputs": [], 800 | "source": "import qiskit\nprint(f\"Qiskit version: {qiskit.__version__}\")" 801 | } 802 | ], 803 | "metadata": { 804 | "anaconda-cloud": {}, 805 | "celltoolbar": "Slideshow", 806 | "kernelspec": { 807 | "display_name": "Python 3 (ipykernel)", 808 | "language": "python", 809 | "name": "python3" 810 | }, 811 | "language_info": { 812 | "codemirror_mode": { 813 | "name": "ipython", 814 | "version": 3 815 | }, 816 | "file_extension": ".py", 817 | "mimetype": "text/x-python", 818 | "name": "python", 819 | "nbconvert_exporter": "python", 820 | "pygments_lexer": "ipython3", 821 | "version": "3.9.13" 822 | }, 823 | "latex_envs": { 824 | "bibliofile": "biblio.bib", 825 | "cite_by": "apalike", 826 | "current_citInitial": 1, 827 | "eqLabelWithNumbers": true, 828 | "eqNumInitial": 0 829 | }, 830 | "livereveal": { 831 | "autolaunch": true 832 | }, 833 | "nav_menu": {}, 834 | "toc": { 835 | "navigate_menu": true, 836 | "number_sections": true, 837 | "sideBar": true, 838 | "threshold": 6, 839 | "toc_cell": false, 840 | "toc_section_display": "block", 841 | "toc_window_display": false 842 | }, 843 | "vscode": { 844 | "interpreter": { 845 | "hash": "e16cca789c3b3f860c4b37f0e5f47750ae11603099100dca2282e886e70be351" 846 | } 847 | } 848 | }, 849 | "nbformat": 4, 850 | "nbformat_minor": 4 851 | } -------------------------------------------------------------------------------- /GHZ-on-Real-Devices.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "pycharm": { 7 | "name": "#%% md\n" 8 | } 9 | }, 10 | "source": [ 11 | "# Winning the GHZ Game on Real IBM Quantum Computer" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": { 17 | "pycharm": { 18 | "name": "#%% md\n" 19 | } 20 | }, 21 | "source": [ 22 | "*Lennart Schulze, Dr. Jan-Rainer Lahmann, October 2020*" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": { 28 | "pycharm": { 29 | "name": "#%% md\n" 30 | } 31 | }, 32 | "source": [ 33 | "The GHZ game is a serious game for quantum computing to show the quantum mechanical property of entanglement. Three players are asked for two different properties A and B of an object, an each property can be one of two conditions 1 or 0. When all are asked for property A, 1 should appear two times or zero times. When two players are asked for property B, 1 should appear three times ore one time. When the players don't know what the other two were asked, respectively, there is no strategy in classical logic that can lead to winning in all cases. Using Quantum Computing, however, there is. If you wish to find out more about this game, click [here](https://github.com/JanLahmann/Fun-with-Quantum/blob/master/GHZ-Game.ipynb)." 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "metadata": { 39 | "pycharm": { 40 | "name": "#%% md\n" 41 | } 42 | }, 43 | "source": [ 44 | "The Qiskit built-in QASM Aer-simulator for running the Quantum circuit of the GHZ game is perfect, giving us the results that we expected from theory. In reality, however, the imperfection of real Quantum Computing hardware returns \"noisy\" output due to various sources of errors. Luckily, there is a variety of options available to mitigate these effects, some of which we will apply and inspect in this notebook." 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": { 50 | "pycharm": { 51 | "name": "#%% md\n" 52 | } 53 | }, 54 | "source": [ 55 | "In the following, we will have a closer look by:\n", 56 | "1. Comparing IBM Quantum devices [[Go](#devices)]\n", 57 | "2. Manually optimizing our circuit for a specific device [[Go](#manual)]\n", 58 | "3. Using the Qiskit transpiler optimization [[Go](#transpiler)]\n", 59 | "4. Running Measurement Error Mitigation [[Go](#mem)]\n", 60 | "1. Combining the techniques [[Go](#all)]\n", 61 | "\n", 62 | "Appendix [[Go](#appendix)]\n", 63 | "* A: Solution for chapter 5. [[Go](#appendix-a)]\n", 64 | "* B: Running this notebook on real Quantum Computers [[Go](#appendix-b)]\n", 65 | "\n", 66 | "_____" 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": { 72 | "pycharm": { 73 | "name": "#%% md\n" 74 | } 75 | }, 76 | "source": [ 77 | "## Setup\n", 78 | "[[Top](#top)]" 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": { 84 | "pycharm": { 85 | "name": "#%% md\n" 86 | } 87 | }, 88 | "source": [ 89 | "First, let's do the necessary imports:" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "metadata": { 96 | "pycharm": { 97 | "name": "#%%\n" 98 | } 99 | }, 100 | "outputs": [], 101 | "source": [ 102 | "from qiskit import QuantumCircuit, QuantumRegister, transpile # circuit creation\n", 103 | "from qiskit.visualization import plot_histogram # visualize results\n", 104 | "from qiskit_aer import AerSimulator # simulator\n", 105 | "\n", 106 | "import pandas as pd # handy tools for calculation" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": { 112 | "pycharm": { 113 | "name": "#%% md\n" 114 | } 115 | }, 116 | "source": [ 117 | "Next we'll define our global variables: 3 qubits are needed for our circuit (GHZ state), and we use a high number of shots (=times a circuit is executed per run on a device, out of which the relative result distribution is calculated) to get consistent results:" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": null, 123 | "metadata": { 124 | "pycharm": { 125 | "name": "#%%\n" 126 | } 127 | }, 128 | "outputs": [], 129 | "source": [ 130 | "# global vars\n", 131 | "n = 3 # number of qubits\n", 132 | "shots = 8000 # high number of shots per execution" 133 | ] 134 | }, 135 | { 136 | "cell_type": "markdown", 137 | "metadata": { 138 | "pycharm": { 139 | "name": "#%% md\n" 140 | } 141 | }, 142 | "source": [ 143 | "For the remainder of this notebook, this will be our reference circuit for assessment. Note that the GHZ game uses 4 distinct circuits. These, however, only differ in their measurment base and order, but all have the same core - the GHZ state, which is the entanglement of 3 qubits (see part before the first barrier)." 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": null, 149 | "metadata": { 150 | "pycharm": { 151 | "name": "#%%\n" 152 | } 153 | }, 154 | "outputs": [], 155 | "source": [ 156 | "# basic circuit\n", 157 | "name = \"GHZ yyx default\" # ghz in measurement base Y,Y,X\n", 158 | "ghz = QuantumCircuit(n,n, name=name) # create circuit\n", 159 | "ghz.h(0) # h gate\n", 160 | "ghz.cx(0,1) # cnot gate\n", 161 | "ghz.cx(0,2) # cnot gate\n", 162 | "ghz.barrier()\n", 163 | "ghz.sdg([0,1]) # s dagger gate\n", 164 | "ghz.h(range(n)) # h gates\n", 165 | "ghz.barrier()\n", 166 | "ghz.measure(range(n), range(n)) # measurements\n", 167 | "ghz.draw(\"mpl\") # draw cirucit" 168 | ] 169 | }, 170 | { 171 | "cell_type": "markdown", 172 | "metadata": { 173 | "pycharm": { 174 | "name": "#%% md\n" 175 | } 176 | }, 177 | "source": [ 178 | "In order to evaluate the accuracy of the results of executing the GHZ state on different devices and with different mitigation techniques, we need a metric which we will define as state accuracy. It represents the percentage of counts resulted from the experiment that belong to one of the four target states of the GHZ game ('001', '010', '100', '111'). Reaching either of them equals winning they game, therefore our metric represents the fidelity with which we win the GHZ game on our quantum computer. We will use the state accuracy to measure the effectiveness of our different optimization approaches." 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": null, 184 | "metadata": { 185 | "pycharm": { 186 | "name": "#%%\n" 187 | } 188 | }, 189 | "outputs": [], 190 | "source": [ 191 | "expected_states = ['001', '010', '100', '111'] # target states\n", 192 | "\n", 193 | "def get_state_accuracy(counts): # funciton to calculate state accuracy\n", 194 | " expected_counts = 0\n", 195 | " for state in counts.keys():\n", 196 | " if state in expected_states:\n", 197 | " expected_counts = expected_counts + counts[state]\n", 198 | " state_accuracy = expected_counts / shots\n", 199 | " return str(state_accuracy*100)+\"%\"" 200 | ] 201 | }, 202 | { 203 | "cell_type": "markdown", 204 | "metadata": { 205 | "pycharm": { 206 | "name": "#%% md\n" 207 | } 208 | }, 209 | "source": [ 210 | "For reference, this is what the state accuracy would look like for results from a perfect backend (100%)." 211 | ] 212 | }, 213 | { 214 | "cell_type": "code", 215 | "execution_count": null, 216 | "metadata": { 217 | "pycharm": { 218 | "name": "#%%\n" 219 | } 220 | }, 221 | "outputs": [], 222 | "source": [ 223 | "backend = AerSimulator()\n", 224 | "counts = backend.run(ghz, shots=shots).result().get_counts() # execution on simulator\n", 225 | "state_accuracy = get_state_accuracy(counts)\n", 226 | "print(f\"state accuracy on the simulator: {state_accuracy}\")" 227 | ] 228 | }, 229 | { 230 | "cell_type": "markdown", 231 | "metadata": { 232 | "pycharm": { 233 | "name": "#%% md\n" 234 | } 235 | }, 236 | "source": [ 237 | "Note that the state accuracy is a custom metric designed for our needs. Depending on your research aim, other metrics such as the built-in [hellinger fidelity](https://qiskit.org/documentation/stubs/qiskit.quantum_info.hellinger_fidelity.html?highlight=hellinger_fidelity) might be a better choice to assess circuit execution on different devices." 238 | ] 239 | }, 240 | { 241 | "cell_type": "markdown", 242 | "metadata": { 243 | "pycharm": { 244 | "name": "#%% md\n" 245 | } 246 | }, 247 | "source": [ 248 | "Great, now we're all set to run our experiments!" 249 | ] 250 | }, 251 | { 252 | "cell_type": "markdown", 253 | "metadata": { 254 | "pycharm": { 255 | "name": "#%% md\n" 256 | } 257 | }, 258 | "source": [ 259 | "## 1. Comparing IBM quantum devices\n", 260 | "[[Top](#top)]" 261 | ] 262 | }, 263 | { 264 | "cell_type": "markdown", 265 | "metadata": { 266 | "pycharm": { 267 | "name": "#%% md\n" 268 | } 269 | }, 270 | "source": [ 271 | "To start with, we need to find available quantum devices that are suitable to run our experiment. However, in case IBM Quantum devices are busy with jobs from other users, the execution of the following experiments take very long. To avoid this, we will use Qiskit built-in mock implementations of them, executed locally.\n", 272 | "\n", 273 | "(If you want to try out this notebook on real systems, execute the cell above again and read the [appendix](#appendix-b) for detailed instructions.)" 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": null, 279 | "metadata": { 280 | "pycharm": { 281 | "name": "#%%\n" 282 | } 283 | }, 284 | "outputs": [], 285 | "source": [ 286 | "from qiskit_ibm_runtime.fake_provider import FakeYorktownV2, FakeMelbourneV2, FakeVigoV2, FakeOurenseV2, FakeValenciaV2, FakeLondonV2, FakeBurlingtonV2, FakeEssexV2\n", 287 | "# mock devices\n", 288 | "\n", 289 | "backends = [FakeYorktownV2(), FakeMelbourneV2(), FakeVigoV2(), FakeOurenseV2(), FakeValenciaV2(), FakeLondonV2(),\n", 290 | " FakeBurlingtonV2(), FakeEssexV2()] # no mock santiago available" 291 | ] 292 | }, 293 | { 294 | "cell_type": "markdown", 295 | "metadata": { 296 | "pycharm": { 297 | "name": "#%% md\n" 298 | } 299 | }, 300 | "source": [ 301 | "Now, let's compare the results from the execution of the GHZ circuit on these backends:" 302 | ] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": null, 307 | "metadata": { 308 | "pycharm": { 309 | "name": "#%%\n" 310 | } 311 | }, 312 | "outputs": [], 313 | "source": [ 314 | "results_backends = []\n", 315 | "\n", 316 | "for backend in backends:\n", 317 | " transpiled = transpile(ghz, backend=backend, optimization_level=0)\n", 318 | " counts = backend.run(transpiled, shots=shots).result().get_counts() # get results\n", 319 | " state_accuracy = get_state_accuracy(counts) # calculate state accuracy\n", 320 | " results_backends.append((backend.name, counts, state_accuracy)) # store results\n", 321 | " \n", 322 | "# plot results\n", 323 | "plot_histogram([counts for backend_name, counts, state_accuracy in results_backends],\n", 324 | " legend=[backend_name for backend_name, counts, state_accuracy in results_backends])" 325 | ] 326 | }, 327 | { 328 | "cell_type": "markdown", 329 | "metadata": { 330 | "pycharm": { 331 | "name": "#%% md\n" 332 | } 333 | }, 334 | "source": [ 335 | "The bars represent the relative amount (probability) to read a certain state from the execution of our circuit on the respective backend over the 8000 times the experiment was executed there. Remember that ideally, only four states should be returned by our circuit: 001, 010, 100, 100. Therefore, the best devices are those with the highest probability on these states while having the lowest on all other, undesired states." 336 | ] 337 | }, 338 | { 339 | "cell_type": "markdown", 340 | "metadata": { 341 | "pycharm": { 342 | "name": "#%% md\n" 343 | } 344 | }, 345 | "source": [ 346 | "To make it easier to interpret, we use the state accuracy as indicator of the results' quality:" 347 | ] 348 | }, 349 | { 350 | "cell_type": "code", 351 | "execution_count": null, 352 | "metadata": { 353 | "pycharm": { 354 | "name": "#%%\n" 355 | } 356 | }, 357 | "outputs": [], 358 | "source": [ 359 | "# setup ordered table\n", 360 | "\n", 361 | "results_backends_table = pd.DataFrame(columns=[\"backend\", \"state_accuracy\"]) # create table\n", 362 | "for backend_name, counts, state_accuracy in results_backends:\n", 363 | " results_backends_table.loc[len(results_backends_table)] = [backend_name, state_accuracy] # put values into table\n", 364 | "results_backends_table = results_backends_table.sort_values(by=[\"state_accuracy\"], ascending=False).reset_index().iloc[:, 1:] # sort values\n", 365 | "results_backends_table \n" 366 | ] 367 | }, 368 | { 369 | "cell_type": "markdown", 370 | "metadata": { 371 | "pycharm": { 372 | "name": "#%% md\n" 373 | } 374 | }, 375 | "source": [ 376 | "Now we know the ranking of the devices based on their fidelity. Let's pick one with a high state accuracy to test the optimization techniques in the remaining chapters. In addition, we will pick one system with a lower accuracy to compare against later." 377 | ] 378 | }, 379 | { 380 | "cell_type": "code", 381 | "execution_count": null, 382 | "metadata": { 383 | "pycharm": { 384 | "name": "#%%\n" 385 | } 386 | }, 387 | "outputs": [], 388 | "source": "# high accuracy\nbackend1 = FakeVigoV2()\n\n# lower accuracy\nbackend2 = FakeYorktownV2()" 389 | }, 390 | { 391 | "cell_type": "markdown", 392 | "metadata": { 393 | "pycharm": { 394 | "name": "#%% md\n" 395 | } 396 | }, 397 | "source": [ 398 | "## 2. Manually optimizing circuits\n", 399 | "[[Top](#top)]" 400 | ] 401 | }, 402 | { 403 | "cell_type": "markdown", 404 | "metadata": { 405 | "pycharm": { 406 | "name": "#%% md\n" 407 | } 408 | }, 409 | "source": [ 410 | "Why is it that the IBM Quantum computer show different performances? As mentioned earlier, hardware optimization is a major research field at the moment. Essentially, our Quantum computer consist of different numbers of qubits that are connected with each other and manipulated according to our circuit. However, the way in which they are connected - the so called coupling map - as well the error rates when performing gates on single qubits, multiple qubits and when reading the state of the qubits differs across our devices due to manifacturing. Finally, the qubits posess varying coherence times (T1/T2). \n", 411 | "\n", 412 | "Let's have a closer look on how the coupling map and error rates look like on our two picked devices." 413 | ] 414 | }, 415 | { 416 | "cell_type": "code", 417 | "execution_count": null, 418 | "metadata": { 419 | "pycharm": { 420 | "name": "#%%\n" 421 | }, 422 | "scrolled": false 423 | }, 424 | "outputs": [], 425 | "source": "%matplotlib inline\nfrom qiskit.visualization import plot_gate_map\nplot_gate_map(backend1)" 426 | }, 427 | { 428 | "cell_type": "markdown", 429 | "metadata": { 430 | "pycharm": { 431 | "name": "#%% md\n" 432 | } 433 | }, 434 | "source": [ 435 | "On the Configuration tab you can see the coupling map of the devices. Qubits that are connected with a line can perform a multiple qubit gate such as CNOT directly. Trying to apply CNOT on non-connected qubits would require Qiskit to internally change the circuit before execution to fit it to the coupling map of the device. \n", 436 | "\n", 437 | "On the Error Map tab you can see the quality of each qubit and each connection where a darker color indicates a lower error rate (more desirable). If you are interested in the exact error rates, you can find them on the Qubit Properties tab (U2 - single qubit error rate, readout error rate) and on the Multi-Qubit Gates tab." 438 | ] 439 | }, 440 | { 441 | "cell_type": "markdown", 442 | "metadata": { 443 | "pycharm": { 444 | "name": "#%% md\n" 445 | } 446 | }, 447 | "source": [ 448 | "**Coupling Map based optimization**" 449 | ] 450 | }, 451 | { 452 | "cell_type": "markdown", 453 | "metadata": { 454 | "pycharm": { 455 | "name": "#%% md\n" 456 | } 457 | }, 458 | "source": [ 459 | "The Qiskit transpiler prepares our code for execution on the real device. In case our circuit layout does not match the device's configuration, it will introduce swaps into our circuit so to make it applicable to the coupling map. Sometimes this can produce a worse accuracy than that from our original circuit. Therefore, let's try to create custom coupling map specific circuits of the GHZ state to avoid the transpiler interfering." 460 | ] 461 | }, 462 | { 463 | "cell_type": "markdown", 464 | "metadata": { 465 | "pycharm": { 466 | "name": "#%% md\n" 467 | } 468 | }, 469 | "source": [ 470 | "Backend 1:" 471 | ] 472 | }, 473 | { 474 | "cell_type": "code", 475 | "execution_count": null, 476 | "metadata": { 477 | "pycharm": { 478 | "name": "#%%\n" 479 | } 480 | }, 481 | "outputs": [], 482 | "source": "# backend1: create coupling map optimised circuit\n\n# map: qubit 0 -> qubit 1\n# qubit 1 -> qubit 0\n# qubit 2 -> qubit 2\n\nghz_backend1 = QuantumCircuit(5,3, name=f\"GHZ yyx {backend1.name} optimized\") # note that we use 5 qubits here to match the configuration of the device\nghz_backend1.h(1) # we map qubit 0 to qubit 1 and qubit 1 to qubit 0 ..\nghz_backend1.cx(1,0) # .. to ensure that all logical CNOT gates are performed on physical connections\nghz_backend1.cx(1,2)\nghz_backend1.barrier()\nghz_backend1.sdg([1,0]) \nghz_backend1.h(range(n))\nghz_backend1.barrier()\nghz_backend1.measure([1,0,2], range(n))\nghz_backend1.draw(\"mpl\")\n" 483 | }, 484 | { 485 | "cell_type": "markdown", 486 | "metadata": { 487 | "pycharm": { 488 | "name": "#%% md\n" 489 | } 490 | }, 491 | "source": [ 492 | "Let's compare the two circuits:" 493 | ] 494 | }, 495 | { 496 | "cell_type": "code", 497 | "execution_count": null, 498 | "metadata": { 499 | "pycharm": { 500 | "name": "#%%\n" 501 | } 502 | }, 503 | "outputs": [], 504 | "source": "# compare results between original and coupling map optimized circuit on backend1\nresults_backend1_coupling = []\n\ncircuits = [ghz, ghz_backend1]\nfor circuit in circuits:\n transpiled_circuit = transpile(circuit, backend=backend1, optimization_level=0)\n counts = backend1.run(transpiled_circuit, shots=shots).result().get_counts() # get results\n state_accuracy = get_state_accuracy(counts) # calculate state accuracy\n results_backend1_coupling.append((circuit.name, counts, state_accuracy)) # store results\n \n# print state accuracy\nprint(\"State Accuracy\")\nfor circuit_name, counts, state_accuracy in results_backend1_coupling:\n print(f\"{circuit_name}: {state_accuracy}%\")\n\n# plot results\nplot_histogram([counts for circuit_name, counts, state_accuracy in results_backend1_coupling],\n legend=[circuit_name for circuit_name, counts, state_accuracy in results_backend1_coupling])" 505 | }, 506 | { 507 | "cell_type": "markdown", 508 | "metadata": { 509 | "pycharm": { 510 | "name": "#%% md\n" 511 | } 512 | }, 513 | "source": [ 514 | "As you can see, the state accuracy improved by around 2% on our system." 515 | ] 516 | }, 517 | { 518 | "cell_type": "markdown", 519 | "metadata": { 520 | "pycharm": { 521 | "name": "#%% md\n" 522 | } 523 | }, 524 | "source": [ 525 | "**Error Map based optimization**" 526 | ] 527 | }, 528 | { 529 | "cell_type": "markdown", 530 | "metadata": { 531 | "pycharm": { 532 | "name": "#%% md\n" 533 | } 534 | }, 535 | "source": [ 536 | "As mentioned, this was one of two ways we are considering to manually optimize the circuits. The second builds off of the results from the coupling map based optimization and additionally takes into account the error rates. For this, we have another look on the error map and pick qubits that have the least single qubit gate, multiple qubit gate and readout error rate (see colors for intuition). Sometimes you need to trade off between these as for instance some qubits have excellent read out error rates while not as good single qubit gate error rates. Though, as this approach is manual, you don not need to try out each possible configuration to find the best one." 537 | ] 538 | }, 539 | { 540 | "cell_type": "markdown", 541 | "metadata": { 542 | "pycharm": { 543 | "name": "#%% md\n" 544 | } 545 | }, 546 | "source": [ 547 | "Backend 1:" 548 | ] 549 | }, 550 | { 551 | "cell_type": "code", 552 | "execution_count": null, 553 | "metadata": { 554 | "pycharm": { 555 | "name": "#%%\n" 556 | } 557 | }, 558 | "outputs": [], 559 | "source": "# backend1: create coupling map + error rate optimised circuit\n\n# map: qubit 0 -> qubit 3\n# qubit 1 -> qubit 1\n# qubit 2 -> qubit 4\n\nghz_backend1_error = QuantumCircuit(5,3, name=f\"GHZ yyx {backend1.name} error rate optimised\") # note that we use 5 qubits here to match the configuration of the device\nghz_backend1_error.h(3) # qubit with best adjacent multi-qubit gates error rates (cnot)\nghz_backend1_error.cx(3,1)\nghz_backend1_error.cx(3,4) \nghz_backend1_error.barrier()\nghz_backend1_error.sdg([3,1]) # avoid 4 as it has a worse single qubit error rate\nghz_backend1_error.h([3,1,4]) # best readout error rates\nghz_backend1_error.barrier()\nghz_backend1_error.measure([3,1,4], range(n)) # readout still in same order\nghz_backend1_error.draw(\"mpl\")\n" 560 | }, 561 | { 562 | "cell_type": "code", 563 | "execution_count": null, 564 | "metadata": { 565 | "pycharm": { 566 | "name": "#%%\n" 567 | } 568 | }, 569 | "outputs": [], 570 | "source": [ 571 | "# compare results between original, coupling map, and error rates optimized circuit on backend1\n", 572 | "results_backend1_error = []\n", 573 | "\n", 574 | "circuits = [ghz_backend1_error]\n", 575 | "for circuit in circuits:\n", 576 | " transpiled_circuit = transpile(circuit, backend=backend1, optimization_level=0)\n", 577 | " counts = backend1.run(transpiled_circuit, shots=shots).result().get_counts() # get results\n", 578 | " state_accuracy = get_state_accuracy(counts) # calculate state accuracy\n", 579 | " results_backend1_error.append((circuit.name, counts, state_accuracy)) # store results\n", 580 | " \n", 581 | "# plot combined results\n", 582 | "plot_histogram([counts for circuit_name, counts, state_accuracy in results_backend1_coupling + results_backend1_error],\n", 583 | " legend=[circuit_name for circuit_name, counts, state_accuracy in results_backend1_coupling + results_backend1_error])" 584 | ] 585 | }, 586 | { 587 | "cell_type": "markdown", 588 | "metadata": { 589 | "pycharm": { 590 | "name": "#%% md\n" 591 | } 592 | }, 593 | "source": [ 594 | "As can be seen, the results from the coupling map and error rate optimised circuit are around the same as those from the circuit only optimised for its coupling map. This is because while the new circuit uses qubits with improved CNOT error rates, the old one natively had good readout error rates so that effects of swapping qubit mappings do not produce a significantly better result. However, there might be still a better circuit layout than the one found here." 595 | ] 596 | }, 597 | { 598 | "cell_type": "markdown", 599 | "metadata": { 600 | "pycharm": { 601 | "name": "#%% md\n" 602 | } 603 | }, 604 | "source": [ 605 | "## 3. Qiskit Transpiler Optimization\n", 606 | "[[Top](#top)]" 607 | ] 608 | }, 609 | { 610 | "cell_type": "markdown", 611 | "metadata": { 612 | "pycharm": { 613 | "name": "#%% md\n" 614 | } 615 | }, 616 | "source": [ 617 | "Qiskit comes with a built-in transpiler to translate logical circuits into circuits that are executable on the target backend. In addition, the transpile function (and execute function calling it) has four different levels of optimization available that can be set as parameter. See an extract of the [documentation](https://qiskit.org/documentation/apidoc/transpiler_preset.html) here to understand their differences:" 618 | ] 619 | }, 620 | { 621 | "cell_type": "markdown", 622 | "metadata": { 623 | "pycharm": { 624 | "name": "#%% md\n" 625 | } 626 | }, 627 | "source": [ 628 | "Optimization Levels\n", 629 | "\n", 630 | "* Level 0: no explicit optimization other than **mapping to backend**.\n", 631 | "\n", 632 | "* level 1: light optimization by simple **adjacent gate collapsing**.\n", 633 | "\n", 634 | "* level 2: medium optimization by **initial layout selection** and **gate cancellation** using commutativity rules.\n", 635 | "\n", 636 | "* level 3: heavy optimization by **noise adaptive qubit mapping** and gate cancellation using commutativity rules and **unitary synthesis**.m" 637 | ] 638 | }, 639 | { 640 | "cell_type": "markdown", 641 | "metadata": { 642 | "pycharm": { 643 | "name": "#%% md\n" 644 | } 645 | }, 646 | "source": [ 647 | "Next to other actions taken such as combining gates and mapping to the device's coupling map (which takes place necessarily on every level), note that only optimization level 3 applies noise adaptive mapping, which we attempted to do manually in the previous chapter." 648 | ] 649 | }, 650 | { 651 | "cell_type": "markdown", 652 | "metadata": { 653 | "pycharm": { 654 | "name": "#%% md\n" 655 | } 656 | }, 657 | "source": [ 658 | "To separately view the effects from each chapter, we will now again use the basic GHZ YYX circuit and compare the different transpiler optimization levels on backend 1." 659 | ] 660 | }, 661 | { 662 | "cell_type": "code", 663 | "execution_count": null, 664 | "metadata": { 665 | "pycharm": { 666 | "name": "#%%\n" 667 | }, 668 | "scrolled": false 669 | }, 670 | "outputs": [], 671 | "source": "# compare results from different optimization levels on backend 1\nresults_backend1_transpiler = []\n\noptimization_levels = [0,1,2,3]\n#print(ghz)\nfor optim_level in optimization_levels:\n transpiled_circuit = transpile(ghz, backend=backend1, optimization_level=optim_level)\n #print(transpiled_circuit)\n counts = backend1.run(transpiled_circuit, shots=shots).result().get_counts() # get results\n state_accuracy = get_state_accuracy(counts) # calculate state accuracy\n results_backend1_transpiler.append((f\"optim_level={optim_level}\", counts, state_accuracy)) # store results\n \n# print state accuracy\nprint(\"State Accuracy\")\nfor circuit_name, counts, state_accuracy in results_backend1_transpiler:\n print(f\"{circuit_name}: {state_accuracy}%\")\n\n# plot results\nplot_histogram([counts for circuit_name, counts, state_accuracy in results_backend1_transpiler],\n legend=[circuit_name for circuit_name, counts, state_accuracy in results_backend1_transpiler])" 672 | }, 673 | { 674 | "cell_type": "markdown", 675 | "metadata": { 676 | "pycharm": { 677 | "name": "#%% md\n" 678 | } 679 | }, 680 | "source": [ 681 | "Did you notice? Counterintuively, there was only a visible improvement from optimization level 0 to 1, and from 1 to 2, while level 2 and 3 produce similar results on the backend. But why is there no improvement on each level? To understand this phenomenon better, let's see what happens under the hood by inspecting the transpiled circuits that the transpiler produces to execute them on our backend:" 682 | ] 683 | }, 684 | { 685 | "cell_type": "code", 686 | "execution_count": null, 687 | "metadata": { 688 | "pycharm": { 689 | "name": "#%%\n" 690 | }, 691 | "scrolled": false 692 | }, 693 | "outputs": [], 694 | "source": [ 695 | "# compare transpiled circuits for different optimization levels on backend 1\n", 696 | "\n", 697 | "optimization_levels = [0,1,2,3]\n", 698 | "print(\"original circuit\")\n", 699 | "print(ghz)\n", 700 | "for optim_level in optimization_levels:\n", 701 | " print(f\"optimization level: {optim_level}\")\n", 702 | " transpiled_circuit = transpile(ghz, backend=backend1, optimization_level=optim_level)\n", 703 | " print(transpiled_circuit)\n" 704 | ] 705 | }, 706 | { 707 | "cell_type": "markdown", 708 | "metadata": { 709 | "pycharm": { 710 | "name": "#%% md\n" 711 | } 712 | }, 713 | "source": [ 714 | "The circuits of level 2 and 3 are identical! Also circuits 0 and 1 behave similarly, even though the compiler picks different qubits to map our circuit. This implies that the circuit with optimization level 2 is already the best the transpiler can do for us. The small deviation between optimization level 2 and 3 only comes due to probabilistic quantum readout randomness." 715 | ] 716 | }, 717 | { 718 | "cell_type": "markdown", 719 | "metadata": { 720 | "pycharm": { 721 | "name": "#%% md\n" 722 | } 723 | }, 724 | "source": [ 725 | "Remember that the compiler is supposed to follow the idea from our manual optimization in a more sophisticated manner. Therefore let's compare its output again with what we produced in chapter 2:" 726 | ] 727 | }, 728 | { 729 | "cell_type": "code", 730 | "execution_count": null, 731 | "metadata": { 732 | "pycharm": { 733 | "name": "#%%\n" 734 | }, 735 | "scrolled": false 736 | }, 737 | "outputs": [], 738 | "source": [ 739 | "print(\"manual optimization\")\n", 740 | "print(ghz_backend1_error)\n", 741 | "\n", 742 | "print(\"State Accuracy\")\n", 743 | "print(\"manual optimization: \"+str(results_backend1_error[0][2]))\n", 744 | "print(\"transpiler optimization: \"+str(results_backend1_transpiler[3][2]))" 745 | ] 746 | }, 747 | { 748 | "cell_type": "markdown", 749 | "metadata": { 750 | "pycharm": { 751 | "name": "#%% md\n" 752 | } 753 | }, 754 | "source": [ 755 | "As can be seen, the structure of the circuit remains the same even on level 3 of the transpiler optimized circuit, matching our circuit from chapter 2. However, they differ in the qubits that were selected for mapping the logical circuit onto the real physical hardware. Taking this into account, the transpiler apparently has a better intuition on how to balance the importances of different kinds of errors." 756 | ] 757 | }, 758 | { 759 | "cell_type": "markdown", 760 | "metadata": { 761 | "pycharm": { 762 | "name": "#%% md\n" 763 | } 764 | }, 765 | "source": [ 766 | "## 4. Measurement Error Mitigation\n", 767 | "[[Top](#top)]" 768 | ] 769 | }, 770 | { 771 | "cell_type": "markdown", 772 | "metadata": { 773 | "pycharm": { 774 | "name": "#%% md\n" 775 | } 776 | }, 777 | "source": [ 778 | "So far, we achieved some improvements of the state accuracy with manual optimization and to a small extent with transpiler optimization. While these are rather simple steps to take, there is an entire research field dealing with how to mitigate quantum computer hardware imperfection with software driven techniques, one of which we will examine now. \n", 779 | "\n", 780 | "\n", 781 | "As discussed earlier, there are various types of errors that can perpetrate noisy results when executing a quantum circuit. A significant one is that what happens when the quantum states are read from qubits forcing them to collapse into classical states, i.e. our experiment should return one of our four targeted classical states (001, 010, 100, or 111). However, we've seen that in our results a lot more states than these incorrectly appear. \n", 782 | "\n", 783 | "These returned result represent the real results plus the readout error. Hence, knowing the devices' readout error behavior, we can estimate the real results from our obtained results through matrix multiplication, which is called Measurement Error Mitigation. For this, each basis state (one of the 2^n combinations of 0 and 1 per each of the n qubits, e.g. 00, 01, 10, 11 for 2 qubits) is measured on the device so to derive its measurment errors, allowing to restore our real counts.\n", 784 | "\n", 785 | "For a detailed explenation of how Measurement Error Mitigation works have a look at the [Qiskit Textbook](https://qiskit.org/textbook/ch-quantum-hardware/measurement-error-mitigation.html) or the well explained Qiskit [youtube video](https://www.youtube.com/watch?v=yuDxHJOKsVA) on MEM." 786 | ] 787 | }, 788 | { 789 | "cell_type": "code", 790 | "execution_count": null, 791 | "metadata": { 792 | "pycharm": { 793 | "name": "#%%\n" 794 | } 795 | }, 796 | "outputs": [], 797 | "source": [ 798 | "# measurement error mitigation on backend 1\n", 799 | "# Note: In Qiskit 2.x, measurement error mitigation is handled via Qiskit Runtime resilience levels\n", 800 | "# For fake backends, we demonstrate the noisy execution\n", 801 | "\n", 802 | "results_backend1_mem = []\n", 803 | "\n", 804 | "# normal execution\n", 805 | "transpiled = transpile(ghz, backend=backend1, optimization_level=0)\n", 806 | "noisy_counts = backend1.run(transpiled, shots=shots).result().get_counts() # get results\n", 807 | "noisy_state_accuracy = get_state_accuracy(noisy_counts) # calculate state accuracy from original results\n", 808 | "results_backend1_mem.append((\"noisy\", noisy_counts, noisy_state_accuracy))\n", 809 | "\n", 810 | "# Note: For real devices with Qiskit Runtime, use resilience_level parameter\n", 811 | "# Example: sampler = Sampler(backend, options={\"resilience_level\": 1})\n", 812 | "\n", 813 | "# plot results\n", 814 | "plot_histogram([counts for name, counts, accuracy in results_backend1_mem],\n", 815 | " legend=[name for name, counts, accuracy in results_backend1_mem])" 816 | ] 817 | }, 818 | { 819 | "cell_type": "markdown", 820 | "metadata": { 821 | "pycharm": { 822 | "name": "#%% md\n" 823 | } 824 | }, 825 | "source": [ 826 | "As can be seen, there is a visible improvement on the system with a high accuracy as result from the measurement error mitigated results." 827 | ] 828 | }, 829 | { 830 | "cell_type": "markdown", 831 | "metadata": { 832 | "pycharm": { 833 | "name": "#%% md\n" 834 | } 835 | }, 836 | "source": [ 837 | "While this is what we looked for, let's compare the results to the mitigated results from another, natively less accurate system in order to understand the capabilities of measurement error mitigation." 838 | ] 839 | }, 840 | { 841 | "cell_type": "code", 842 | "execution_count": null, 843 | "metadata": { 844 | "pycharm": { 845 | "name": "#%%\n" 846 | } 847 | }, 848 | "outputs": [], 849 | "source": "plot_gate_map(backend2)" 850 | }, 851 | { 852 | "cell_type": "code", 853 | "execution_count": null, 854 | "metadata": { 855 | "pycharm": { 856 | "name": "#%%\n" 857 | } 858 | }, 859 | "outputs": [], 860 | "source": [ 861 | "# measurement error mitigation on backend 2\n", 862 | "\n", 863 | "results_backend2_mem = []\n", 864 | "\n", 865 | "# normal execution\n", 866 | "transpiled = transpile(ghz, backend=backend2, optimization_level=0)\n", 867 | "noisy_counts = backend2.run(transpiled, shots=shots).result().get_counts() # get results\n", 868 | "noisy_state_accuracy = get_state_accuracy(noisy_counts) # calculate state accuracy from original results\n", 869 | "results_backend2_mem.append((\"noisy\", noisy_counts, noisy_state_accuracy))\n", 870 | "\n", 871 | "# plot results\n", 872 | "plot_histogram([counts for name, counts, accuracy in results_backend2_mem],\n", 873 | " legend=[name for name, counts, accuracy in results_backend2_mem])" 874 | ] 875 | }, 876 | { 877 | "cell_type": "markdown", 878 | "metadata": { 879 | "pycharm": { 880 | "name": "#%% md\n" 881 | } 882 | }, 883 | "source": [ 884 | "As can be seen, this is a truly remarkable improvement, demonstrating the power of measurement error mitigation. This implies that a large part of the erroneous results on backend 2 is due to measurement errors. In addition, it is worth noting that the effect of the measurement error mitigation fluctuates over different applications on the same backend, which in turn depends on the device's quality in the first place. Try it by executing the above cell multiple times. " 885 | ] 886 | }, 887 | { 888 | "cell_type": "markdown", 889 | "metadata": { 890 | "pycharm": { 891 | "name": "#%% md\n" 892 | } 893 | }, 894 | "source": [ 895 | "## 5. Combining the optimization techniques\n", 896 | "[[Top](#top)]" 897 | ] 898 | }, 899 | { 900 | "cell_type": "markdown", 901 | "metadata": { 902 | "pycharm": { 903 | "name": "#%% md\n" 904 | } 905 | }, 906 | "source": [ 907 | "We've now successfully gone through a variety of optimization techniques that differ in approach, complexity and results.\n", 908 | "However, the results for our system are still under 100%, which is what we aim for. Therefore, let's attempt to get the maximum fidelity through combining all of the measures discussed." 909 | ] 910 | }, 911 | { 912 | "cell_type": "code", 913 | "execution_count": null, 914 | "metadata": { 915 | "pycharm": { 916 | "name": "#%%\n" 917 | } 918 | }, 919 | "outputs": [], 920 | "source": "# backend1\n\nresults_backend1_all = []\n\n# basic circuit\ntranspiled = transpile(ghz, backend=backend1, optimization_level=0)\nnoisy_counts = backend1.run(transpiled, shots=shots).result().get_counts() # get results\nnoisy_state_accuracy = get_state_accuracy(noisy_counts) # calculate state accuracy from original results\nresults_backend1_all.append((\"noisy\", noisy_counts, noisy_state_accuracy))\n\n# manually optimized circuit\ntranspiled = transpile(ghz_backend1_error, backend=backend1, optimization_level=0)\nmanual_counts = backend1.run(transpiled, shots=shots).result().get_counts() # get results\nmanual_state_accuracy = get_state_accuracy(manual_counts)\nresults_backend1_all.append((\"manual\", manual_counts, manual_state_accuracy))\n\n# transpiler optimized circuit\ntranspiled = transpile(ghz, backend=backend1, optimization_level=3)\ntranspiler_counts = backend1.run(transpiled, shots=shots).result().get_counts() # get results\ntranspiler_state_accuracy = get_state_accuracy(transpiler_counts)\nresults_backend1_all.append((\"transpiler\", transpiler_counts, transpiler_state_accuracy))\n\n# print state accuracy\nprint(\"State Accuracy\")\nfor name, counts, accuracy in results_backend1_all:\n print(f\"{name}: {accuracy}%\")\n\n# plot results\nplot_histogram([counts for name, counts, accuracy in results_backend1_all],\n legend=[name for name, counts, accuracy in results_backend1_all])" 921 | }, 922 | { 923 | "cell_type": "markdown", 924 | "metadata": { 925 | "pycharm": { 926 | "name": "#%% md\n" 927 | } 928 | }, 929 | "source": [ 930 | "If we wish to understand the effects better, we can inspect the transpiled circuit for each technique in order:" 931 | ] 932 | }, 933 | { 934 | "cell_type": "code", 935 | "execution_count": null, 936 | "metadata": { 937 | "pycharm": { 938 | "name": "#%%\n" 939 | } 940 | }, 941 | "outputs": [], 942 | "source": "%matplotlib inline\n\n# inspect circuits on backend1\nprint(\"*** \",backend1.name,\" ***\")\nprint(\"basic circuit\")\nprint(transpile(ghz, backend=backend1, optimization_level=0))\nprint(\"manually optimized circuit (coupling map and error map)\")\nprint(transpile(ghz_backend1_error, backend=backend1, optimization_level=0))\nprint(\"transpiler optimized circuit\")\nprint(transpile(ghz, backend=backend1, optimization_level=3))\n# (measurement error mitigation doesn't transform the circuit)" 943 | }, 944 | { 945 | "cell_type": "markdown", 946 | "metadata": { 947 | "pycharm": { 948 | "name": "#%% md\n" 949 | } 950 | }, 951 | "source": [ 952 | "Great, we can see that the combination of methods really produces a tremendous value for the state accuracy of backend 1, close to the ideal value of 100%." 953 | ] 954 | }, 955 | { 956 | "cell_type": "markdown", 957 | "metadata": { 958 | "pycharm": { 959 | "name": "#%% md\n" 960 | } 961 | }, 962 | "source": [ 963 | "**Applying the combined techniques on another backend**\n", 964 | "\n", 965 | "Now it's your turn! Can you combine all the methods for backend 2? To get started, we provided a possible configuration of a manually optimized circuit for backend 2:" 966 | ] 967 | }, 968 | { 969 | "cell_type": "code", 970 | "execution_count": null, 971 | "metadata": { 972 | "pycharm": { 973 | "name": "#%%\n" 974 | } 975 | }, 976 | "outputs": [], 977 | "source": "# backend 2: coupling map + error rate optimised circuit\n\n# map: qubit 0 -> qubit 1\n# qubit 1 -> qubit 2\n# qubit 2 -> qubit 0\n\nghz_backend2_error = QuantumCircuit(5,3, name=f\"GHZ yyx {backend2.name} error rate optimised\") # note that we use 5 qubits here to match the configuration of the device\nghz_backend2_error.h(1) \nghz_backend2_error.cx(1,2)\nghz_backend2_error.cx(1,0) \nghz_backend2_error.barrier()\nghz_backend2_error.sdg([1,2]) # better single qubit error rate\nghz_backend2_error.h([1,2,0])\nghz_backend2_error.barrier()\nghz_backend2_error.measure([1,2,0], range(n)) # readout still in same order\nghz_backend2_error.draw(\"mpl\")\n" 978 | }, 979 | { 980 | "cell_type": "markdown", 981 | "metadata": { 982 | "pycharm": { 983 | "name": "#%% md\n" 984 | } 985 | }, 986 | "source": [ 987 | "Now, your task is to fill in the following cell to execute each optimization step in order just as we did with backend 1.\n", 988 | "\n", 989 | "\n", 990 | "*(Hint: You can copy the code from the previous cell and change the variables for those relevant for backend 2. The solution can be found in the [appendix](#appendix-a) at the end of this notebook.)*" 991 | ] 992 | }, 993 | { 994 | "cell_type": "code", 995 | "execution_count": null, 996 | "metadata": { 997 | "pycharm": { 998 | "name": "#%%\n" 999 | }, 1000 | "scrolled": true 1001 | }, 1002 | "outputs": [], 1003 | "source": "### YOUR WORK ###\n\n# backend 2\n\nresults_backend2_all = []\nnoisy_counts = {}\nmanual_counts = {}\ntranspiler_counts = {}\nmitigated_counts = {}\n\n# basic circuit\n## YOUR CODE HERE ##\nresults_backend2_all.append((\"noisy\", noisy_counts, noisy_state_accuracy))\n\n# manually optimized circuit\n## YOUR CODE HERE ##\nresults_backend2_all.append((\"manual\", manual_counts, manual_state_accuracy))\n\n# transpiler optimization (level 3 overrides manually optimized circuit)\n## YOUR CODE HERE ##\nresults_backend2_all.append((\"transpiler\", transpiler_counts, transpiler_state_accuracy))\n\n# transpiler optimization + measurement error mitigation\n## YOUR CODE HERE ##\nresults_backend2_all.append((\"transpiler - mitigated\", mitigated_counts, mitigated_state_accuracy))\n\n# compare state accuracies\nprint(\"State Accuracy \\n\"+\n \"\\n\".join([str(label)+': '+str(state_accuracy) for label, counts, state_accuracy in results_backend2_all]))\n\n# plot results\nplot_histogram([counts for label, counts, state_accuracy in results_backend2_all],\n legend=[label for label, counts, state_accuracy in results_backend2_all],\n title=backend2.name,\n bar_labels=False)" 1004 | }, 1005 | { 1006 | "cell_type": "markdown", 1007 | "metadata": { 1008 | "pycharm": { 1009 | "name": "#%% md\n" 1010 | } 1011 | }, 1012 | "source": [ 1013 | "If you get a state accuracy of around 95% you did a good job!\n", 1014 | "\n", 1015 | "Finally, we can inspect the transpiled circuits to understand the results better:" 1016 | ] 1017 | }, 1018 | { 1019 | "cell_type": "code", 1020 | "execution_count": null, 1021 | "metadata": { 1022 | "pycharm": { 1023 | "name": "#%%\n" 1024 | }, 1025 | "scrolled": false 1026 | }, 1027 | "outputs": [], 1028 | "source": "# inspect circuits on backend2\n\nprint(\"*** \",backend2.name,\" ***\")\nprint(\"basic circuit\")\nprint(transpile(ghz, backend=backend2, optimization_level=0))\nprint(\"manually optimized circuit (coupling map and error map)\")\nprint(transpile(ghz_backend2_error, backend=backend2, optimization_level=0))\nprint(\"transpiler optimized circuit\")\nprint(transpile(ghz, backend=backend2, optimization_level=3))\n# (measurement error mitigation doesn't transform the circuit)" 1029 | }, 1030 | { 1031 | "cell_type": "markdown", 1032 | "metadata": { 1033 | "pycharm": { 1034 | "name": "#%% md\n" 1035 | } 1036 | }, 1037 | "source": [ 1038 | "\n", 1039 | "\n", 1040 | "---\n", 1041 | "\n", 1042 | "Congratulations! In this tutorial, we found ways to greatly increase the probabbility with which we can win the Quantum GHZ game on a quantum computer. Please note that there is a large field of research revolving around optimising quantum hardware and software - and we only covered a few simple ones of them. Therefore feel free to dig deeper into the topic or run your own circuits on [IBM Quantum Experience](quantum-computing.ibm.com).\n", 1043 | "\n", 1044 | "Thank you!" 1045 | ] 1046 | }, 1047 | { 1048 | "cell_type": "markdown", 1049 | "metadata": { 1050 | "pycharm": { 1051 | "name": "#%% md\n" 1052 | } 1053 | }, 1054 | "source": [ 1055 | "*Lennart Schulze, IBM Germany, lennart.schulze@ibm.com*\n", 1056 | "\n", 1057 | "*Dr. Jan-Rainer Lahmann, IBM Germany, Jan.Lahmann@de.ibm.com*" 1058 | ] 1059 | }, 1060 | { 1061 | "cell_type": "markdown", 1062 | "metadata": { 1063 | "pycharm": { 1064 | "name": "#%% md\n" 1065 | } 1066 | }, 1067 | "source": [ 1068 | "**Important:** If running this notebook on a shared system (e.g. mybinder.org), remember to log off from your IBM Q account in case you used it for execution on real devices by running the following cell:" 1069 | ] 1070 | }, 1071 | { 1072 | "cell_type": "code", 1073 | "execution_count": null, 1074 | "metadata": { 1075 | "pycharm": { 1076 | "name": "#%%\n" 1077 | } 1078 | }, 1079 | "outputs": [], 1080 | "source": [ 1081 | "# IBMQ account management removed - use QiskitRuntimeService instead\n", 1082 | "# See: https://docs.quantum.ibm.com/migration-guides/qiskit-runtime\n", 1083 | "pass" 1084 | ] 1085 | }, 1086 | { 1087 | "cell_type": "markdown", 1088 | "metadata": { 1089 | "pycharm": { 1090 | "name": "#%% md\n" 1091 | } 1092 | }, 1093 | "source": [ 1094 | "---\n", 1095 | "## .\n", 1096 | "## .\n", 1097 | "## .\n", 1098 | "## .\n", 1099 | "\n", 1100 | "\n", 1101 | "## *Appendix*\n", 1102 | "[[Top](#top)]\n" 1103 | ] 1104 | }, 1105 | { 1106 | "cell_type": "markdown", 1107 | "metadata": { 1108 | "pycharm": { 1109 | "name": "#%% md\n" 1110 | } 1111 | }, 1112 | "source": [ 1113 | "### A: Solutions (Chapter 5)" 1114 | ] 1115 | }, 1116 | { 1117 | "cell_type": "code", 1118 | "execution_count": null, 1119 | "metadata": { 1120 | "pycharm": { 1121 | "name": "#%%\n" 1122 | }, 1123 | "scrolled": false 1124 | }, 1125 | "outputs": [], 1126 | "source": [ 1127 | "#### SOLUTION TO CHAPTER 5 #####\n", 1128 | " \n", 1129 | "results_backend2_all = []\n", 1130 | "\n", 1131 | "# basic circuit\n", 1132 | "transpiled = transpile(ghz, backend=backend2, optimization_level=0)\n", 1133 | "noisy_counts = backend2.run(transpiled, shots=shots).result().get_counts() # get results\n", 1134 | "noisy_state_accuracy = get_state_accuracy(noisy_counts)\n", 1135 | "results_backend2_all.append((\"noisy\", noisy_counts, noisy_state_accuracy))\n", 1136 | "\n", 1137 | "# manually optimized circuit (coupling map and error map)\n", 1138 | "transpiled = transpile(ghz_backend2_error, backend=backend2, optimization_level=0)\n", 1139 | "manual_counts = backend2.run(transpiled, shots=shots).result().get_counts()\n", 1140 | "manual_state_accuracy = get_state_accuracy(manual_counts)\n", 1141 | "results_backend2_all.append((\"manual\", manual_counts, manual_state_accuracy))\n", 1142 | "\n", 1143 | "# transpiler optimized circuit\n", 1144 | "transpiled = transpile(ghz, backend=backend2, optimization_level=3)\n", 1145 | "transpiler_counts = backend2.run(transpiled, shots=shots).result().get_counts()\n", 1146 | "transpiler_state_accuracy = get_state_accuracy(transpiler_counts)\n", 1147 | "results_backend2_all.append((\"transpiler\", transpiler_counts, transpiler_state_accuracy))\n", 1148 | "\n", 1149 | "# plot results\n", 1150 | "plot_histogram([counts for name, counts, accuracy in results_backend2_all],\n", 1151 | " legend=[name for name, counts, accuracy in results_backend2_all])" 1152 | ] 1153 | }, 1154 | { 1155 | "cell_type": "markdown", 1156 | "metadata": { 1157 | "pycharm": { 1158 | "name": "#%% md\n" 1159 | } 1160 | }, 1161 | "source": [ 1162 | "---" 1163 | ] 1164 | }, 1165 | { 1166 | "cell_type": "markdown", 1167 | "metadata": { 1168 | "pycharm": { 1169 | "name": "#%% md\n" 1170 | } 1171 | }, 1172 | "source": [ 1173 | "### B: Running this notebook on real quantum computers" 1174 | ] 1175 | }, 1176 | { 1177 | "cell_type": "markdown", 1178 | "metadata": { 1179 | "pycharm": { 1180 | "name": "#%% md\n" 1181 | } 1182 | }, 1183 | "source": [ 1184 | "You can run the entire on real quantum hardware provided and made accesible by IBM on the [IBM Quantum Experience](https://quantum-computing.ibm.com).\n", 1185 | "You should take into consideration that this will take longer, as each job submitted for execution to either of the quantum computers is queued based on a fair share algorithm. Therefore, you will most likely need to wait until other jobs queued before yours are finished. You can see the status of your jobs under \"Pending Results\" on the [Results page](https://quantum-computing.ibm.com/results).\n", 1186 | "\n", 1187 | "As the first chapter executes circuits on all suitable devices, it will take especially long. For this reason we'll provide guidance for that chapter seperately." 1188 | ] 1189 | }, 1190 | { 1191 | "cell_type": "markdown", 1192 | "metadata": { 1193 | "pycharm": { 1194 | "name": "#%% md\n" 1195 | } 1196 | }, 1197 | "source": [ 1198 | "**Connect IBM Quantum Experience Account**" 1199 | ] 1200 | }, 1201 | { 1202 | "cell_type": "markdown", 1203 | "metadata": { 1204 | "pycharm": { 1205 | "name": "#%% md\n" 1206 | } 1207 | }, 1208 | "source": [ 1209 | "We need to connect to our IBM Quantum Experience account in order to be able to execute the results on real hardware.\n", 1210 | "\n", 1211 | "If you haven't linked your IBM Quantum Experience Account yet, follow this [guide](https://qiskit.org/documentation/install.html#install-access-ibm-q-devices-label) and paste your credentials in the following cell. Remember to remove your credentials at the [end of the tutorial](#end)." 1212 | ] 1213 | }, 1214 | { 1215 | "cell_type": "code", 1216 | "execution_count": null, 1217 | "metadata": { 1218 | "pycharm": { 1219 | "name": "#%%\n" 1220 | } 1221 | }, 1222 | "outputs": [], 1223 | "source": [ 1224 | "# For real device access, use QiskitRuntimeService:\n", 1225 | "# from qiskit_ibm_runtime import QiskitRuntimeService\n", 1226 | "# service = QiskitRuntimeService(channel=\"ibm_quantum\", token=\"YOUR_TOKEN\")\n", 1227 | "# service.save_account(channel=\"ibm_quantum\", token=\"YOUR_TOKEN\")" 1228 | ] 1229 | }, 1230 | { 1231 | "cell_type": "markdown", 1232 | "metadata": { 1233 | "pycharm": { 1234 | "name": "#%% md\n" 1235 | } 1236 | }, 1237 | "source": [ 1238 | "Having stored your account credentials, run the following to activate your account. " 1239 | ] 1240 | }, 1241 | { 1242 | "cell_type": "code", 1243 | "execution_count": null, 1244 | "metadata": { 1245 | "pycharm": { 1246 | "name": "#%%\n" 1247 | } 1248 | }, 1249 | "outputs": [], 1250 | "source": [ 1251 | "# Load saved account and get backend:\n", 1252 | "# from qiskit_ibm_runtime import QiskitRuntimeService\n", 1253 | "# service = QiskitRuntimeService()\n", 1254 | "# backend = service.backend(\"ibm_brisbane\") # or other available backend" 1255 | ] 1256 | }, 1257 | { 1258 | "cell_type": "markdown", 1259 | "metadata": { 1260 | "pycharm": { 1261 | "name": "#%% md\n" 1262 | } 1263 | }, 1264 | "source": [ 1265 | "**Using real Quantum Computers in chapter 1**" 1266 | ] 1267 | }, 1268 | { 1269 | "cell_type": "markdown", 1270 | "metadata": { 1271 | "pycharm": { 1272 | "name": "#%% md\n" 1273 | } 1274 | }, 1275 | "source": [ 1276 | "We simply need to change the \"backend\" variable. Just execute the following cell or use it to replace the corresponding cell above, then execute the cell in chapter 1 in which the circuits are executed and review the results." 1277 | ] 1278 | }, 1279 | { 1280 | "cell_type": "code", 1281 | "execution_count": null, 1282 | "metadata": { 1283 | "pycharm": { 1284 | "name": "#%%\n" 1285 | } 1286 | }, 1287 | "outputs": [], 1288 | "source": "backends = provider.backends(filters=lambda x: # get available quantum computer from IBM Q provider where\n x.configuration().n_qubits >= n and # number of qubits high enough\n not x.configuration().simulator and # only real devices (no simulator)\n x.status().operational==True) # only devices that work\nprint(f\"IBM Q backends: {[str(backend.name) for backend in backends]}\")" 1289 | }, 1290 | { 1291 | "cell_type": "markdown", 1292 | "metadata": { 1293 | "pycharm": { 1294 | "name": "#%% md\n" 1295 | } 1296 | }, 1297 | "source": [ 1298 | "**Using real Quantum Computers in chapters 2-5**" 1299 | ] 1300 | }, 1301 | { 1302 | "cell_type": "markdown", 1303 | "metadata": { 1304 | "pycharm": { 1305 | "name": "#%% md\n" 1306 | } 1307 | }, 1308 | "source": [ 1309 | "First, we need to change our backend variables for the real devices we would like to examine. You can pick the real devices corresponding to the mock ones we used above or choose your own based on the selection returned by the previous cell. (Again execute the cell here or replace the code in the corresponding cell above.)" 1310 | ] 1311 | }, 1312 | { 1313 | "cell_type": "code", 1314 | "execution_count": null, 1315 | "metadata": { 1316 | "pycharm": { 1317 | "name": "#%%\n" 1318 | } 1319 | }, 1320 | "outputs": [], 1321 | "source": [ 1322 | "# higher accuracy\n", 1323 | "backend1 = provider.get_backend(\"ibmq_manila\")\n", 1324 | "\n", 1325 | "# lower accuracy\n", 1326 | "backend2 = provider.get_backend(\"ibmq_belem\")" 1327 | ] 1328 | }, 1329 | { 1330 | "cell_type": "markdown", 1331 | "metadata": { 1332 | "pycharm": { 1333 | "name": "#%% md\n" 1334 | } 1335 | }, 1336 | "source": [ 1337 | "Almost done! However, as the name indicates, chapter 2 uses circuits that are manually optimized to specifically fit the default devices used so far. In order to get reasonable results in its sections, you now need to review the coupling and error map of the devices you picked by executing the first two cells in chapter 2. \n", 1338 | "\n", 1339 | "Based on this, your job is to create a new circuit for each (or decide to leave the default one if it is already fitting) that fits its coupling map, i.e. so that all qubits that interact with a cnot gate in the circuit are physically connected as indicated in the coupling map. Then, adapt the circuit again so to pick qubits with the lowest error rates. This doesn't need to be accurate, as it is hard to weigh the effects of the different kinds of errors present. A good indicator if you did a good job is that the state accuracy of the result is not significantly lower than that of the default circuit or that of the coupling map sensitive circuit without error optimization, respectively. Once you're done, execute the remaining cells in chapters 2-5." 1340 | ] 1341 | }, 1342 | { 1343 | "cell_type": "code", 1344 | "execution_count": null, 1345 | "metadata": { 1346 | "pycharm": { 1347 | "name": "#%%\n" 1348 | } 1349 | }, 1350 | "outputs": [], 1351 | "source": "# backend1: coupling map optimised circuit\n\nghz_backend1 = QuantumCircuit(5,3, name=f\"GHZ yyx {backend1.name} optimised\") \n### YOUR CODE HERE ###\n\n\nghz_backend1.draw(\"mpl\")" 1352 | }, 1353 | { 1354 | "cell_type": "code", 1355 | "execution_count": null, 1356 | "metadata": { 1357 | "pycharm": { 1358 | "name": "#%%\n" 1359 | } 1360 | }, 1361 | "outputs": [], 1362 | "source": "# backend2: coupling map optimised circuit\n\nghz_backend2 = QuantumCircuit(5,3, name=f\"GHZ yyx {backend2.name} optimised\") \n### YOUR CODE HERE ###\n\n\nghz_backend2.draw(\"mpl\")" 1363 | }, 1364 | { 1365 | "cell_type": "code", 1366 | "execution_count": null, 1367 | "metadata": { 1368 | "pycharm": { 1369 | "name": "#%%\n" 1370 | } 1371 | }, 1372 | "outputs": [], 1373 | "source": "# backend1: coupling map + error rate optimised circuit\n\nghz_backend1_error = QuantumCircuit(5,3, name=f\"GHZ yyx {backend1.name} error rate optimised\") \n### YOUR CODE HERE ###\n\nghz_backend1_error.draw(\"mpl\")" 1374 | }, 1375 | { 1376 | "cell_type": "code", 1377 | "execution_count": null, 1378 | "metadata": { 1379 | "pycharm": { 1380 | "name": "#%%\n" 1381 | } 1382 | }, 1383 | "outputs": [], 1384 | "source": "# backend2: coupling map + error rate optimised circuit\n\nghz_backend2_error = QuantumCircuit(5,3, name=f\"GHZ yyx {backend2.name} error rate optimised\") \n### YOUR CODE HERE ###\n\nghz_backend2_error.draw(\"mpl\")" 1385 | } 1386 | ], 1387 | "metadata": { 1388 | "kernelspec": { 1389 | "display_name": "Python 3 (ipykernel)", 1390 | "language": "python", 1391 | "name": "python3" 1392 | }, 1393 | "language_info": { 1394 | "codemirror_mode": { 1395 | "name": "ipython", 1396 | "version": 3 1397 | }, 1398 | "file_extension": ".py", 1399 | "mimetype": "text/x-python", 1400 | "name": "python", 1401 | "nbconvert_exporter": "python", 1402 | "pygments_lexer": "ipython3", 1403 | "version": "3.9.13" 1404 | } 1405 | }, 1406 | "nbformat": 4, 1407 | "nbformat_minor": 2 1408 | } --------------------------------------------------------------------------------