├── .github └── workflows │ └── jekyll-gh-pages.yml ├── Adjoint_Differentiation.ipynb ├── Classical_and_Quantum_Probabilities.ipynb ├── Data_reuploading_Classifier.ipynb ├── Evolution_in_Closed_and_Open_Systems.ipynb ├── Galaxy_Detection_using_QNN.ipynb ├── Gaussian_Transformation.ipynb ├── Hybrid_Computation.ipynb ├── Intro_to_QAOA.ipynb ├── Introduction_to_Qiskit.ipynb ├── LICENSE ├── Mathematical_Introduction.ipynb ├── Measurement_and_Mixed_States.ipynb ├── Noisy_Cricuits.ipynb ├── Quantum_Gradients_with_Backpropagation.ipynb ├── Quantum_Models_as_Fourier_Series.ipynb ├── Quantum_gradients_with_backpropagation.ipynb ├── Qubit_Rotation.ipynb ├── README.md ├── Running_Pennylane_in_AWS_Bracket.ipynb ├── Variational_Classifier_Iris.ipynb ├── What_is_Quantum_Machine_Learning.ipynb └── requirements.txt /.github/workflows/jekyll-gh-pages.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a Jekyll site to GitHub Pages 2 | name: Deploy Jekyll with GitHub Pages dependencies preinstalled 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow one concurrent deployment 19 | concurrency: 20 | group: "pages" 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | # Build job 25 | build: 26 | runs-on: ubuntu-latest 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v3 30 | - name: Setup Pages 31 | uses: actions/configure-pages@v2 32 | - name: Build with Jekyll 33 | uses: actions/jekyll-build-pages@v1 34 | with: 35 | source: ./ 36 | destination: ./_site 37 | - name: Upload artifact 38 | uses: actions/upload-pages-artifact@v1 39 | 40 | # Deployment job 41 | deploy: 42 | environment: 43 | name: github-pages 44 | url: ${{ steps.deployment.outputs.page_url }} 45 | runs-on: ubuntu-latest 46 | needs: build 47 | steps: 48 | - name: Deploy to GitHub Pages 49 | id: deployment 50 | uses: actions/deploy-pages@v1 51 | -------------------------------------------------------------------------------- /Adjoint_Differentiation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "view-in-github" 8 | }, 9 | "source": [ 10 | "\"Open" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": { 16 | "id": "LEqORNrkqHBb" 17 | }, 18 | "source": [ 19 | "# Adjoint Differentiation\n", 20 | "\n", 21 | "[Classical automatic differentiation](https://en.wikipedia.org/wiki/Automatic_differentiation#The_chain_rule,_forward_and_reverse_accumulation) has two methods for calculation\n", 22 | "* forward\n", 23 | "* reverse\n", 24 | "\n", 25 | "\n", 26 | "\n", 27 | "The Optimal choice of method depends on the structutre of the problem, whether the function is **many-to-one** or **one-to-many**?\n", 28 | "\n", 29 | "\n", 30 | "\n", 31 | "Most methods for calculating the derivatives of quantum circuit are either direct applications of classical gradient methods to quantum simulations, or quantum hardware methods like parameter-shift where we can only extract restricted pieces of information.\n", 32 | "\n", 33 | "------\n", 34 | "\n", 35 | "Adjoint differentiation studies these two strategies, taking benefits from each. On simulators, we can examine and modify the state vector at any point. At the same time, we know our quantum circuit holds specific properties not present in the arbitrary classsical computation.\n", 36 | "\n", 37 | "\n", 38 | "\n", 39 | "\n", 40 | "Quantum circuits only involve:\n", 41 | "\n", 42 | "1. initialization $$|0⟩$$\n", 43 | "\n", 44 | "2. application of unitary operators $$|\\Psi\\rangle = U_{n} U_{n-1} \\dots U_0 |0\\rangle,$$\n", 45 | "\n", 46 | "3. measurement, such as estimating an expectation value of a Hermitian operator $$\\langle M \\rangle = \\langle \\Psi | M | \\Psi \\rangle.$$\n", 47 | "\n", 48 | "\n", 49 | "\n", 50 | "Since all our operators are unitary, we can easily **undo** or **erase** them by applying their adjoint\n", 51 | "\n", 52 | "$$U^{\\dagger} U | \\phi \\rangle = |\\phi\\rangle.$$\n", 53 | "\n", 54 | "\n", 55 | "\n", 56 | "\n", 57 | "The **adjoint differentiation method** takes advantage of the ability to erase, creating a time- and memory- efficient method for computing quantum gradients on state vector simulators. \n", 58 | "\n", 59 | "\n", 60 | "--------\n", 61 | "\n", 62 | "~\n", 63 | "[Efficient calculation of gradients in classical simulations of variational quantum algorithm](https://arxiv.org/abs/2009.02823) \n" 64 | ] 65 | }, 66 | { 67 | "cell_type": "markdown", 68 | "metadata": { 69 | "id": "SGNOO8LNsLMY" 70 | }, 71 | "source": [ 72 | "## Time for some code\n", 73 | "\n", 74 | "Let's explore the above equations and their implementations in a little bit more detail.\n", 75 | "\n", 76 | "\n", 77 | "Start by importing pennylane and pennylane's numpy" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": 7, 83 | "metadata": { 84 | "id": "JbNZSRrMsWs-" 85 | }, 86 | "outputs": [], 87 | "source": [ 88 | "import pennylane as qml\n", 89 | "import jax\n", 90 | "from jax import numpy as np\n", 91 | "\n", 92 | "jax.config.update(\"jax_platform_name\", \"cpu\")" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": { 98 | "id": "rfEO4ay9sjzx" 99 | }, 100 | "source": [ 101 | "Need a quantum circuit to simulate" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 8, 107 | "metadata": { 108 | "id": "uCfQZBshsbT4" 109 | }, 110 | "outputs": [], 111 | "source": [ 112 | "dev = qml.device('default.qubit', wires=2)\n", 113 | "\n", 114 | "x = np.array([0.1, 0.2, 0.3])\n", 115 | "\n", 116 | "@qml.qnode(dev, diff_method=\"adjoint\")\n", 117 | "def circuit(a):\n", 118 | " qml.RX(a[0], wires=0)\n", 119 | " qml.CNOT(wires=(0,1))\n", 120 | " qml.RY(a[1], wires=1)\n", 121 | " qml.RZ(a[2], wires=1)\n", 122 | " return qml.expval(qml.PauliX(wires=1))" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": { 128 | "id": "LYSKH7MPsqHO" 129 | }, 130 | "source": [ 131 | "The fast c++ simulator device `\"lightning.qubit\"` also supports adjoint differentiation, but here we want to quickly prototype a minimal version to illustrate how the algorithm works. We recommend performing adjoint differentiation on `\"lightning.qubit\"` for substantial performance increases.\n", 132 | "\n", 133 | "We will use the `circuit` QNode just for comparison purposes. Throughout this demo, we will instead use a list of its operations `ops` and a single observable `M`." 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": 9, 139 | "metadata": { 140 | "id": "y2zE9-xZsnnE" 141 | }, 142 | "outputs": [], 143 | "source": [ 144 | "n_gates = 4\n", 145 | "n_params = 3\n", 146 | "\n", 147 | "ops = [\n", 148 | " qml.RX(x[0], wires=0),\n", 149 | " qml.CNOT(wires=(0,1)),\n", 150 | " qml.RY(x[1], wires=1),\n", 151 | " qml.RZ(x[2], wires=1)\n", 152 | "]\n", 153 | "M = qml.PauliX(wires=1)" 154 | ] 155 | }, 156 | { 157 | "cell_type": "markdown", 158 | "metadata": { 159 | "id": "TMXACcejtNL_" 160 | }, 161 | "source": [ 162 | "We create our state by using the `\"default.qubit\"` methods `_create_basis_state` and `_apply_operation`.\n", 163 | "\n", 164 | "These are private methods that you shouldn’t typically use and are subject to change without a deprecation period, but we use them here to illustrate the algorithm.\n", 165 | "\n", 166 | "Internally, the device uses a 2x2x2x… array to represent the state, whereas the measurement `qml.state()` and the device attribute `dev.state` flatten this internal representation." 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": 10, 172 | "metadata": { 173 | "colab": { 174 | "base_uri": "https://localhost:8080/" 175 | }, 176 | "id": "vQ07KCzOsx3Z", 177 | "outputId": "36f8b0c7-2cea-4eba-e934-b80c05ee0cbd" 178 | }, 179 | "outputs": [ 180 | { 181 | "name": "stdout", 182 | "output_type": "stream", 183 | "text": [ 184 | "[[9.8260182e-01-0.14850575j 9.8589033e-02+0.01490028j]\n", 185 | " [7.4563531e-04+0.00493356j 7.4314815e-03-0.04917107j]]\n" 186 | ] 187 | } 188 | ], 189 | "source": [ 190 | "from pennylane.devices.qubit import create_initial_state, apply_operation\n", 191 | "\n", 192 | "state = create_initial_state((0, 1))\n", 193 | "\n", 194 | "for op in ops:\n", 195 | " state = apply_operation(op, state)\n", 196 | "\n", 197 | "print(state)\n" 198 | ] 199 | }, 200 | { 201 | "cell_type": "markdown", 202 | "metadata": { 203 | "id": "35NeH5VitXwe" 204 | }, 205 | "source": [ 206 | "We can think of the expectation $⟨M⟩$ as an inner product between bra and a ket:\n", 207 | "\n", 208 | "$$\\langle M \\rangle = \\langle b | k \\rangle = \\langle \\Psi | M | \\Psi \\rangle,$$\n", 209 | "\n", 210 | "where \n", 211 | "\n", 212 | "$$\\langle b | = \\langle \\Psi| M = \\langle 0 | U_0^{\\dagger} \\dots U_n^{\\dagger} M,$$\n", 213 | "\n", 214 | "$$| k \\rangle = |\\Psi \\rangle = U_n U_{n-1} \\dots U_0 |0\\rangle.$$\n", 215 | "\n", 216 | "\n", 217 | "Using the `state` calculated above, we can create these $|b⟩$ and |k⟩$ vectors\n", 218 | "\n" 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": 11, 224 | "metadata": { 225 | "id": "bar2L7CMtWSR" 226 | }, 227 | "outputs": [], 228 | "source": [ 229 | "bra = apply_operation(M, state)\n", 230 | "ket = state" 231 | ] 232 | }, 233 | { 234 | "cell_type": "markdown", 235 | "metadata": { 236 | "id": "SvCbcrUuuD5p" 237 | }, 238 | "source": [ 239 | "Now we use `np.vdot` to take their inner product. `np.vdot` sums over all dimensions and takes the complex conjugate of the first input." 240 | ] 241 | }, 242 | { 243 | "cell_type": "code", 244 | "execution_count": 12, 245 | "metadata": { 246 | "colab": { 247 | "base_uri": "https://localhost:8080/" 248 | }, 249 | "id": "KsnkvEbcuCBO", 250 | "outputId": "f9a858a8-66b1-446c-e56c-db84909eaf9a" 251 | }, 252 | "outputs": [ 253 | { 254 | "name": "stdout", 255 | "output_type": "stream", 256 | "text": [ 257 | "vdot : (0.18884787+0j)\n", 258 | "QNode : 0.18884787\n" 259 | ] 260 | } 261 | ], 262 | "source": [ 263 | "M_expval = np.vdot(bra, ket)\n", 264 | "print(\"vdot : \", M_expval)\n", 265 | "print(\"QNode : \", circuit(x))" 266 | ] 267 | }, 268 | { 269 | "cell_type": "markdown", 270 | "metadata": { 271 | "id": "JTIDkinwuJ85" 272 | }, 273 | "source": [ 274 | "We got the same result via both methods! This validates our use of vdot and device methods.\n", 275 | "\n", 276 | "But the dividing line between what makes the “bra” and “ket” vector is actually fairly arbitrary. We can divide the two vectors at any point from one $⟨0|$ to the other \n", 277 | "$|\n", 278 | "0\n", 279 | "⟩$\n", 280 | ". For example, we could have used:\n", 281 | "\n", 282 | "$$\\langle b_n | = \\langle 0 | U_1^{\\dagger} \\dots U_n^{\\dagger} M U_n,$$\n", 283 | "\n", 284 | "$$|k_n \\rangle = U_{n-1} \\dots U_1 |0\\rangle,$$\n", 285 | "\n", 286 | "and gotten the exact same results. Here, the subscript $n$\n", 287 | " is used to indicate that \n", 288 | "$U_n$\n", 289 | " was moved to the bra side of the expression. Let’s calculate that instead\n" 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "execution_count": 13, 295 | "metadata": { 296 | "colab": { 297 | "base_uri": "https://localhost:8080/" 298 | }, 299 | "id": "QDogUYp2uH-R", 300 | "outputId": "f132833f-4b39-443a-a86e-de2142ca4bee" 301 | }, 302 | "outputs": [ 303 | { 304 | "name": "stdout", 305 | "output_type": "stream", 306 | "text": [ 307 | "(0.18884787-1.8626451e-09j)\n" 308 | ] 309 | } 310 | ], 311 | "source": [ 312 | "bra_n = create_initial_state((0, 1))\n", 313 | "\n", 314 | "for op in ops:\n", 315 | " bra_n = apply_operation(op, bra_n)\n", 316 | "bra_n = apply_operation(M, bra_n)\n", 317 | "bra_n = apply_operation(qml.adjoint(ops[-1]), bra_n)\n", 318 | "\n", 319 | "ket_n = create_initial_state((0, 1))\n", 320 | "\n", 321 | "for op in ops[:-1]: # don't apply last operation\n", 322 | " ket_n = apply_operation(op, ket_n)\n", 323 | "\n", 324 | "M_expval_n = np.vdot(bra_n, ket_n)\n", 325 | "print(M_expval_n)" 326 | ] 327 | }, 328 | { 329 | "cell_type": "markdown", 330 | "metadata": { 331 | "id": "D9BUgdQ4ucWA" 332 | }, 333 | "source": [ 334 | "Same answer!\n", 335 | "\n", 336 | "We can calculate this in a more efficient way if we already have the initial `state` $\n", 337 | "|\n", 338 | "Ψ\n", 339 | "⟩$\n", 340 | ". To shift the splitting point, we don’t have to recalculate everything from scratch. We just remove the operation from the ket and add it to the bra:\n", 341 | "\n", 342 | "$$\\langle b_n | = \\langle b | U_n,$$\n", 343 | "\n", 344 | "$$|k_n\\rangle = U_n^{\\dagger} |k\\rangle .$$\n", 345 | "\n", 346 | "\n", 347 | "For the ket vector, you can think of $U^{\\dagger}_n$ as “eating” its corresponding unitary from the vector, erasing it from the list of operations.\n", 348 | "\n", 349 | "Of course, we actually work with the conjugate transpose of \n", 350 | "$⟨\n", 351 | "b_\n", 352 | "n\n", 353 | "|$\n", 354 | ",\n", 355 | "\n", 356 | "\n", 357 | "$$|b_n\\rangle = U_n^{\\dagger} | b \\rangle. $$\n", 358 | "\n", 359 | "Once we write it in this form, we see that the adjoint of the operation \n", 360 | "$U^\n", 361 | "†_\n", 362 | "n$\n", 363 | " operates on both \n", 364 | "$|\n", 365 | "k_\n", 366 | "n\n", 367 | "⟩$\n", 368 | " and \n", 369 | "$|\n", 370 | "b_\n", 371 | "n\n", 372 | "⟩$\n", 373 | " to move the splitting point right.\n", 374 | "\n", 375 | "Let’s call this the “version 2” method.\n" 376 | ] 377 | }, 378 | { 379 | "cell_type": "code", 380 | "execution_count": 14, 381 | "metadata": { 382 | "colab": { 383 | "base_uri": "https://localhost:8080/" 384 | }, 385 | "id": "RM0scC-auaXV", 386 | "outputId": "989e3b06-4fb7-4619-c9f1-583f768f8849" 387 | }, 388 | "outputs": [ 389 | { 390 | "name": "stdout", 391 | "output_type": "stream", 392 | "text": [ 393 | "(0.18884787-1.869921e-09j)\n" 394 | ] 395 | } 396 | ], 397 | "source": [ 398 | "bra_n_v2 = apply_operation(M, state)\n", 399 | "ket_n_v2 = state\n", 400 | "\n", 401 | "adj_op = qml.adjoint(ops[-1])\n", 402 | "\n", 403 | "bra_n_v2 = apply_operation(adj_op, bra_n_v2)\n", 404 | "ket_n_v2 = apply_operation(adj_op, ket_n_v2)\n", 405 | "\n", 406 | "M_expval_n_v2 = np.vdot(bra_n_v2, ket_n_v2)\n", 407 | "print(M_expval_n_v2)" 408 | ] 409 | }, 410 | { 411 | "cell_type": "markdown", 412 | "metadata": { 413 | "id": "FkzP1f6dvBZb" 414 | }, 415 | "source": [ 416 | "Much simpler!\n", 417 | "\n", 418 | "We can easily iterate over all the operations to show that the same result occurs no matter where you split the operations:\n", 419 | "$$\\langle b_i | = \\langle b_{i+1}| U_{i},$$\n", 420 | "\n", 421 | "$$|k_{i+1} \\rangle = U_{i} |k_{i}\\rangle.$$\n", 422 | "\n", 423 | "Rewritten, we have our iteration formulas\n", 424 | "\n", 425 | "$$| b_i \\rangle = U_i^{\\dagger} |b_{i+1}\\rangle,$$\n", 426 | "\n", 427 | "$$| k_i \\rangle = U_i^{\\dagger} |k_{i+1}\\rangle.$$\n", 428 | "\n", 429 | "For each iteration, we move an operation from the ket side to the bra side. We start near the center at \n", 430 | "$U_\n", 431 | "n$\n", 432 | " and reverse through the operations list until we reach \n", 433 | "$U_0$\n", 434 | ".\n", 435 | "\n" 436 | ] 437 | }, 438 | { 439 | "cell_type": "code", 440 | "execution_count": 15, 441 | "metadata": { 442 | "colab": { 443 | "base_uri": "https://localhost:8080/" 444 | }, 445 | "id": "uqdlFAqku_vL", 446 | "outputId": "26de66e7-fb6d-4a03-e744-7fd51788b55d" 447 | }, 448 | "outputs": [ 449 | { 450 | "name": "stdout", 451 | "output_type": "stream", 452 | "text": [ 453 | "(0.18884787-1.869921e-09j)\n", 454 | "(0.18884788+8.6925944e-10j)\n", 455 | "(0.18884788+8.6925944e-10j)\n", 456 | "(0.1888479+8.6925944e-10j)\n" 457 | ] 458 | } 459 | ], 460 | "source": [ 461 | "bra_loop = apply_operation(M, state)\n", 462 | "ket_loop = state\n", 463 | "\n", 464 | "for op in reversed(ops):\n", 465 | " adj_op = qml.adjoint(op)\n", 466 | " bra_loop = apply_operation(adj_op, bra_loop)\n", 467 | " ket_loop = apply_operation(adj_op, ket_loop)\n", 468 | " print(np.vdot(bra_loop, ket_loop))" 469 | ] 470 | }, 471 | { 472 | "cell_type": "markdown", 473 | "metadata": { 474 | "id": "m16Shqcwva2P" 475 | }, 476 | "source": [ 477 | "# Finally the Derivatives\n", 478 | "\n", 479 | "We showed how to calculate the same thing a bunch of different ways. Why is this useful? Wherever we cut, we can stick additional things in the middle. What are we sticking in the middle? The derivative of a gate.\n", 480 | "\n", 481 | "For simplicity’s sake, assume each unitary operation \n", 482 | "$U_\n", 483 | "i$\n", 484 | " is a function of a single parameter \n", 485 | "$θ_\n", 486 | "i$\n", 487 | ". For non-parametrized gates like CNOT, we say its derivative is zero. We can also generalize the algorithm to multi-parameter gates, but we leave those out for now.\n", 488 | "\n", 489 | "Remember that each parameter occurs twice in \n", 490 | "$⟨\n", 491 | "M\n", 492 | "⟩$\n", 493 | ": once in the bra and once in the ket. Therefore, we use the product rule to take the derivative with respect to both locations:\n", 494 | "\n", 495 | "\n", 496 | "$$\\frac{\\partial \\langle M \\rangle}{\\partial \\theta_i} =\n", 497 | "\\langle 0 | U_1^{\\dagger} \\dots \\frac{\\text{d} U_i^{\\dagger}}{\\text{d} \\theta_i} \\dots M \\dots U_i \\dots U_1 | 0\\rangle $$\n", 498 | "$$+ \\langle 0 | U_1^{\\dagger} \\dots U_i^{\\dagger} \\dots M \\dots \\frac{\\text{d} U_i}{\\text{d} \\theta_i} \\dots U_1 |0\\rangle$$\n", 499 | "\n", 500 | "\n", 501 | "We can now notice that those two components are complex conjugates of each other, so we can further simplify. Note that each term is not an expectation value of a Hermitian observable, and therefore not guaranteed to be real. When we add them together, the imaginary part cancels out, and we obtain twice the value of the real part:\n", 502 | "\n", 503 | "\n", 504 | "$$= 2 \\cdot \\text{Re}\\left( \\langle 0 | U_1^{\\dagger} \\dots U_i^{\\dagger} \\dots M \\dots \\frac{\\text{d} U_i}{\\text{d} \\theta_i} \\dots U_1 |0\\rangle \\right).$$\n", 505 | "\n", 506 | "\n", 507 | "We can take that formula and break it into its “bra” and “ket” halves for a derivative at the \n", 508 | "$i^{th}$ position\n", 509 | "\n", 510 | "\n", 511 | "$$\\frac{\\partial \\langle M \\rangle }{\\partial \\theta_i } =\n", 512 | "2 \\text{Re} \\left( \\langle b_i | \\frac{\\text{d} U_i }{\\text{d} \\theta_i} | k_i \\rangle \\right)$$\n", 513 | "\n", 514 | "where \n", 515 | "\n", 516 | "$$\\langle b_i | = \\langle 0 | U_1^{\\dagger} \\dots U_n^{\\dagger} M U_n \\dots U_{i+1},$$\n", 517 | "\n", 518 | "$$|k_i \\rangle = U_{i-1} \\dots U_1 |0\\rangle.$$\n", 519 | "\n", 520 | "\n", 521 | "Notice that \n", 522 | "$U_\n", 523 | "i$\n", 524 | " does not appear in either the bra or the ket in the above equations. These formulas differ from the ones we used when just calculating the expectation value. For the actual derivative calculation, we use a temporary version of the bra,\n", 525 | "\n", 526 | "\n", 527 | " $$| \\tilde{k}_i \\rangle = \\frac{\\text{d} U_i}{\\text{d} \\theta_i} | k_i \\rangle$$\n", 528 | "\n", 529 | " and use to get the derivatives\n", 530 | "\n", 531 | " $$\\frac{\\partial \\langle M \\rangle}{\\partial \\theta_i} = 2 \\text{Re}\\left( \\langle b_i | \\tilde{k}_i \\rangle \\right).$$\n", 532 | "\n", 533 | " Both the bra and the ket can be calculated recursively:\n", 534 | "\n", 535 | " $$| b_{i} \\rangle = U^{\\dagger}_{i+1} |b_{i+1}\\rangle,$$\n", 536 | "\n", 537 | " $$| k_{i} \\rangle = U^{\\dagger}_{i} |k_{i+1}\\rangle.$$\n", 538 | "\n", 539 | "We can iterate through the operations starting at \n", 540 | "$n$\n", 541 | " and ending at \n", 542 | "$1$\n", 543 | ".\n", 544 | "\n", 545 | "We do have to calculate initial state first, the “forward” pass:\n", 546 | "\n", 547 | "\n", 548 | "$$ |\\Psi\\rangle = U_{n} U_{n-1} \\dots U_0 |0\\rangle.$$\n", 549 | "\n", 550 | "\n", 551 | "Once we have that, we only have about the same amount of work to calculate all the derivatives, instead of quadratically more work.\n", 552 | "\n", 553 | "\n", 554 | "\n" 555 | ] 556 | }, 557 | { 558 | "cell_type": "markdown", 559 | "metadata": { 560 | "id": "bQ-Czh0Rwij5" 561 | }, 562 | "source": [ 563 | "# Derivative of an Operator\n", 564 | "\n", 565 | "One final thing before we get back to coding: how do we get the derivative of an operator?\n", 566 | "\n", 567 | "Most parametrized gates can be represented in terms of Pauli Rotations, which can be written as\n", 568 | "$$U = e^{i c \\hat{G} \\theta}$$\n", 569 | "\n", 570 | "for a Pauli matrix\n", 571 | "$\\hat{G}$\n", 572 | ", a constant \n", 573 | "$c$\n", 574 | ", and the parameter \n", 575 | "$θ$\n", 576 | ". Thus we can easily calculate their derivatives:\n", 577 | "\n", 578 | "$$\\frac{\\text{d} U}{\\text{d} \\theta} = i c \\hat{G} e^{i c \\hat{G} \\theta} = i c \\hat{G} U .$$\n", 579 | "\n", 580 | "Luckily, PennyLane already has a built-in function for calculating this\n", 581 | "\n", 582 | "\n", 583 | "\n" 584 | ] 585 | }, 586 | { 587 | "cell_type": "code", 588 | "execution_count": 16, 589 | "metadata": { 590 | "colab": { 591 | "base_uri": "https://localhost:8080/" 592 | }, 593 | "id": "_grtiqDmvZNW", 594 | "outputId": "da3e5190-144d-4746-bdea-427fda59b9b6" 595 | }, 596 | "outputs": [ 597 | { 598 | "name": "stdout", 599 | "output_type": "stream", 600 | "text": [ 601 | "[[-0.02498958+0.j 0. -0.49937513j]\n", 602 | " [ 0. -0.49937513j -0.02498958+0.j ]]\n" 603 | ] 604 | } 605 | ], 606 | "source": [ 607 | "grad_op0 = qml.operation.operation_derivative(ops[0])\n", 608 | "print(grad_op0)" 609 | ] 610 | }, 611 | { 612 | "cell_type": "markdown", 613 | "metadata": { 614 | "id": "EnrCe2rjxB1K" 615 | }, 616 | "source": [ 617 | "Now for calculating the full derivative using the adjoint method!\n", 618 | "\n", 619 | "We loop over the reversed operations, just as before. But if the operation has a parameter, we calculate its derivative and append it to a list before moving on. Since the `operation_derivative` function spits back out a matrix instead of an operation, we have to use `dev._apply_unitary` instead to create \n", 620 | "$|k_i⟩$\n", 621 | "." 622 | ] 623 | }, 624 | { 625 | "cell_type": "code", 626 | "execution_count": 17, 627 | "metadata": { 628 | "colab": { 629 | "base_uri": "https://localhost:8080/" 630 | }, 631 | "id": "3PIkIQZOxANo", 632 | "outputId": "80aa0aae-f493-4b7c-f5d2-74e3d6f0b4a1" 633 | }, 634 | "outputs": [ 635 | { 636 | "name": "stdout", 637 | "output_type": "stream", 638 | "text": [ 639 | "our calculation: [-0.018947992473840714, 0.931615948677063, -0.05841749906539917]\n", 640 | "comparison: [-0.01894799 0.93161577 -0.0584175 ]\n" 641 | ] 642 | } 643 | ], 644 | "source": [ 645 | "bra = apply_operation(M, state)\n", 646 | "ket = state\n", 647 | "\n", 648 | "grads = []\n", 649 | "\n", 650 | "for op in reversed(ops):\n", 651 | " adj_op = qml.adjoint(op)\n", 652 | " ket = apply_operation(adj_op, ket)\n", 653 | "\n", 654 | " # Calculating the derivative\n", 655 | " if op.num_params != 0:\n", 656 | " dU = qml.operation.operation_derivative(op)\n", 657 | " ket_temp = apply_operation(qml.QubitUnitary(dU, op.wires), ket)\n", 658 | "\n", 659 | " dM = 2 * np.real(np.vdot(bra, ket_temp))\n", 660 | " grads.append(dM)\n", 661 | "\n", 662 | " bra = apply_operation(adj_op, bra)\n", 663 | "\n", 664 | "\n", 665 | "# Finally reverse the order of the gradients\n", 666 | "# since we calculated them in reverse\n", 667 | "grads = grads[::-1]\n", 668 | "\n", 669 | "print(\"our calculation: \", [float(grad) for grad in grads])\n", 670 | "\n", 671 | "grad_compare = jax.grad(circuit)(x)\n", 672 | "print(\"comparison: \", grad_compare)" 673 | ] 674 | }, 675 | { 676 | "cell_type": "markdown", 677 | "metadata": { 678 | "id": "vXP5rCHwxQTi" 679 | }, 680 | "source": [ 681 | "It matches!!!\n", 682 | "\n", 683 | "If you want to use adjoint differentiation without having to code up your own method that can support arbitrary circuits, you can use `diff_method=\"adjoint\"` in PennyLane with `\"default.qubit\"` or PennyLane’s fast C++ simulator `\"lightning.qubit\"`" 684 | ] 685 | }, 686 | { 687 | "cell_type": "code", 688 | "execution_count": 19, 689 | "metadata": { 690 | "colab": { 691 | "base_uri": "https://localhost:8080/" 692 | }, 693 | "id": "-18TflnixOX2", 694 | "outputId": "a69a28b7-844d-4bca-f965-41b9d21f02e4" 695 | }, 696 | "outputs": [ 697 | { 698 | "name": "stdout", 699 | "output_type": "stream", 700 | "text": [ 701 | "[-0.01894799 0.93161577 -0.0584175 ]\n" 702 | ] 703 | } 704 | ], 705 | "source": [ 706 | "dev_lightning = qml.device('lightning.qubit', wires=2)\n", 707 | "\n", 708 | "@qml.qnode(dev_lightning, diff_method=\"adjoint\")\n", 709 | "def circuit_adjoint(a):\n", 710 | " qml.RX(a[0], wires=0)\n", 711 | " qml.CNOT(wires=(0,1))\n", 712 | " qml.RY(a[1], wires=1)\n", 713 | " qml.RZ(a[2], wires=1)\n", 714 | " return qml.expval(M)\n", 715 | "\n", 716 | "print(jax.grad(circuit_adjoint)(x))" 717 | ] 718 | }, 719 | { 720 | "cell_type": "markdown", 721 | "metadata": { 722 | "id": "eQB594CbxaHt" 723 | }, 724 | "source": [ 725 | "# Performance\n", 726 | "\n", 727 | "The algorithm gives us the correct answers, but is it worth using? Parameter-shift gradients require at least two executions per parameter, so that method gets more and more expensive with the size of the circuit, especially on simulators. Backpropagation demonstrates decent time scaling, but requires more and more memory as the circuit gets larger. Simulation of large circuits is already RAM-limited, and backpropagation constrains the size of possible circuits even more. PennyLane also achieves backpropagation derivatives from a Python simulator and interface-specific functions. The `\"lightning.qubit\"` device does not support backpropagation, so backpropagation derivatives lose the speedup from an optimized simulator.\n", 728 | "\n", 729 | "With adjoint differentiation on `\"lightning.qubit\"`, you can get the best of both worlds: fast and memory efficient.\n", 730 | "\n", 731 | "But how fast? The provided script [here](https://pennylane.ai/qml/demos/adjoint_diff_benchmarking.html) generated the following images on a mid-range laptop. The backpropagation times were produced with the Python simulator `\"default.qubit\"`, while parameter-shift and adjoint differentiation times were calculated with `\"lightning.qubit\"`. The adjoint method clearly wins out for performance\n", 732 | "\n", 733 | "\n", 734 | "\n", 735 | "![alt text](https://pennylane.ai/_images/scaling.png)" 736 | ] 737 | }, 738 | { 739 | "cell_type": "markdown", 740 | "metadata": { 741 | "id": "OpLtEKSCxsQz" 742 | }, 743 | "source": [ 744 | "# Conclusion\n", 745 | "\n", 746 | "So what have we learned? Adjoint differentiation is an efficient method for differentiating quantum circuits with state vector simulation. It scales nicely in time without excessive memory requirements. Now that you’ve seen how the algorithm works, you can better understand what is happening when you select adjoint differentiation from one of PennyLane’s simulators." 747 | ] 748 | } 749 | ], 750 | "metadata": { 751 | "colab": { 752 | "authorship_tag": "ABX9TyMIQSRcOOW0Zspn3tcJi8uH", 753 | "include_colab_link": true, 754 | "provenance": [] 755 | }, 756 | "kernelspec": { 757 | "display_name": "Python 3", 758 | "name": "python3" 759 | }, 760 | "language_info": { 761 | "codemirror_mode": { 762 | "name": "ipython", 763 | "version": 3 764 | }, 765 | "file_extension": ".py", 766 | "mimetype": "text/x-python", 767 | "name": "python", 768 | "nbconvert_exporter": "python", 769 | "pygments_lexer": "ipython3", 770 | "version": "3.12.2" 771 | } 772 | }, 773 | "nbformat": 4, 774 | "nbformat_minor": 0 775 | } 776 | -------------------------------------------------------------------------------- /Evolution_in_Closed_and_Open_Systems.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "view-in-github" 8 | }, 9 | "source": [ 10 | "\"Open" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": { 16 | "id": "4Bmw8IqFa1CV" 17 | }, 18 | "source": [ 19 | "Classical probability distributions can be written as a stochastic vector, which can be transformed to another stochastic vector by applying a stochastic matrix. In other words, the evolution of stochastic vectors can be described by a stochastic matrix.\n", 20 | "\n", 21 | "Quantum states also evolve and their evolution is described by unitary matrices. This leads to some interesting properties in quantum computing. Unitary evolution is true for a closed system, that is, a quantum system perfectly isolated from the environment. This is not the case in the quantum computers we have today: these are open quantum systems that evolve differently due to to uncontrolled interactions with the environment. In this notebook, we take a glimpse at both types of evolution.\n", 22 | "\n", 23 | "\n", 24 | "# Unitary evolution\n", 25 | "\n", 26 | "A unitary matrix has the property that its conjugate transpose is its inverse. Formally, it means that a matrix $U$ is unitary if $UU^\\dagger=U^\\dagger U=\\mathbb{1}$, where $^\\dagger$ stands for conjugate transpose, and $\\mathbb{1}$ is the identity matrix. A quantum computer is a machine that implements unitary operations.\n", 27 | "\n", 28 | "As an example, we have seen the NOT operation before, which is performed by the X gate in a quantum computer. While the generic discussion on gates will only occur in a subsequent notebook, we can study the properties of the X gate. Its matrix representation is $X = \\begin{bmatrix} 0 & 1\\\\ 1 & 0\\end{bmatrix}$. Let's check if it is indeed unitary:" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 1, 34 | "metadata": { 35 | "ExecuteTime": { 36 | "end_time": "2018-11-19T19:49:07.614968Z", 37 | "start_time": "2018-11-19T19:49:07.530927Z" 38 | }, 39 | "colab": { 40 | "base_uri": "https://localhost:8080/" 41 | }, 42 | "id": "8hbMef9ra1Ck", 43 | "outputId": "ec9b6328-e1c1-4652-ae0d-36ffa52ac5c9" 44 | }, 45 | "outputs": [ 46 | { 47 | "name": "stdout", 48 | "output_type": "stream", 49 | "text": [ 50 | "XX^dagger\n", 51 | "[[1 0]\n", 52 | " [0 1]]\n", 53 | "X^daggerX\n", 54 | "[[1 0]\n", 55 | " [0 1]]\n" 56 | ] 57 | } 58 | ], 59 | "source": [ 60 | "import numpy as np\n", 61 | "X = np.array([[0, 1], [1, 0]])\n", 62 | "print(\"XX^dagger\")\n", 63 | "print(X @ X.T.conj())\n", 64 | "print(\"X^daggerX\")\n", 65 | "print(X.T.conj() @ X)" 66 | ] 67 | }, 68 | { 69 | "cell_type": "markdown", 70 | "metadata": { 71 | "id": "z0s5kKpca1Cp" 72 | }, 73 | "source": [ 74 | "It looks like a legitimate unitary operation. The unitary nature ensures that the $l_2$ norm is preserved, that is, quantum states are mapped to quantum states." 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 2, 80 | "metadata": { 81 | "ExecuteTime": { 82 | "end_time": "2018-11-19T19:49:07.626531Z", 83 | "start_time": "2018-11-19T19:49:07.618259Z" 84 | }, 85 | "colab": { 86 | "base_uri": "https://localhost:8080/" 87 | }, 88 | "id": "aaViof-Qa1Cq", 89 | "outputId": "3039c06f-1117-4ef6-aa01-472dd84ff434" 90 | }, 91 | "outputs": [ 92 | { 93 | "name": "stdout", 94 | "output_type": "stream", 95 | "text": [ 96 | "The norm of the state |0> before applying X\n", 97 | "1.0\n", 98 | "The norm of the state after applying X\n", 99 | "1.0\n" 100 | ] 101 | } 102 | ], 103 | "source": [ 104 | "print(\"The norm of the state |0> before applying X\")\n", 105 | "zero_ket = np.array([[1], [0]])\n", 106 | "print(np.linalg.norm(zero_ket))\n", 107 | "print(\"The norm of the state after applying X\")\n", 108 | "print(np.linalg.norm(X @ zero_ket))" 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": { 114 | "id": "8GgFIPwDa1Cr" 115 | }, 116 | "source": [ 117 | "Furthermore, since the unitary operation is a matrix, it is linear. Measurements are also represented by matrices. These two observations imply that everything a quantum computer implements is actually linear. If we want to see some form of nonlinearity, that must involve some classical intervention.\n", 118 | "\n", 119 | "Another consequence of the unitary operations is reversibility. Any unitary operation can be reversed. Quantum computing libraries often provide a function to reverse entire circuits. Reversing the X gate is simple: we just apply it again (its conjugate transpose is itself, therefore $X^2=\\mathbb{1}$)." 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": 3, 125 | "metadata": { 126 | "ExecuteTime": { 127 | "end_time": "2018-11-19T19:49:08.710098Z", 128 | "start_time": "2018-11-19T19:49:07.629733Z" 129 | }, 130 | "colab": { 131 | "base_uri": "https://localhost:8080/" 132 | }, 133 | "id": "drJJ58sta1Cs", 134 | "outputId": "96192518-8d9a-4120-ab0d-4c6b4dd23b6d" 135 | }, 136 | "outputs": [ 137 | { 138 | "name": "stdout", 139 | "output_type": "stream", 140 | "text": [ 141 | "[1.+0.j 0.+0.j]\n" 142 | ] 143 | } 144 | ], 145 | "source": [ 146 | "import numpy as np\n", 147 | "from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister\n", 148 | "from qiskit_aer import AerSimulator\n", 149 | "from qiskit.quantum_info import Statevector\n", 150 | "np.set_printoptions(precision=3, suppress=True)\n", 151 | "\n", 152 | "backend_statevector = AerSimulator(method='statevector')\n", 153 | "q = QuantumRegister(1)\n", 154 | "c = ClassicalRegister(1)\n", 155 | "circuit = QuantumCircuit(q, c)\n", 156 | "circuit.x(q[0])\n", 157 | "circuit.x(q[0])\n", 158 | "\n", 159 | "# get the statevector\n", 160 | "state = Statevector.from_instruction(circuit)\n", 161 | "print(state.data)" 162 | ] 163 | }, 164 | { 165 | "cell_type": "markdown", 166 | "metadata": { 167 | "id": "Xed2txNEa1Cu" 168 | }, 169 | "source": [ 170 | "which is exactly $|0\\rangle$ as we would expect.\n", 171 | "\n", 172 | "In the next notebook, you will learn about classical and quantum many-body systems and the Hamiltonian. In the notebook on adiabatic quantum computing, you will learn that a unitary operation is in fact the Schrödinger equation solved for a Hamiltonian for some duration of time. This connects the computer science way of thinking about gates and unitary operations to actual physics, but there is some learning to be done before we can make that connection. Before that, let us take another look at the interaction with the environment." 173 | ] 174 | }, 175 | { 176 | "cell_type": "markdown", 177 | "metadata": { 178 | "id": "D0J9W8Xxa1C-" 179 | }, 180 | "source": [ 181 | "# Interaction with the environment: open systems\n", 182 | "\n", 183 | "Actual quantum systems are seldom closed: they constantly interact with their environment in a largely uncontrolled fashion, which causes them to lose coherence. This is true for current and near-term quantum computers too.\n", 184 | "\n", 185 | "![](https://raw.githubusercontent.com/qosf/qml-mooc/9143b9aba0e58a049e2e4b98efd4b7caf471bd6e/figures/open_system.svg)\n", 186 | "\n", 187 | "This also means that their actual time evolution is not described by a unitary matrix as we would want it, but some other operator (the technical name for it is a completely positive trace-preserving map).\n", 188 | "\n", 189 | "Quantum computing libraries often offer a variety of noise models that mimic different types of interaction, and increasing the strength of the interaction with the environment leads to faster decoherence. The timescale for decoherence is often called $T_2$ time. Among a couple of other parameters, $T_2$ time is critically important for the number of gates or the duration of the quantum computation we can perform.\n", 190 | "\n", 191 | "A very cheap way of studying the effects of decoherence is mixing a pure state with the maximally mixed state $\\mathbb{1}/2^d$, where $d$ is the number of qubits, with some visibility parameter in $[0,1]$. This way we do not have to specify noise models or any other map modelling decoherence. For instance, we can mix the $|\\phi^+\\rangle$ state with the maximally mixed state:" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": 4, 197 | "metadata": { 198 | "ExecuteTime": { 199 | "end_time": "2018-11-19T19:49:08.730307Z", 200 | "start_time": "2018-11-19T19:49:08.714253Z" 201 | }, 202 | "colab": { 203 | "base_uri": "https://localhost:8080/" 204 | }, 205 | "id": "iK1FfvoFa1C_", 206 | "outputId": "145cf394-3dea-47b6-bd83-3fbf98f7940c" 207 | }, 208 | "outputs": [ 209 | { 210 | "name": "stdout", 211 | "output_type": "stream", 212 | "text": [ 213 | "Maximum visibility is a pure state:\n", 214 | "[[0.5 0. 0. 0.5]\n", 215 | " [0. 0. 0. 0. ]\n", 216 | " [0. 0. 0. 0. ]\n", 217 | " [0.5 0. 0. 0.5]]\n", 218 | "The state is still entangled with visibility 0.8:\n", 219 | "[[0.45 0. 0. 0.4 ]\n", 220 | " [0. 0.05 0. 0. ]\n", 221 | " [0. 0. 0.05 0. ]\n", 222 | " [0.4 0. 0. 0.45]]\n", 223 | "Entanglement is lost by 0.6:\n", 224 | "[[0.4 0. 0. 0.3]\n", 225 | " [0. 0.1 0. 0. ]\n", 226 | " [0. 0. 0.1 0. ]\n", 227 | " [0.3 0. 0. 0.4]]\n", 228 | "Barely any coherence remains by 0.2:\n", 229 | "[[0.3 0. 0. 0.1]\n", 230 | " [0. 0.2 0. 0. ]\n", 231 | " [0. 0. 0.2 0. ]\n", 232 | " [0.1 0. 0. 0.3]]\n" 233 | ] 234 | } 235 | ], 236 | "source": [ 237 | "def mixed_state(pure_state, visibility):\n", 238 | " density_matrix = pure_state @ pure_state.T.conj()\n", 239 | " maximally_mixed_state = np.eye(4)/2**2\n", 240 | " return visibility*density_matrix + (1-visibility)*maximally_mixed_state\n", 241 | "\n", 242 | "ϕ = np.array([[1],[0],[0],[1]])/np.sqrt(2)\n", 243 | "print(\"Maximum visibility is a pure state:\")\n", 244 | "print(mixed_state(ϕ, 1.0))\n", 245 | "print(\"The state is still entangled with visibility 0.8:\")\n", 246 | "print(mixed_state(ϕ, 0.8))\n", 247 | "print(\"Entanglement is lost by 0.6:\")\n", 248 | "print(mixed_state(ϕ, 0.6))\n", 249 | "print(\"Barely any coherence remains by 0.2:\")\n", 250 | "print(mixed_state(ϕ, 0.2))" 251 | ] 252 | }, 253 | { 254 | "cell_type": "markdown", 255 | "metadata": { 256 | "id": "ifZmi3oaa1DA" 257 | }, 258 | "source": [ 259 | "Another way to look at what happens to a quantum state in an open system is through equilibrium processes. Think of a cup of coffee: left alone, it will equilibrate with the environment, eventually reaching the temperature of the environment. This includes energy exchange. A quantum state does the same thing and the environment has a defined temperature, just like the environment of a cup of coffee.\n", 260 | "\n", 261 | "The equilibrium state is called the thermal state. It has a very specific structure and we will revisit it, but for now, suffice to say that the energy of the samples pulled out of a thermal state follows a Boltzmann distribution. The Boltzmann -- also called Gibbs -- distribution is described as $P(E_i) = \\frac {e^{-E_{i}/T}}{\\sum _{j=1}^{M}{e^{-E_{j}/T}}}$, where $E_i$ is an energy, and $M$ is the total number of possible energy levels. Temperature enters the definition: the higher the temperature, the closer we are to the uniform distribution. In the infinite temperature limit, it recovers the uniform distribution. At high temperatures, all energy levels have an equal probability. In contrast, at zero temperature, the entire probability mass is concentrated on the lowest energy level, the ground state energy. To get a sense of this, let's plot the Boltzmann distribution with vastly different temperatures:" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": 5, 267 | "metadata": { 268 | "ExecuteTime": { 269 | "end_time": "2018-11-19T19:49:09.226294Z", 270 | "start_time": "2018-11-19T19:49:08.733112Z" 271 | }, 272 | "colab": { 273 | "base_uri": "https://localhost:8080/", 274 | "height": 283 275 | }, 276 | "id": "RIjKKK3la1DB", 277 | "outputId": "2284da30-c234-4606-c1c2-0f2473fc08f0" 278 | }, 279 | "outputs": [ 280 | { 281 | "data": { 282 | "text/plain": [ 283 | "" 284 | ] 285 | }, 286 | "execution_count": 5, 287 | "metadata": {}, 288 | "output_type": "execute_result" 289 | }, 290 | { 291 | "data": { 292 | "image/png": "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LOeccw4pKSl07dqV9957z/D877//njPOOIO0tDRatWrFJZdcwp49e6rOn3rqqUyZMoUbb7yRnJwcTj/9dADee+89unbtSlJSEsOGDeP555/HZrOxf/9+SkpKyMjIYPbs2Yb3mjNnDqmpqVWL8IlI+FK4EJFa/fnPf2bChAmsWrWK0aNHc9FFF7F3714A9u/fz/Dhw+nbty//+9//+Pjjj9m5cycTJkwwvMbzzz9PQkICCxcu5Omnn2bz5s2MGzeOs88+m5UrV3LNNddw5513Vl2fmprK+eef79WjMmvWLMaNG0d6enrwP3ERaRq3iMSkyy67zO1wONypqamGx4MPPuh2u91uwH3XXXdVXV9cXOwG3B999JHb7Xa777//fvfIkSMNr5mfn+8G3Bs2bHC73W730KFD3X379jVcM23aNHePHj0MbXfeeacbcO/bt8/tdrvd3377rdvhcLi3bdvmdrvd7p07d7rj4uLcX3zxReC+ACISNBpzIRLDhg0bxsyZMw1tzZs3r/q4V69eVR+npqaSkZHBrl27AFi5ciXz588nLS3N63U3bdrE0UcfDUD//v0N5zZs2MAJJ5xgaBswYIDX8XHHHcfzzz/P7bffzksvvUT79u0ZMmRIIz5LEQk1hQuRGJaamkqXLl1qPR8fH284ttlsuFyeXXOLi4s588wz+etf/+r1vDZt2hjeozGuuuoqnnzySW6//XZmzZrF5Zdfrr2IRCKEwoWINEq/fv1466236NChA3Fx/n8r6datGx9++KGhbenSpV7XXXzxxfzhD39gxowZrF27lssuu6zJNYtIaGhAp0gMKysrY8eOHYZHzdkedZk8eTJ79+7lggsuYOnSpWzatIm5c+dy+eWX43Q6a33eNddcw/r165k2bRo//PADb7zxBv/5z38ADD0TzZo1Y+zYsdx2222MHDmStm3bNulzFZHQUbgQiWEff/wxbdq0MTwGDRrk13Nzc3NZuHAhTqeTkSNH0rNnT2688UaysrLq3Iq5Y8eOzJ49m7fffptevXoxc+bMqtki5jU2rrzySsrLy7niiisa/0mKSMhpy3URsdyDDz7I008/TX5+vqH9xRdf5KabbmLbtm1afEskgmjMhYiE3FNPPcUJJ5xAdnY2Cxcu5JFHHmHKlClV50tLS9m+fTsPP/ww11xzjYKFSITRbRERCbkff/yRMWPG0L17d+6//35uueUW7r333qrz06dP55hjjqF169bccccd1hUqIo2i2yIiIiISUOq5EBERkYBSuBAREZGAUrgQERGRgFK4EBERkYBSuBAREZGAUrgQERGRgFK4EBERkYBSuBAREZGAUrgQERGRgPp/UBEHr6SFUM8AAAAASUVORK5CYII=", 293 | "text/plain": [ 294 | "
" 295 | ] 296 | }, 297 | "metadata": {}, 298 | "output_type": "display_data" 299 | } 300 | ], 301 | "source": [ 302 | "import matplotlib.pyplot as plt\n", 303 | "\n", 304 | "temperatures = [.5, 5, 2000]\n", 305 | "energies = np.linspace(0, 20, 100)\n", 306 | "fig, ax = plt.subplots()\n", 307 | "for i, T in enumerate(temperatures):\n", 308 | " probabilities = np.exp(-energies/T)\n", 309 | " Z = probabilities.sum()\n", 310 | " probabilities /= Z\n", 311 | " ax.plot(energies, probabilities, linewidth=3, label = \"$T_\" + str(i+1)+\"$\")\n", 312 | "ax.set_xlim(0, 20)\n", 313 | "ax.set_ylim(0, 1.2*probabilities.max())\n", 314 | "ax.set_xticks([])\n", 315 | "ax.set_yticks([])\n", 316 | "ax.set_xlabel('Energy')\n", 317 | "ax.set_ylabel('Probability')\n", 318 | "ax.legend()" 319 | ] 320 | }, 321 | { 322 | "cell_type": "markdown", 323 | "metadata": { 324 | "id": "PCpbmGLha1DC" 325 | }, 326 | "source": [ 327 | "Here $T_1\"Open" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": { 16 | "id": "Wo8gh7fA0CM9" 17 | }, 18 | "source": [ 19 | "# Gaussian Transformation\n", 20 | "\n", 21 | "Here we'll learn about the basic principles of continuous variable (CV) photonic devices.\n", 22 | "\n", 23 | "\n", 24 | "Read [this](https://strawberryfields.readthedocs.io/en/latest/) documentation to learn more on it." 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": { 30 | "id": "b2cDyZU00Uzh" 31 | }, 32 | "source": [ 33 | "## The quantum circuit\n", 34 | "----\n", 35 | "\n", 36 | "For this basic tutorial, we will consider a special subset of CV operations: the Gaussian transformations. We work with the following simple Gaussian circuit:\n", 37 | "\n", 38 | "\n", 39 | "![alt text](https://pennylane.ai/qml/_images/gaussian_transformation.svg)\n", 40 | "\n", 41 | "\n", 42 | "What is this circuit doing?\n", 43 | "\n", 44 | "1. **We begin with one wire in the vaccum state** : Note that we use the same notation $|0⟩$ for the initial states. In a photonic CV system, this state is the *vaccum state* i.e a state with no photons in the wire.\n", 45 | "\n", 46 | "\n", 47 | "2. **We displace the wire(qumode)** : The displacement gate linearly shifts the state of the qumode in phase space. The vacuum state is centered at the origin in phase space, while the displaced state will be centered at the point $α$.\n", 48 | "\n", 49 | "3. **We rotate the qumode** : This is another linear transformation in phase space, albeit a rotation (by angle $ϕ$) instead of a displacement.\n", 50 | "\n", 51 | "4. **Finally, we measure the mean photon number** : $\\langle\\hat{n}\\rangle =\n", 52 | "\\langle\\hat{a}^\\dagger \\hat{a}\\rangle$ This quantity, which tells us the average amount of photons in the final state, is proportional to the energy of the photonic system.\n", 53 | "\n", 54 | "\n", 55 | "\n", 56 | "\n", 57 | "\n", 58 | "\n", 59 | "\n", 60 | "----\n", 61 | "Our aim here is to optimize the circuit parameters $(\\alpha, \\phi)$ such that the mean photon number is equal to one. The rotating gate is actually a *passive transformation*, meaning that it does not change the energy of the system. The displacement gate is an *active transformation*, which modifies the energy of the photonic system." 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": { 67 | "id": "v1rphGsv17TA" 68 | }, 69 | "source": [ 70 | "## Constructing the QNode\n", 71 | "\n", 72 | "Import Pennylane and NumPy" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": 1, 78 | "metadata": { 79 | "id": "X5PQCKxW0fv1" 80 | }, 81 | "outputs": [], 82 | "source": [ 83 | "import pennylane as qml\n", 84 | "from pennylane import numpy as np" 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": { 90 | "id": "fIqx_6Ve2D1Y" 91 | }, 92 | "source": [ 93 | "Next, we instantiate a device which will be used to evaluate the circuit. Because our circuit contains only Gaussian operations, we can make use of the built-in `default.gaussian` device." 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": 2, 99 | "metadata": { 100 | "id": "oeoFDYCd2BYe" 101 | }, 102 | "outputs": [], 103 | "source": [ 104 | "dev_gaussian = qml.device(\"default.gaussian\", wires=1)" 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "metadata": { 110 | "id": "aRy5muAV2LHr" 111 | }, 112 | "source": [ 113 | "After initializing the device, we can construct our quantum node. As before, we use the `qnode()` to convert our quantum function (encoded by the circuit above) into a quantum node running on the `default.gaussian` device." 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 3, 119 | "metadata": { 120 | "id": "WeTUdD1w2JVo" 121 | }, 122 | "outputs": [], 123 | "source": [ 124 | "@qml.qnode(dev_gaussian)\n", 125 | "def mean_photon_gaussian(mag_alpha, phase_alpha, phi):\n", 126 | " qml.Displacement(mag_alpha, phase_alpha, wires=0)\n", 127 | " qml.Rotation(phi, wires=0)\n", 128 | " return qml.expval(qml.NumberOperator(0))" 129 | ] 130 | }, 131 | { 132 | "cell_type": "markdown", 133 | "metadata": { 134 | "id": "W2H_pYWr2TC5" 135 | }, 136 | "source": [ 137 | "Notice that we have broken up the complex number $α$\n", 138 | " into two real numbers `mag_alpha` and `phase_alpha`, which form a polar representation of $α$\n", 139 | ". This is so that the notion of a gradient is clear and well-defined." 140 | ] 141 | }, 142 | { 143 | "cell_type": "markdown", 144 | "metadata": { 145 | "id": "G2SWZbav2ZcG" 146 | }, 147 | "source": [ 148 | "## Optimization\n", 149 | "\n", 150 | "As in the [qubit rotation](https://colab.research.google.com/github/MonitSharma/Learn-Quantum-Machine-Learning/blob/main/Qubit_Rotation.ipynb) tutorial, let’s now use one of the built-in PennyLane optimizers in order to optimize the quantum circuit towards the desired output. We want the mean photon number to be exactly one, so we will use a squared-difference cost function:" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": 4, 156 | "metadata": { 157 | "id": "K9bFyeE52RNr" 158 | }, 159 | "outputs": [], 160 | "source": [ 161 | "def cost(params):\n", 162 | " return (mean_photon_gaussian(params[0], params[1], params[2]) - 1.0) ** 2\n" 163 | ] 164 | }, 165 | { 166 | "cell_type": "markdown", 167 | "metadata": { 168 | "id": "MtuCyrtk2mYZ" 169 | }, 170 | "source": [ 171 | "At the beginning of the optimization, we choose arbitrary small initial parameters:" 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": 5, 177 | "metadata": { 178 | "colab": { 179 | "base_uri": "https://localhost:8080/" 180 | }, 181 | "id": "Q4d1C6dw2kcT", 182 | "outputId": "a0c748c6-9618-4036-8140-c368c6056f8c" 183 | }, 184 | "outputs": [ 185 | { 186 | "name": "stdout", 187 | "output_type": "stream", 188 | "text": [ 189 | "0.9995500506249999\n" 190 | ] 191 | } 192 | ], 193 | "source": [ 194 | "init_params = np.array([0.015, 0.02, 0.005], requires_grad=True)\n", 195 | "print(cost(init_params))" 196 | ] 197 | }, 198 | { 199 | "cell_type": "markdown", 200 | "metadata": { 201 | "id": "V6ngLFOe2qxz" 202 | }, 203 | "source": [ 204 | "When the gate parameters are near to zero, the gates are close to the identity transformation, which leaves the initial state largely unchanged. Since the initial state contains no photons, the mean photon number of the circuit output is approximately zero, and the cost is close to one.\n", 205 | "\n", 206 | "-----\n", 207 | "We avoided initial parameters which are exactly zero because that corresponds to a critical point with zero gradient.\n", 208 | "\n", 209 | "-------\n", 210 | "\n", 211 | "Now, let’s use the `GradientDescentOptimizer`, and update the circuit parameters over 100 optimization steps.\n", 212 | "\n", 213 | "\n" 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": 6, 219 | "metadata": { 220 | "colab": { 221 | "base_uri": "https://localhost:8080/" 222 | }, 223 | "id": "h5Wi_2AA2olc", 224 | "outputId": "3c61c917-2ae0-42c9-92ff-70d4eb64538c" 225 | }, 226 | "outputs": [ 227 | { 228 | "name": "stdout", 229 | "output_type": "stream", 230 | "text": [ 231 | "Cost after step 1: 0.999118\n", 232 | "Cost after step 2: 0.998273\n", 233 | "Cost after step 3: 0.996618\n", 234 | "Cost after step 4: 0.993382\n", 235 | "Cost after step 5: 0.987074\n", 236 | "Cost after step 6: 0.974837\n", 237 | "Cost after step 7: 0.951332\n", 238 | "Cost after step 8: 0.907043\n", 239 | "Cost after step 9: 0.826649\n", 240 | "Cost after step 10: 0.690812\n", 241 | "Cost after step 11: 0.490303\n", 242 | "Cost after step 12: 0.258845\n", 243 | "Cost after step 13: 0.083224\n", 244 | "Cost after step 14: 0.013179\n", 245 | "Cost after step 15: 0.001001\n", 246 | "Cost after step 16: 0.000049\n", 247 | "Cost after step 17: 0.000002\n", 248 | "Cost after step 18: 0.000000\n", 249 | "Cost after step 19: 0.000000\n", 250 | "Cost after step 20: 0.000000\n", 251 | "Optimized mag_alpha:0.999994\n", 252 | "Optimized phase_alpha:0.020000\n", 253 | "Optimized phi:0.005000\n" 254 | ] 255 | } 256 | ], 257 | "source": [ 258 | "# initialise the optimizer\n", 259 | "opt = qml.GradientDescentOptimizer(stepsize=0.1)\n", 260 | "\n", 261 | "# set the number of steps\n", 262 | "steps = 20\n", 263 | "# set the initial parameter values\n", 264 | "params = init_params\n", 265 | "\n", 266 | "for i in range(steps):\n", 267 | " # update the circuit parameters\n", 268 | " params = opt.step(cost, params)\n", 269 | "\n", 270 | " print(\"Cost after step {:5d}: {:8f}\".format(i + 1, cost(params)))\n", 271 | "\n", 272 | "print(\"Optimized mag_alpha:{:8f}\".format(params[0]))\n", 273 | "print(\"Optimized phase_alpha:{:8f}\".format(params[1]))\n", 274 | "print(\"Optimized phi:{:8f}\".format(params[2]))" 275 | ] 276 | }, 277 | { 278 | "cell_type": "markdown", 279 | "metadata": { 280 | "id": "EFN1mc8824HM" 281 | }, 282 | "source": [ 283 | "The optimization converges after about 20 steps to a cost function value of zero.\n", 284 | "\n", 285 | "We observe that the two angular parameters phase_alpha and phi do not change during the optimization. Only the magnitude of the complex displacement $|α|$ affects the mean photon number of the circuit.\n", 286 | "\n", 287 | "\n", 288 | "Next we will learn how to utilize the extensive plugin ecosystem of Pennylane, build continuous variable quantum nodes." 289 | ] 290 | } 291 | ], 292 | "metadata": { 293 | "colab": { 294 | "authorship_tag": "ABX9TyOXzUywhEI6EA0KtJqWmdod", 295 | "include_colab_link": true, 296 | "provenance": [] 297 | }, 298 | "kernelspec": { 299 | "display_name": "Python 3", 300 | "name": "python3" 301 | }, 302 | "language_info": { 303 | "codemirror_mode": { 304 | "name": "ipython", 305 | "version": 3 306 | }, 307 | "file_extension": ".py", 308 | "mimetype": "text/x-python", 309 | "name": "python", 310 | "nbconvert_exporter": "python", 311 | "pygments_lexer": "ipython3", 312 | "version": "3.12.2" 313 | } 314 | }, 315 | "nbformat": 4, 316 | "nbformat_minor": 0 317 | } 318 | -------------------------------------------------------------------------------- /Hybrid_Computation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "view-in-github" 8 | }, 9 | "source": [ 10 | "\"Open" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": { 16 | "id": "EvWVLt22zN-q" 17 | }, 18 | "source": [ 19 | "# Plugins and Hybrid Computation\n", 20 | "\n", 21 | "Here you'll be introduced to the notion of Hybrid Computation by combining several PennyLane plugins. Including [Strawberry Field Plugin](https://pennylane-sf.readthedocs.io/) which is used to explore a non-Gaussian photonic circuit. We combine this photonic circuit with a qubit circuit - along with some classical processing - to create and optimize a fully hybrid computation. \n", 22 | "\n", 23 | "-----\n", 24 | "\n", 25 | "\n", 26 | "Be sure to read through the introductory [qubit rotation](https://colab.research.google.com/github/MonitSharma/Learn-Quantum-Machine-Learning/blob/main/Qubit_Rotation.ipynb) and [Gaussian transformation](https://colab.research.google.com/github/MonitSharma/Learn-Quantum-Machine-Learning/blob/main/Gaussian_Transformation.ipynb) tutorials before going through with this one." 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "metadata": {}, 32 | "source": [ 33 | "**Only Compatible with Pennylane Version $0.29$ or below**" 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "metadata": { 39 | "id": "ePUxnjBLoPxb" 40 | }, 41 | "source": [ 42 | "\n", 43 | "\n", 44 | "This will require the `pennylane-SF` plugon, in order to access the Strawberry Fields Fock backend using Pennylane. You can install it via \n", 45 | "\n", 46 | "`pip install pennylane-sf`" 47 | ] 48 | }, 49 | { 50 | "cell_type": "markdown", 51 | "metadata": { 52 | "id": "Xumb2nomopCl" 53 | }, 54 | "source": [ 55 | "## A Non-Gaussian Circuit\n", 56 | "\n", 57 | "We first consider a photonic circuit which is similar to the qubit rotation circuit:\n", 58 | "\n", 59 | "\n", 60 | "![alt text](https://pennylane.ai/qml/_images/photon_redirection.png)\n", 61 | "\n", 62 | "-------\n", 63 | "\n", 64 | "\n", 65 | "Breaking this down, step-by-steo\n", 66 | "\n", 67 | "2. **We start the computaion with two qumode subsystems** : In PennyLane, we use the shorthand ‘wires’ to refer to quantum subsystems, whether they are qumodes, qubits, or any other kind of quantum register.\n", 68 | "\n", 69 | "\n", 70 | "2. **Prepare the state $|1,0⟩$** : That is, the first wire (wire 0) is prepared in a single-photon state, while the second wire (wire 1) is prepared in the vacuum state. The former state is non-Gaussian, necessitating the use of the `'strawberryfields.fock'` backend device.\n", 71 | "\n", 72 | "\n", 73 | "3. **Bith wires are then incident on a beamsplitter** with free parameters $\\theta$ and $\\phi$. Here, we have the convention that the beam splitter transmission amplitudes is $t = \\cos \\theta$ , and the reflection amplitude is $r = e^{\\iota \\phi} \\sin \\theta$.\n", 74 | "\n", 75 | "4. **Finally, we measure the mean photon number** $\\langle n ⟩$ of the second wire, where $ n = a^{\\dagger} a$ is the number operator, acting on the Fock basis number states, such that $\\hat{n}\\left|n\\right\\rangle = n\\left|n\\right\\rangle$\n", 76 | "\n", 77 | "\n", 78 | "-------\n", 79 | "\n", 80 | "\n", 81 | "The aim of this tutorial is to optimize the beamsplitter parameters \n", 82 | "$(\n", 83 | "θ\n", 84 | ",\n", 85 | "ϕ\n", 86 | ")$\n", 87 | " such that the expected photon number of the second wire is **maximized**. Since the beamsplitter is a passive optical element that preserves the total photon number, this to the output state \n", 88 | "$|\n", 89 | "0\n", 90 | ",\n", 91 | "1\n", 92 | "⟩$\n", 93 | " — i.e., when the incident photon from the first wire has been ‘redirected’ to the second wire.\n", 94 | "\n", 95 | "\n", 96 | "\n", 97 | "\n", 98 | " \n", 99 | "\n", 100 | "\n" 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "metadata": { 106 | "id": "D89-r2etqRTJ" 107 | }, 108 | "source": [ 109 | "## Exact Calculations\n", 110 | "\n", 111 | "To compare with later numerical values, we can consider what happens analytically. The initial state of the circuit is $\\left|\\psi_0\\right\\rangle=\\left|1,0\\right\\rangle$ , and the output state of the system is of the form $\\left|\\psi\\right\\rangle = a\\left|1, 0\\right\\rangle + b\\left|0,1\\right\\rangle$ where $|a|^2+|b|^2=1$. We may thus write the output state as a vector in the computational basis, $\\left|\\psi\\right\\rangle = \\begin{bmatrix}a & b\\end{bmatrix}^T$\n", 112 | "\n", 113 | "------\n", 114 | "\n", 115 | "The beamsplitter acts on this two-dimensional subspace as follows:\n", 116 | "\n", 117 | "$$\\begin{split}\\left|\\psi\\right\\rangle = B(\\theta, \\phi)\\left|1, 0\\right\\rangle = \\begin{bmatrix}\n", 118 | " \\cos\\theta & -e^{-i\\phi}\\sin\\theta\\\\\n", 119 | " e^{i\\phi}\\sin\\theta & \\cos\\theta\n", 120 | "\\end{bmatrix}\\begin{bmatrix} 1\\\\ 0\\end{bmatrix} = \\begin{bmatrix}\n", 121 | " \\cos\\theta\\\\\n", 122 | " e^{i\\phi} \\sin\\theta\n", 123 | "\\end{bmatrix}\\end{split}$$\n", 124 | "\n", 125 | "Furthermore, the mean photon number of the second wire is ⁉\n", 126 | "$$\\left\\langle{\\hat{n}_1}\\right\\rangle = \\langle{\\psi}\\mid{\\hat{n}_1}\\mid{\\psi}\\rangle = |e^{i\\phi} \\sin\\theta|^2\n", 127 | "\\langle{0,1}\\mid{\\hat{n}_1}\\mid{0,1}\\rangle = \\sin^2 \\theta.$$\n", 128 | "\n", 129 | "\n", 130 | "Therefore, we can see that:\n", 131 | "\n", 132 | "1. $0\\leq \\left\\langle \\hat{n}_1\\right\\rangle\\leq 1$ : the output of the quantum circuit is bounded between $0$ and $1$\n", 133 | "\n", 134 | "2. $\\frac{\\partial}{\\partial \\phi} \\left\\langle \\hat{n}_1\\right\\rangle=0$ : the output of the quantum circuit is independent of the beamsplitter phase $\\phi$\n", 135 | "\n", 136 | "3. The output of the quantum circuit above is maximized when $\\theta=(2m+1)\\pi/2$ for $m\\in\\mathbb{Z}_0$\n", 137 | "\n", 138 | "\n", 139 | "\n", 140 | "\n", 141 | "\n" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": { 147 | "id": "nTKkmn1uraWW" 148 | }, 149 | "source": [ 150 | "## Loading the plugin device\n", 151 | "\n", 152 | "While PennyLane provides a basic qubit simulator (`'default.qubit'`) and a basic CV Gaussian simulator (`'default.gaussian'`), the true power of PennyLane comes from its plugin ecosystem, allowing quantum computations to be run on a variety of quantum simulator and hardware devices.\n", 153 | "\n", 154 | "For this circuit, we will be using the `'strawberryfields.fock'` device to construct a QNode. This allows the underlying quantum computation to be performed using the Strawberry Fields Fock backend.\n", 155 | "\n", 156 | "As usual, we begin by importing PennyLane and the wrapped version of NumPy provided by PennyLane" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": 3, 162 | "metadata": { 163 | "id": "NbtTfCqxolZE" 164 | }, 165 | "outputs": [], 166 | "source": [ 167 | "import pennylane as qml\n", 168 | "from pennylane import numpy as np" 169 | ] 170 | }, 171 | { 172 | "cell_type": "markdown", 173 | "metadata": { 174 | "id": "pkkWXqAqro1I" 175 | }, 176 | "source": [ 177 | "Next, we create a device to run the quantum node. This is easy in PennyLane; as soon as the PennyLane-SF plugin is installed, the `'strawberryfields.fock'` device can be loaded — no additional commands or library imports required." 178 | ] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "execution_count": 4, 183 | "metadata": { 184 | "id": "OkWIcXROrm7L" 185 | }, 186 | "outputs": [], 187 | "source": [ 188 | "dev_fock = qml.device(\"strawberryfields.fock\", wires=2, cutoff_dim=2)" 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "metadata": { 194 | "id": "NkGTd2Z5ruRk" 195 | }, 196 | "source": [ 197 | "Compared to the default devices provided with PennyLane, the `'strawberryfields.fock'` device requires the additional keyword argument:\n", 198 | "\n", 199 | "* `cutoff_dim` : the Fock space trunction used to perform the quantum simulation" 200 | ] 201 | }, 202 | { 203 | "cell_type": "markdown", 204 | "metadata": { 205 | "id": "ktDU4b0Qr4zT" 206 | }, 207 | "source": [ 208 | "## Constructing the QNode\n", 209 | "\n", 210 | "Now that we have initialized the device, we can construct our quantum node. Like the other tutorials, we use the `qnode` decorator to convert our quantum function (encoded by the circuit above) into a quantum node running on Strawberry Fields." 211 | ] 212 | }, 213 | { 214 | "cell_type": "code", 215 | "execution_count": 5, 216 | "metadata": { 217 | "id": "R3sAFDy_rski" 218 | }, 219 | "outputs": [], 220 | "source": [ 221 | "@qml.qnode(dev_fock, diff_method=\"parameter-shift\")\n", 222 | "def photon_redirection(params):\n", 223 | " qml.FockState(1, wires=0)\n", 224 | " qml.Beamsplitter(params[0], params[1], wires=[0, 1])\n", 225 | " return qml.expval(qml.NumberOperator(1))" 226 | ] 227 | }, 228 | { 229 | "cell_type": "markdown", 230 | "metadata": { 231 | "id": "AsJ9N58dsAb3" 232 | }, 233 | "source": [ 234 | "The `'strawberryfields.fock'` device supports all CV objects provided by PennyLane" 235 | ] 236 | }, 237 | { 238 | "cell_type": "markdown", 239 | "metadata": { 240 | "id": "hmCaKvEasEq-" 241 | }, 242 | "source": [ 243 | "## Optimization\n", 244 | "\n", 245 | "Let’s now use one of the built-in PennyLane optimizers in order to carry out photon redirection. Since we wish to maximize the mean photon number of the second wire, we can define our cost function to minimize the negative of the circuit output." 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": 6, 251 | "metadata": { 252 | "id": "-0_4M0o3r_zs" 253 | }, 254 | "outputs": [], 255 | "source": [ 256 | "def cost(params):\n", 257 | " return -photon_redirection(params)" 258 | ] 259 | }, 260 | { 261 | "cell_type": "markdown", 262 | "metadata": { 263 | "id": "aoQnL2l0sKQ9" 264 | }, 265 | "source": [ 266 | "To begin our optimization, let’s choose the following small initial values of \n", 267 | "$θ$\n", 268 | " and \n", 269 | "$ϕ$\n", 270 | ":" 271 | ] 272 | }, 273 | { 274 | "cell_type": "code", 275 | "execution_count": 7, 276 | "metadata": { 277 | "colab": { 278 | "base_uri": "https://localhost:8080/" 279 | }, 280 | "id": "QsFqCx7UsI5I", 281 | "outputId": "a0ada891-c7e4-446c-a312-b952e7809149" 282 | }, 283 | "outputs": [ 284 | { 285 | "name": "stdout", 286 | "output_type": "stream", 287 | "text": [ 288 | "-9.999666671111081e-05\n" 289 | ] 290 | } 291 | ], 292 | "source": [ 293 | "init_params = np.array([0.01, 0.01], requires_grad=True)\n", 294 | "print(cost(init_params))" 295 | ] 296 | }, 297 | { 298 | "cell_type": "markdown", 299 | "metadata": { 300 | "id": "hSLjDu2ysQWI" 301 | }, 302 | "source": [ 303 | "Here, we choose the values of $\\theta$ and $\\phi$ to be very close to zero, this results in $B(\\theta, \\phi) \\sim I$, and the output of the quantum circuit will be very close to $\\left|1, 0\\right\\rangle$ -i.e , the circuit leaves the photons in the first mode.\n", 304 | "\n", 305 | "\n", 306 | "Why don't we choose $\\theta = 0$ and $\\phi = 0$?\n", 307 | "\n", 308 | "\n", 309 | "At this point in the parameter space $\\left\\langle \\hat{n}_1\\right\\rangle = 0$ and $\\frac{d}{d\\theta}\\left\\langle{\\hat{n}_1}\\right\\rangle|_{\\theta=0}=2\\sin\\theta\\cos\\theta|_{\\theta=0}=0$. \n", 310 | "Since the gradient is zero at those initial parameter values, the optimization algorithm would never descend from the maximum.\n", 311 | "\n", 312 | "\n", 313 | "This can also be verified directly using PennyLane:" 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": 8, 319 | "metadata": { 320 | "colab": { 321 | "base_uri": "https://localhost:8080/" 322 | }, 323 | "id": "ypIQrjJ7sOSH", 324 | "outputId": "0305a256-0b3c-4285-d558-55f925f44c2c" 325 | }, 326 | "outputs": [ 327 | { 328 | "name": "stdout", 329 | "output_type": "stream", 330 | "text": [ 331 | "[array(0.), array(0.)]\n" 332 | ] 333 | } 334 | ], 335 | "source": [ 336 | "dphoton_redirection = qml.grad(photon_redirection, argnum=0)\n", 337 | "print(dphoton_redirection([0.0, 0.0]))" 338 | ] 339 | }, 340 | { 341 | "cell_type": "markdown", 342 | "metadata": { 343 | "id": "l6jyfS3JtEp5" 344 | }, 345 | "source": [ 346 | "Now, let’s use the `GradientDescentOptimizer`, and update the circuit parameters over $100$ optimization steps" 347 | ] 348 | }, 349 | { 350 | "cell_type": "code", 351 | "execution_count": 9, 352 | "metadata": { 353 | "colab": { 354 | "base_uri": "https://localhost:8080/" 355 | }, 356 | "id": "n226G7ydtDMN", 357 | "outputId": "92f7ef4a-ec52-4940-c4ac-8aabc2121fb1" 358 | }, 359 | "outputs": [ 360 | { 361 | "name": "stdout", 362 | "output_type": "stream", 363 | "text": [ 364 | "Cost after step 5: -0.0349558\n", 365 | "Cost after step 10: -0.9969017\n", 366 | "Cost after step 15: -1.0000000\n", 367 | "Cost after step 20: -1.0000000\n", 368 | "Cost after step 25: -1.0000000\n", 369 | "Cost after step 30: -1.0000000\n", 370 | "Cost after step 35: -1.0000000\n", 371 | "Cost after step 40: -1.0000000\n", 372 | "Cost after step 45: -1.0000000\n", 373 | "Cost after step 50: -1.0000000\n", 374 | "Cost after step 55: -1.0000000\n", 375 | "Cost after step 60: -1.0000000\n", 376 | "Cost after step 65: -1.0000000\n", 377 | "Cost after step 70: -1.0000000\n", 378 | "Cost after step 75: -1.0000000\n", 379 | "Cost after step 80: -1.0000000\n", 380 | "Cost after step 85: -1.0000000\n", 381 | "Cost after step 90: -1.0000000\n", 382 | "Cost after step 95: -1.0000000\n", 383 | "Cost after step 100: -1.0000000\n", 384 | "Optimized rotation angles: [1.57079633 0.01 ]\n" 385 | ] 386 | } 387 | ], 388 | "source": [ 389 | "# initialise the optimizer\n", 390 | "opt = qml.GradientDescentOptimizer(stepsize=0.4)\n", 391 | "\n", 392 | "# set the number of steps\n", 393 | "steps = 100\n", 394 | "# set the initial parameter values\n", 395 | "params = init_params\n", 396 | "\n", 397 | "for i in range(steps):\n", 398 | " # update the circuit parameters\n", 399 | " params = opt.step(cost, params)\n", 400 | "\n", 401 | " if (i + 1) % 5 == 0:\n", 402 | " print(\"Cost after step {:5d}: {: .7f}\".format(i + 1, cost(params)))\n", 403 | "\n", 404 | "print(\"Optimized rotation angles: {}\".format(params))" 405 | ] 406 | }, 407 | { 408 | "cell_type": "markdown", 409 | "metadata": { 410 | "id": "0APjrVEMtNMo" 411 | }, 412 | "source": [ 413 | "Comparing this to the exact calculation above, this is close to the optimum value of $θ\n", 414 | "=\n", 415 | "π\n", 416 | "/\n", 417 | "2$\n", 418 | ", while the value of \n", 419 | "$ϕ$\n", 420 | " has not changed — consistent with the fact that \n", 421 | "$\\left\\langle \\hat{n}_1\\right\\rangle$\n", 422 | " is independent of \n", 423 | "$ϕ$\n", 424 | "." 425 | ] 426 | }, 427 | { 428 | "cell_type": "markdown", 429 | "metadata": { 430 | "id": "VaSlVmKwtVHe" 431 | }, 432 | "source": [ 433 | "## Hybrid Computation\n", 434 | "\n", 435 | "\n", 436 | "To really highlight the capabilities of PennyLane, let’s now combine the qubit-rotation QNode from the qubit rotation tutorial with the CV photon-redirection QNode from above, as well as some classical processing, to produce a truly hybrid computational model.\n", 437 | "\n", 438 | "First, we define a computation consisting of three steps: two quantum nodes (the qubit rotation and photon redirection circuits, running on the `'default.qubit'` and `'strawberryfields.fock'` devices, respectively), along with a classical function, that simply returns the squared difference of its two inputs using NumPy:\n" 439 | ] 440 | }, 441 | { 442 | "cell_type": "code", 443 | "execution_count": 10, 444 | "metadata": { 445 | "id": "qaXG0949tJlB" 446 | }, 447 | "outputs": [], 448 | "source": [ 449 | "# create the devices\n", 450 | "dev_qubit = qml.device(\"default.qubit\", wires=1)\n", 451 | "dev_fock = qml.device(\"strawberryfields.fock\", wires=2, cutoff_dim=10)\n", 452 | "\n", 453 | "\n", 454 | "@qml.qnode(dev_qubit)\n", 455 | "def qubit_rotation(phi1, phi2):\n", 456 | " \"\"\"Qubit rotation QNode\"\"\"\n", 457 | " qml.RX(phi1, wires=0)\n", 458 | " qml.RY(phi2, wires=0)\n", 459 | " return qml.expval(qml.PauliZ(0))\n", 460 | "\n", 461 | "\n", 462 | "@qml.qnode(dev_fock, diff_method=\"parameter-shift\")\n", 463 | "def photon_redirection(params):\n", 464 | " \"\"\"The photon redirection QNode\"\"\"\n", 465 | " qml.FockState(1, wires=0)\n", 466 | " qml.Beamsplitter(params[0], params[1], wires=[0, 1])\n", 467 | " return qml.expval(qml.NumberOperator(1))\n", 468 | "\n", 469 | "\n", 470 | "def squared_difference(x, y):\n", 471 | " \"\"\"Classical node to compute the squared\n", 472 | " difference between two inputs\"\"\"\n", 473 | " return np.abs(x - y) ** 2" 474 | ] 475 | }, 476 | { 477 | "cell_type": "markdown", 478 | "metadata": { 479 | "id": "kggEcb6Btgji" 480 | }, 481 | "source": [ 482 | "Now, we can define an objective function associated with the optimization, linking together our three subcomponents. Here, we wish to perform the following hybrid quantum-classical optimization:\n", 483 | "\n", 484 | "\n", 485 | "\n", 486 | "![alt text](https://pennylane.ai/qml/_images/hybrid_graph.png)\n", 487 | "\n", 488 | "\n", 489 | "\n", 490 | "\n", 491 | "\n", 492 | "1. The qubit-rotation circuit will contain fixed rotation angles $ϕ_1$\n", 493 | " and \n", 494 | "$ϕ_2$.\n", 495 | "\n", 496 | "2. The photon-redirection circuit will contain two free parameters, the beamsplitter angles \n", 497 | "$θ$\n", 498 | " and \n", 499 | "$ϕ$\n", 500 | ", which are to be optimized.\n", 501 | "\n", 502 | "\n", 503 | "3. The outputs of both QNodes will then be fed into the classical node, returning the squared difference of the two quantum functions.\n", 504 | "\n", 505 | "4. Finally, the optimizer will calculate the gradient of the entire computation with respect to the free parameters \n", 506 | "$θ$\n", 507 | " and \n", 508 | "$ϕ$\n", 509 | ", and update their values\n", 510 | "\n", 511 | "\n", 512 | "In essence, we are optimizing the photon-redirection circuit to return the **same expectation value** as the qubit-rotation circuit, even though they are two completely independent quantum systems.\n", 513 | "\n", 514 | "\n", 515 | "We can translate this computational graph to the following function, which combines the three nodes into a single hybrid computation. Below, we choose default values $ϕ_1=0.5\n", 516 | ", \n", 517 | "ϕ_\n", 518 | "2\n", 519 | "=\n", 520 | "0.1\n", 521 | "$:\n", 522 | "\n", 523 | "\n", 524 | "\n" 525 | ] 526 | }, 527 | { 528 | "cell_type": "code", 529 | "execution_count": 11, 530 | "metadata": { 531 | "id": "kg2ceRpcte0B" 532 | }, 533 | "outputs": [], 534 | "source": [ 535 | "def cost(params, phi1=0.5, phi2=0.1):\n", 536 | " \"\"\"Returns the squared difference between\n", 537 | " the photon-redirection and qubit-rotation QNodes, for\n", 538 | " fixed values of the qubit rotation angles phi1 and phi2\"\"\"\n", 539 | " qubit_result = qubit_rotation(phi1, phi2)\n", 540 | " photon_result = photon_redirection(params)\n", 541 | " return squared_difference(qubit_result, photon_result)" 542 | ] 543 | }, 544 | { 545 | "cell_type": "markdown", 546 | "metadata": { 547 | "id": "avlZ9l2WuBtQ" 548 | }, 549 | "source": [ 550 | "Now, we use the built-in GradientDescentOptimizer to perform the optimization for $100$ steps. As before, we choose initial beamsplitter parameters of \n", 551 | "$θ\n", 552 | "=\n", 553 | "0.01\n", 554 | ", \n", 555 | "ϕ\n", 556 | "=\n", 557 | "0.01\n", 558 | "$." 559 | ] 560 | }, 561 | { 562 | "cell_type": "code", 563 | "execution_count": 12, 564 | "metadata": { 565 | "colab": { 566 | "base_uri": "https://localhost:8080/" 567 | }, 568 | "id": "cEm--OpNuAMY", 569 | "outputId": "744e42b5-3024-49f1-ac99-da53deb80091" 570 | }, 571 | "outputs": [ 572 | { 573 | "name": "stdout", 574 | "output_type": "stream", 575 | "text": [ 576 | "Cost after step 5: 0.2154539\n", 577 | "Cost after step 10: 0.0000982\n", 578 | "Cost after step 15: 0.0000011\n", 579 | "Cost after step 20: 0.0000000\n", 580 | "Cost after step 25: 0.0000000\n", 581 | "Cost after step 30: 0.0000000\n", 582 | "Cost after step 35: 0.0000000\n", 583 | "Cost after step 40: 0.0000000\n", 584 | "Cost after step 45: 0.0000000\n", 585 | "Cost after step 50: 0.0000000\n", 586 | "Cost after step 55: 0.0000000\n", 587 | "Cost after step 60: 0.0000000\n", 588 | "Cost after step 65: 0.0000000\n", 589 | "Cost after step 70: 0.0000000\n", 590 | "Cost after step 75: 0.0000000\n", 591 | "Cost after step 80: 0.0000000\n", 592 | "Cost after step 85: 0.0000000\n", 593 | "Cost after step 90: 0.0000000\n", 594 | "Cost after step 95: 0.0000000\n", 595 | "Cost after step 100: 0.0000000\n", 596 | "Optimized rotation angles: [1.20671364 0.01 ]\n" 597 | ] 598 | } 599 | ], 600 | "source": [ 601 | "# initialise the optimizer\n", 602 | "opt = qml.GradientDescentOptimizer(stepsize=0.4)\n", 603 | "\n", 604 | "# set the number of steps\n", 605 | "steps = 100\n", 606 | "# set the initial parameter values\n", 607 | "params = np.array([0.01, 0.01], requires_grad=True)\n", 608 | "\n", 609 | "for i in range(steps):\n", 610 | " # update the circuit parameters\n", 611 | " params = opt.step(cost, params)\n", 612 | "\n", 613 | " if (i + 1) % 5 == 0:\n", 614 | " print(\"Cost after step {:5d}: {: .7f}\".format(i + 1, cost(params)))\n", 615 | "\n", 616 | "print(\"Optimized rotation angles: {}\".format(params))\n" 617 | ] 618 | }, 619 | { 620 | "cell_type": "markdown", 621 | "metadata": { 622 | "id": "3cQsnLZmuLT1" 623 | }, 624 | "source": [ 625 | "Substituting this into the photon redirection QNode shows that it now produces the same output as the qubit rotation QNode:" 626 | ] 627 | }, 628 | { 629 | "cell_type": "code", 630 | "execution_count": 13, 631 | "metadata": { 632 | "colab": { 633 | "base_uri": "https://localhost:8080/" 634 | }, 635 | "id": "o0uGkg2auIsF", 636 | "outputId": "351170fe-520c-48ee-9430-28c730e3162c" 637 | }, 638 | "outputs": [ 639 | { 640 | "name": "stdout", 641 | "output_type": "stream", 642 | "text": [ 643 | "0.8731983021146449\n", 644 | "0.8731983044562817\n" 645 | ] 646 | } 647 | ], 648 | "source": [ 649 | "result = [1.20671364, 0.01]\n", 650 | "print(photon_redirection(result))\n", 651 | "print(qubit_rotation(0.5, 0.1))" 652 | ] 653 | }, 654 | { 655 | "cell_type": "markdown", 656 | "metadata": { 657 | "id": "hTrDAM2nuPnT" 658 | }, 659 | "source": [ 660 | "This is just a simple example of the kind of hybrid computation that can be carried out in PennyLane. Quantum nodes (bound to different devices) and classical functions can be combined in many different and interesting ways." 661 | ] 662 | } 663 | ], 664 | "metadata": { 665 | "colab": { 666 | "authorship_tag": "ABX9TyM9UE2Mc61HUiOK+73FZsQk", 667 | "include_colab_link": true, 668 | "provenance": [] 669 | }, 670 | "kernelspec": { 671 | "display_name": "Python 3", 672 | "name": "python3" 673 | }, 674 | "language_info": { 675 | "name": "python", 676 | "version": "3.12.2" 677 | } 678 | }, 679 | "nbformat": 4, 680 | "nbformat_minor": 0 681 | } 682 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Noisy_Cricuits.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "provenance": [], 7 | "authorship_tag": "ABX9TyNhc3I8GpNpWFhgnZbyT5OT", 8 | "include_colab_link": true 9 | }, 10 | "kernelspec": { 11 | "name": "python3", 12 | "display_name": "Python 3" 13 | }, 14 | "language_info": { 15 | "name": "python" 16 | } 17 | }, 18 | "cells": [ 19 | { 20 | "cell_type": "markdown", 21 | "metadata": { 22 | "id": "view-in-github", 23 | "colab_type": "text" 24 | }, 25 | "source": [ 26 | "\"Open" 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "source": [ 32 | "# Noisy Circuits\n", 33 | "\n", 34 | "Here you'll learn how to simulate noisy circuits using built-in functionallity in Pennylane.\n", 35 | "\n", 36 | "\n", 37 | "We'll go through the basics of the noisy channels and density matrices, then use example code to simulate noisy circuits. \n", 38 | "\n", 39 | "\n", 40 | "We're going to put the $N$ in the $NISQ$\n", 41 | "\n", 42 | "![alt text](https://pennylane.ai/qml/_images/N-Nisq.png)\n", 43 | "\n", 44 | "\n", 45 | "\n" 46 | ], 47 | "metadata": { 48 | "id": "m7mkvt_cTA7b" 49 | } 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "source": [ 54 | "# Noisy Operations\n", 55 | "\n", 56 | "Noise is any unwanted transformation that corrupts the intended output of a quantum computation. It can be separated into two categories:\n", 57 | "\n", 58 | "* **Coherent Noise** : Described by unitary operations that maintain the purity of the output quantum state. A common source are systematic errors originating from imperfectly-calibrated devices that do not exactly apply the desired gates, e.g., applying a rotation by an angle \n", 59 | "$ϕ\n", 60 | "+\n", 61 | "ϵ$\n", 62 | " instead of \n", 63 | "$ϕ$\n", 64 | ".\n", 65 | "\n", 66 | "\n", 67 | "\n", 68 | "* **Incoherent Noise** : It originates from a quantum computer becoming entangled with the environment, resulting in mixed states — probability distributions over different pure states. Incoherent noise thus leads to outputs that are always random, regardless of what basis we measure in.\n", 69 | "\n", 70 | "\n", 71 | "\n", 72 | "\n", 73 | "\n", 74 | "-----\n", 75 | "\n", 76 | "\n", 77 | "Mixed states are described by [density matrices](https://en.wikipedia.org/wiki/Density_matrices). They provide a more general method of describing quantum states that elegantly encodes a distribution over pure states in a single mathematical object. Mixed states are the most general description of a quantum state, of which the pure states are a special case.\n", 78 | "\n", 79 | "\n", 80 | "The purpose of `default.mixed` device is to provide native support for mixed states and for simulation noisy computations, Let's use `deafult.mixed` to simulate a simple circuit for preparing the Bell state $|\\psi\\rangle=\\frac{1}{\\sqrt{2}}(|00\\rangle+|11\\rangle)$. We then return the expectation value for $Z_0\\otimes Z_1$" 81 | ], 82 | "metadata": { 83 | "id": "6tZQGB1KTdtA" 84 | } 85 | }, 86 | { 87 | "cell_type": "code", 88 | "source": [ 89 | "import pennylane as qml\n", 90 | "from pennylane import numpy as np" 91 | ], 92 | "metadata": { 93 | "id": "YVIivFEtS9sq" 94 | }, 95 | "execution_count": 3, 96 | "outputs": [] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "source": [ 101 | "dev = qml.device('default.mixed', wires = 2)\n", 102 | "\n", 103 | "\n", 104 | "@qml.qnode(dev)\n", 105 | "def circuit():\n", 106 | " qml.Hadamard(wires = 0)\n", 107 | " qml.CNOT(wires = [0,1])\n", 108 | "\n", 109 | " return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))\n", 110 | "\n", 111 | "\n", 112 | "\n", 113 | "print(f\"QNode output = {circuit() :.4f}\")\n" 114 | ], 115 | "metadata": { 116 | "colab": { 117 | "base_uri": "https://localhost:8080/" 118 | }, 119 | "id": "5_AnOjUOTAN8", 120 | "outputId": "e054e439-16fe-4150-eaae-b71df1227e96" 121 | }, 122 | "execution_count": 4, 123 | "outputs": [ 124 | { 125 | "output_type": "stream", 126 | "name": "stdout", 127 | "text": [ 128 | "QNode output = 1.0000\n" 129 | ] 130 | } 131 | ] 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "source": [ 136 | "The device stors the output state as a density matrix. In this case the density matrix is equal to $|\\psi\\rangle\\langle\\psi|$ where $|\\psi\\rangle=\\frac{1}{\\sqrt{2}}(|00\\rangle + |11\\rangle)$" 137 | ], 138 | "metadata": { 139 | "id": "1wdq02BcVO5K" 140 | } 141 | }, 142 | { 143 | "cell_type": "code", 144 | "source": [ 145 | "print(f\"Output state is = \\n{np.real(dev.state)}\")" 146 | ], 147 | "metadata": { 148 | "colab": { 149 | "base_uri": "https://localhost:8080/" 150 | }, 151 | "id": "0X9SVgiWVKq_", 152 | "outputId": "cb67b303-552e-4e6a-d546-63ad8855455d" 153 | }, 154 | "execution_count": 5, 155 | "outputs": [ 156 | { 157 | "output_type": "stream", 158 | "name": "stdout", 159 | "text": [ 160 | "Output state is = \n", 161 | "[[0.5 0. 0. 0.5]\n", 162 | " [0. 0. 0. 0. ]\n", 163 | " [0. 0. 0. 0. ]\n", 164 | " [0.5 0. 0. 0.5]]\n" 165 | ] 166 | } 167 | ] 168 | }, 169 | { 170 | "cell_type": "markdown", 171 | "source": [ 172 | "Incoherent noise is modelled by quantum channels. Mathematically, a quantum channel is linear, completly positive and trace-preserving ([CPTP](https://www.quantiki.org/wiki/channel-cp-map)) map. A convenient strategy for representing quantum channels is to employ [Kraus Operators](https://en.wikipedia.org/wiki/Quantum_operation#Kraus_operators) $\\{K_i\\}$ satisfying the condition $\\sum_i K_{i}^{\\dagger} K_i = I$. For an initial state $\\rho$ the output state after the action of a channel $\\Phi$ is: \n", 173 | "\n", 174 | "$$\\Phi(\\rho) = \\sum_i K_i \\rho K_{i}^{\\dagger}.$$\n", 175 | "\n", 176 | "\n", 177 | "Just like pure states are special cases of mixed states, unitary transformations are special cases of quantum channels. Unitary transformations are represented by a single Kraus operator, the unitary $U$\n", 178 | ", and they transform a state as $U\\rho U^\\dagger$\n", 179 | "\n", 180 | "\n", 181 | "\n", 182 | "------\n", 183 | "\n", 184 | "More generally, the action of a quantum channel can be interpreted as applying a transformation corresponding to the Kraus operator $K_i$ with some associated probability. More precisely, the channel applies the transformation $\\frac{1}{p_i}K_i\\rho K_i^\\dagger$ with probability 4p_i = \\text{Tr}[K_i \\rho K_{i}^{\n", 185 | "\\dagger}]$. Quantum channels therefore represent a probabilty distribution over different possible transformations on a quantum state. For example, consider the bit flip channel. It describes a transformation that flips the state of a qubit (applies an X gate) with probability $p$ and leaves it unchanged with probability $1-p$. Its Kraus operators are: \n", 186 | "\n", 187 | "$$\\begin{split}K_0 &= \\sqrt{1-p}\\begin{pmatrix}1 & 0\\\\ 0 & 1\\end{pmatrix}, \\\\\n", 188 | "K_1 &= \\sqrt{p}\\begin{pmatrix}0 & 1\\\\ 1 & 0\\end{pmatrix}.\\end{split}$$\n", 189 | "\n", 190 | "This channel can be implemented in PennyLane using the `qml.BitFlip` operation.\n", 191 | "\n", 192 | "\n", 193 | "-----\n", 194 | "\n", 195 | "Let's see what happens when we simulate this type of noise acting on both qubits in the circuit. We'll evaluate the QNode for different bit flip probablities.\n", 196 | "\n" 197 | ], 198 | "metadata": { 199 | "id": "iU3SgetLVgz3" 200 | } 201 | }, 202 | { 203 | "cell_type": "code", 204 | "source": [ 205 | "@qml.qnode(dev)\n", 206 | "def bitflip_circuit(p):\n", 207 | " qml.Hadamard(wires=0)\n", 208 | " qml.CNOT(wires=[0, 1])\n", 209 | " qml.BitFlip(p, wires=0)\n", 210 | " qml.BitFlip(p, wires=1)\n", 211 | " return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))\n", 212 | "\n", 213 | "\n", 214 | "ps = [0.001, 0.01, 0.1, 0.2]\n", 215 | "for p in ps:\n", 216 | " print(f\"QNode output for bit flip probability {p} is {bitflip_circuit(p):.4f}\")" 217 | ], 218 | "metadata": { 219 | "colab": { 220 | "base_uri": "https://localhost:8080/" 221 | }, 222 | "id": "at-azGLxVfMt", 223 | "outputId": "5226c85f-c7ad-4533-914d-cafee5b2e8b6" 224 | }, 225 | "execution_count": 6, 226 | "outputs": [ 227 | { 228 | "output_type": "stream", 229 | "name": "stdout", 230 | "text": [ 231 | "QNode output for bit flip probability 0.001 is 0.9960\n", 232 | "QNode output for bit flip probability 0.01 is 0.9604\n", 233 | "QNode output for bit flip probability 0.1 is 0.6400\n", 234 | "QNode output for bit flip probability 0.2 is 0.3600\n" 235 | ] 236 | } 237 | ] 238 | }, 239 | { 240 | "cell_type": "markdown", 241 | "source": [ 242 | "The circuit behaves quite differently in the presence of noise! This will be familiar to anyone that has run an algorithm on quantum hardware. It is also highlights why error mitigation and error correction are so important. We can use PennyLane to look under the hood and see the output state of the circuit for the largest noise parameter" 243 | ], 244 | "metadata": { 245 | "id": "UYyEly5JX9kc" 246 | } 247 | }, 248 | { 249 | "cell_type": "code", 250 | "source": [ 251 | "print(f\"Output state for bit flip probability {p} is \\n{np.real(dev.state)}\")\n" 252 | ], 253 | "metadata": { 254 | "colab": { 255 | "base_uri": "https://localhost:8080/" 256 | }, 257 | "id": "BGSqw33GX6tg", 258 | "outputId": "946c0b47-e766-4235-d2dc-e8d8c5c83bd6" 259 | }, 260 | "execution_count": 7, 261 | "outputs": [ 262 | { 263 | "output_type": "stream", 264 | "name": "stdout", 265 | "text": [ 266 | "Output state for bit flip probability 0.2 is \n", 267 | "[[0.34 0. 0. 0.34]\n", 268 | " [0. 0.16 0.16 0. ]\n", 269 | " [0. 0.16 0.16 0. ]\n", 270 | " [0.34 0. 0. 0.34]]\n" 271 | ] 272 | } 273 | ] 274 | }, 275 | { 276 | "cell_type": "markdown", 277 | "source": [ 278 | "Besides the bit flip channel, PennyLane supports several other noisy channels that are commonly used to describe experimental imperfections: `PhaseFlip`, `AmplitudeDamping`, `GeneralizedAmplitudeDamping`, `PhaseDamping`, and the `DepolarizingChannel`. You can also build your own custom channel using the operation `QubitChannel` by specifying its Kraus operators.\n", 279 | "\n", 280 | "Let’s take a look at another example. The depolarizing channel is a generalization of the bit flip and phase flip channels, where each of the three possible Pauli errors can be applied to a single qubit. Its Kraus operators are given by:\n", 281 | "\n", 282 | "\n", 283 | "\n", 284 | "$$\\begin{split}K_0 &= \\sqrt{1-p}\\begin{pmatrix}1 & 0\\\\ 0 & 1\\end{pmatrix}, \\\\\n", 285 | "K_1 &= \\sqrt{p/3}\\begin{pmatrix}0 & 1\\\\ 1 & 0\\end{pmatrix}, \\\\\n", 286 | "K_2 &= \\sqrt{p/3}\\begin{pmatrix}0 & -i\\\\ i & 0\\end{pmatrix}, \\\\\n", 287 | "K_3 &= \\sqrt{p/3}\\begin{pmatrix}1 & 0\\\\ 0 & -1\\end{pmatrix}.\\end{split}$$\n", 288 | "\n", 289 | "\n", 290 | "\n", 291 | "A circuit modelling the effect of depolarizing noise in preparing a Bell state is implemented below\n" 292 | ], 293 | "metadata": { 294 | "id": "UmdcZ27jYDQC" 295 | } 296 | }, 297 | { 298 | "cell_type": "code", 299 | "source": [ 300 | "@qml.qnode(dev)\n", 301 | "def depolarizing_circuit(p):\n", 302 | " qml.Hadamard(wires=0)\n", 303 | " qml.CNOT(wires=[0, 1])\n", 304 | " qml.DepolarizingChannel(p, wires=0)\n", 305 | " qml.DepolarizingChannel(p, wires=1)\n", 306 | " return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))\n", 307 | "\n", 308 | "\n", 309 | "ps = [0.001, 0.01, 0.1, 0.2]\n", 310 | "for p in ps:\n", 311 | " print(f\"QNode output for depolarizing probability {p} is {depolarizing_circuit(p):.4f}\")" 312 | ], 313 | "metadata": { 314 | "colab": { 315 | "base_uri": "https://localhost:8080/" 316 | }, 317 | "id": "QROWlRqeYAIl", 318 | "outputId": "46dd3a56-ffb5-4c39-eb73-7c7027460b47" 319 | }, 320 | "execution_count": 8, 321 | "outputs": [ 322 | { 323 | "output_type": "stream", 324 | "name": "stdout", 325 | "text": [ 326 | "QNode output for depolarizing probability 0.001 is 0.9973\n", 327 | "QNode output for depolarizing probability 0.01 is 0.9735\n", 328 | "QNode output for depolarizing probability 0.1 is 0.7511\n", 329 | "QNode output for depolarizing probability 0.2 is 0.5378\n" 330 | ] 331 | } 332 | ] 333 | }, 334 | { 335 | "cell_type": "markdown", 336 | "source": [ 337 | "As before, the output deviates from the desired value as the amount of noise increases. Modelling the noise that occurs in real experiments requires careful consideration. PennyLane offers the flexibility to experiment with different combinations of noisy channels to either mimic the performance of quantum algorithms when deployed on real devices, or to explore the effect of more general quantum transformations." 338 | ], 339 | "metadata": { 340 | "id": "dKIbCLQ6Ydxm" 341 | } 342 | }, 343 | { 344 | "cell_type": "markdown", 345 | "source": [ 346 | "# Channel gradients\n", 347 | "\n", 348 | "The ability to compute gradients of any operation is an essential ingredient of **quantum differentiable programming**. In PennyLane, it is possible to compute gradients of noisy channels and optimize them inside variational circuits. PennyLane supports analytical gradients for channels whose Kraus operators are proportional to unitary matrices 1. In other cases, gradients are evaluated using finite differences.\n", 349 | "\n", 350 | "To illustrate this property, we’ll consider an elementary example. We aim to learn the noise parameters of a circuit in order to reproduce an observed expectation value. So suppose that we run the circuit to prepare a Bell state on a hardware device and observe that the expectation value of $Z_0\\otimes Z_1$ s not equal to $1$ (as would occur with an ideal device), but instead has the value $0.7781$. In the experiment, it is known that the major source of noise is amplitude damping, for example as a result of photon loss. Amplitude damping projects a state to \n", 351 | "$|\n", 352 | "0\n", 353 | "⟩$\n", 354 | " with probability \n", 355 | "$p$\n", 356 | " and otherwise leaves it unchanged. It is described by the Kraus operators\n", 357 | "\n", 358 | "$$\\begin{split}K_0 = \\begin{pmatrix}1 & 0\\\\ 0 & \\sqrt{1-p}\\end{pmatrix}, \\quad\n", 359 | "K_1 = \\begin{pmatrix}0 & \\sqrt{p}\\\\ 0 & 0\\end{pmatrix}.\\end{split}$$\n", 360 | "\n", 361 | "What damping parameter (\n", 362 | "$p$\n", 363 | ") explains the experimental outcome? We can answer this question by optimizing the channel parameters to reproduce the experimental observation! 💪 Since the parameter \n", 364 | "$p$\n", 365 | " is a probability, we use a sigmoid function to ensure that the trainable parameters give rise to a valid channel parameter, i.e., a number between $0$ and $1$.\n" 366 | ], 367 | "metadata": { 368 | "id": "Qq2gJ2VdYf-Y" 369 | } 370 | }, 371 | { 372 | "cell_type": "code", 373 | "source": [ 374 | "ev = np.tensor(0.7781, requires_grad=False) # observed expectation value\n", 375 | "\n", 376 | "def sigmoid(x):\n", 377 | " return 1/(1+np.exp(-x))\n", 378 | "\n", 379 | "@qml.qnode(dev)\n", 380 | "def damping_circuit(x):\n", 381 | " qml.Hadamard(wires=0)\n", 382 | " qml.CNOT(wires=[0, 1])\n", 383 | " qml.AmplitudeDamping(sigmoid(x), wires=0) # p = sigmoid(x)\n", 384 | " qml.AmplitudeDamping(sigmoid(x), wires=1)\n", 385 | " return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))" 386 | ], 387 | "metadata": { 388 | "id": "_Xtkg-3SYcHV" 389 | }, 390 | "execution_count": 9, 391 | "outputs": [] 392 | }, 393 | { 394 | "cell_type": "markdown", 395 | "source": [ 396 | "We optimize the circuit with respect to a simple cost function that attains its minimum when the output of the QNode is equal to the experimental value:" 397 | ], 398 | "metadata": { 399 | "id": "K8-fdmhQZBZG" 400 | } 401 | }, 402 | { 403 | "cell_type": "code", 404 | "source": [ 405 | "def cost(x, target):\n", 406 | " return (damping_circuit(x) - target)**2" 407 | ], 408 | "metadata": { 409 | "id": "2rGLvOj2Y_lY" 410 | }, 411 | "execution_count": 10, 412 | "outputs": [] 413 | }, 414 | { 415 | "cell_type": "markdown", 416 | "source": [ 417 | "All that remains is to optimize the parameter. We use a straightforward gradient descent method." 418 | ], 419 | "metadata": { 420 | "id": "mKJSzIApZHDT" 421 | } 422 | }, 423 | { 424 | "cell_type": "code", 425 | "source": [ 426 | "opt = qml.GradientDescentOptimizer(stepsize=10)\n", 427 | "steps = 35\n", 428 | "x = np.tensor(0.0, requires_grad=True)\n", 429 | "\n", 430 | "for i in range(steps):\n", 431 | " (x, ev), cost_val = opt.step_and_cost(cost, x, ev)\n", 432 | " if i % 5 == 0 or i == steps - 1:\n", 433 | " print(f\"Step: {i} Cost: {cost_val}\")\n", 434 | "\n", 435 | "print(f\"QNode output after optimization = {damping_circuit(x):.4f}\")\n", 436 | "print(f\"Experimental expectation value = {ev}\")\n", 437 | "print(f\"Optimized noise parameter p = {sigmoid(x.take(0)):.4f}\")" 438 | ], 439 | "metadata": { 440 | "colab": { 441 | "base_uri": "https://localhost:8080/" 442 | }, 443 | "id": "wj7eB-ErZFYN", 444 | "outputId": "aaafd5be-e121-4c52-82e3-744f5f02dc7d" 445 | }, 446 | "execution_count": 11, 447 | "outputs": [ 448 | { 449 | "output_type": "stream", 450 | "name": "stderr", 451 | "text": [ 452 | "/usr/local/lib/python3.7/dist-packages/autograd/numpy/numpy_wrapper.py:156: ComplexWarning: Casting complex values to real discards the imaginary part\n", 453 | " return A.astype(dtype, order, casting, subok, copy)\n" 454 | ] 455 | }, 456 | { 457 | "output_type": "stream", 458 | "name": "stdout", 459 | "text": [ 460 | "Step: 0 Cost: 0.07733961000000007\n", 461 | "Step: 5 Cost: 0.07733961000000007\n", 462 | "Step: 10 Cost: 0.07733961000000007\n", 463 | "Step: 15 Cost: 0.07733961000000007\n", 464 | "Step: 20 Cost: 0.07733961000000007\n", 465 | "Step: 25 Cost: 0.07733961000000007\n", 466 | "Step: 30 Cost: 0.07733961000000007\n", 467 | "Step: 34 Cost: 0.07733961000000007\n", 468 | "QNode output after optimization = 0.5000\n", 469 | "Experimental expectation value = 0.7781\n", 470 | "Optimized noise parameter p = 0.5000\n" 471 | ] 472 | } 473 | ] 474 | }, 475 | { 476 | "cell_type": "markdown", 477 | "source": [ 478 | "We’ve trained the noisy channel to reproduce the experimental observation. 😎\n", 479 | "\n", 480 | "Developing quantum algorithms that leverage the power of NISQ devices requires serious consideration of the effects of noise. With PennyLane, you have access to tools that can help you design, simulate, and optimize noisy quantum circuits. We look forward to seeing what the quantum community can achieve with them! 🚀 🎉 😸" 481 | ], 482 | "metadata": { 483 | "id": "ULzTq6gfZMs2" 484 | } 485 | } 486 | ] 487 | } -------------------------------------------------------------------------------- /Quantum_Gradients_with_Backpropagation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "provenance": [], 7 | "authorship_tag": "ABX9TyMBe5pYSRn8QeZfIr7HiN2C", 8 | "include_colab_link": true 9 | }, 10 | "kernelspec": { 11 | "name": "python3", 12 | "display_name": "Python 3" 13 | }, 14 | "language_info": { 15 | "name": "python" 16 | } 17 | }, 18 | "cells": [ 19 | { 20 | "cell_type": "markdown", 21 | "metadata": { 22 | "id": "view-in-github", 23 | "colab_type": "text" 24 | }, 25 | "source": [ 26 | "\"Open" 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "source": [ 32 | "# Quantum Gardients with BackPropagation\n", 33 | "\n", 34 | "Any quantum device, whether a hardware device or a simulator can be trained using the **parameter-shift rule**\n", 35 | "\n", 36 | "-----\n", 37 | "-----\n", 38 | "\n", 39 | "### Parameter-shift rule\n", 40 | "\n", 41 | "The output of a variational circuit (i.e the expectation of an observable) can be written as **quantum function** $f(\\theta)$ parameterized by $\\theta = \\theta_1 , \\theta_2...$\n", 42 | "\n", 43 | "The partial derivative of $f(\\theta)$ can in mnay cases be expressed as a linear combination of other quantum functions. Importantly, these other quantum functions typically use the same circuit, differing only in the shift of the argument. This means that partial derivatives of a variational circuit can be computed by using the same variational circuit architecture.\n", 44 | "\n", 45 | "Recipes of how to get partial derivatives by evaluated parameter-shifted instances of a variational circuit are called parameter-shift rules, and have been first introduced to quantum machine learning in [Mitarai et al. (2018)](https://arxiv.org/abs/1803.00745), and extended in [Schuld et al. (2018)](https://arxiv.org/abs/1811.11184).\n", 46 | "\n", 47 | "\n", 48 | "\n", 49 | "![alt text](https://pennylane.ai/qml/_images/gradients2.png)\n", 50 | "\n", 51 | "\n", 52 | "Making a rough analogy to classically computable functions, this is similar to how the derivative of the function $f(x) = \\sin(x)$ is identical to $\\frac{1}{2} \\sin(x +\\frac{\\pi}{2}) - \\frac{1}{2} \\sin(x - \\frac{\\pi}{2})$.\n", 53 | "\n", 54 | "So the same underlying algorithm can be reused to compute both $\\sin(x)$ and its derivative by evaluating at $x \\pm \\frac{\\pi}{2}$. This inituition holds for many quantum functions of interest : \n", 55 | "\n", 56 | "*the same circuit can be used to compute both the quantum function and the gradient of the quantum function.*" 57 | ], 58 | "metadata": { 59 | "id": "ouEWPALBZBsI" 60 | } 61 | }, 62 | { 63 | "cell_type": "markdown", 64 | "source": [ 65 | "## A more technical explaination\n", 66 | "\n", 67 | "Quantum Circuits are specified by sequence of gates. The unitary transformation carried out by the circuit thus be broken down into a product of unitaries:\n", 68 | "\n", 69 | "$$U(x; \\theta) = U_N(\\theta_{N}) U_{N-1}(\\theta_{N-1}) \\cdots U_i(\\theta_i) \\cdots U_1(\\theta_1) U_0(x).$$\n", 70 | "\n", 71 | "Each of these gates is unitary, and therefore must have the form $U_{j}(\\gamma_j)=\\exp{(i\\gamma_j H_j)}$ where $H_j$ is the Hermitian operator which generates the gate and $\\gamma_j$ is the gate parameter.\n", 72 | "\n", 73 | "\n", 74 | "\n" 75 | ], 76 | "metadata": { 77 | "id": "jihA9-Yla4qK" 78 | } 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "source": [ 83 | "## A single Parameterised gate\n", 84 | "\n", 85 | "Let us single out a single parameter $\\theta_i$ and its associated gate $U_i(\\theta_i)$, For simplicity we remove all gates except $U_i(\\theta)$ and $U_o(x)$ for the moment. In this case, we have a simplified quantum circuit function\n", 86 | "\n", 87 | "$$f(x; \\theta_i) = \\langle 0 | U_0^\\dagger(x)U_i^\\dagger(\\theta_i)\\hat{B}U_i(\\theta_i)U_0(x) | 0 \\rangle = \\langle x | U_i^\\dagger(\\theta_i)\\hat{B}U_i(\\theta_i) | x \\rangle.$$\n", 88 | "\n", 89 | "For convenience, we rewrite the unitary conjugation as a linear transformation $\\mathcal{M}_{\\theta_i}$ acting on the operator $\\hat{B}$\n", 90 | "\n", 91 | "$$U_i^\\dagger(\\theta_i)\\hat{B}U_i(\\theta_i) = \\mathcal{M}_{\\theta_i}(\\hat{B}).$$\n", 92 | "\n", 93 | "The transformation $\\mathcal{M}_{\\theta_i}$ , depends smoothly on the parameter $\\theta_i$, so this qunatum function will have a well-defined gardient:\n", 94 | "\n", 95 | "$$\\nabla_{\\theta_i}f(x; \\theta_i) = \\langle x | \\nabla_{\\theta_i}\\mathcal{M}_{\\theta_i}(\\hat{B}) | x \\rangle \\in \\mathbb{R}.$$\n", 96 | "\n", 97 | "The key insight is that we can, in many cases of interest, express this gradient as a linear combination of the same transformation $\\mathcal{M}$, but with different parameters. Namely,\n", 98 | "\n", 99 | "$$\\nabla_{\\theta_i}\\mathcal{M}_{\\theta_i}(\\hat{B}) = c[\\mathcal{M}_{\\theta_i + s}(\\hat{B}) - \\mathcal{M}_{\\theta_i - s}(\\hat{B})],$$\n", 100 | "\n", 101 | "where the multiplier $c$ and the shift $s$ are determined completely by the type of transformation $\\mathcal{M}$ and independent of the value of $\\theta_i$\n", 102 | "\n", 103 | "\n", 104 | "\n" 105 | ], 106 | "metadata": { 107 | "id": "e2_grtq2ba5U" 108 | } 109 | }, 110 | { 111 | "cell_type": "markdown", 112 | "source": [ 113 | "## Multiple Parameterized Gates\n", 114 | "\n", 115 | "To complete the story, we now go back to the case where there are many gates in the circuit. We can absorb any gates applied before gate i into the initial state:\n", 116 | "\n", 117 | "$$|\\psi_{i-1}\\rangle = U_{i-1}(\\theta_{i-1}) \\cdots U_{1}(\\theta_{1})U_{0}(x)|0\\rangle$$\n", 118 | "\n", 119 | "Similarly, any gates applied after gate $i$ are combined with the observable $\\hat{B}$:\n", 120 | "\n", 121 | "$$\\hat{B}_{i+1} = U_{N}^\\dagger(\\theta_{N}) \\cdots U_{i+1}^\\dagger(\\theta_{i+1}) \\hat{B} U_{i+1}(\\theta_{i+1}) \\cdots U_{N}(\\theta_{N}) $$\n", 122 | "\n", 123 | "With this simplification, the quantum circuit function becomes\n", 124 | "\n", 125 | "$$ f(x; \\theta) = \\langle \\psi_{i-1} | U_i^\\dagger(\\theta_i) \\hat{B}_{i+1} U_i(\\theta_i) | \\psi_{i-1} \\rangle = \\langle \\psi_{i-1} | \\mathcal{M}_{\\theta_i} (\\hat{B}_{i+1}) | \\psi_{i-1} \\rangle,$$\n", 126 | "\n", 127 | "and its gradient is \n", 128 | "\n", 129 | "$$ \\nabla_{\\theta_i}f(x; \\theta) = \\langle \\psi_{i-1} | \\nabla_{\\theta_i}\\mathcal{M}_{\\theta_i} (\\hat{B}_{i+1}) | \\psi_{i-1} \\rangle.$$\n", 130 | "\n", 131 | "This gradient has the exact same form as the single-gate case, except we modify the state $|x⟩ → |\\psi_{i-1}⟩$ and the measurement operator $\\hat{B}\\rightarrow\\hat{B}_{i+1}$. In terms of the circuit, this means we can leave all other gates as they are, and only modify gate $U(\\theta_i)$ when we want to differentiate with respect to the paramter $\\theta_i$\n", 132 | "\n", 133 | "------\n", 134 | "------\n" 135 | ], 136 | "metadata": { 137 | "id": "idveE9mCc0VS" 138 | } 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "source": [ 143 | "## Pauli gate example\n", 144 | "Consider a quantum computer with parameterized gates of the form,\n", 145 | "\n", 146 | "$$ U_i(\\theta_i)=\\exp\\left(-i\\tfrac{\\theta_i}{2}\\hat{P}_i\\right),$$\n", 147 | "\n", 148 | "where $\\hat{P}_i=\\hat{P}_i^\\dagger$ is a Pauli operator.\n", 149 | "\n", 150 | "The gradient of this unitary is \n", 151 | "\n", 152 | "$$\\nabla_{\\theta_i}U_i(\\theta_i) = -\\tfrac{i}{2}\\hat{P}_i U_i(\\theta_i) = -\\tfrac{i}{2}U_i(\\theta_i)\\hat{P}_i .$$\n", 153 | "\n", 154 | "Substituting this into the quantum circuit function $f(x;\\theta)$, we get\n", 155 | "\n", 156 | "$$\\begin{align}\n", 157 | " \\nabla_{\\theta_i}f(x; \\theta) = &\n", 158 | " \\frac{i}{2}\\langle \\psi_{i-1} | U_i^\\dagger(\\theta_i) \\left( P_i \\hat{B}_{i+1} - \\hat{B}_{i+1} P_i \\right) U_i(\\theta_i)| \\psi_{i-1} \\rangle \\\\\n", 159 | " = & \\frac{i}{2}\\langle \\psi_{i-1} | U_i^\\dagger(\\theta_i) \\left[P_i, \\hat{B}_{i+1}\\right]U_i(\\theta_i) | \\psi_{i-1} \\rangle,\n", 160 | "\\end{align}$$\n", 161 | "\n", 162 | "\n", 163 | "where $[X,Y] = XY-YX$ is the commutator.\n", 164 | "\n", 165 | "\n", 166 | "We now make use of the following mathematical identity for the commutators involving Pauli operators\n", 167 | "\n", 168 | "$$ \\left[ \\hat{P}_i, \\hat{B} \\right] = -i\\left(U_i^\\dagger\\left(\\tfrac{\\pi}{2}\\right)\\hat{B}U_i\\left(\\tfrac{\\pi}{2}\\right) - U_i^\\dagger\\left(-\\tfrac{\\pi}{2}\\right)\\hat{B}U_i\\left(-\\tfrac{\\pi}{2}\\right) \\right). $$\n", 169 | "\n", 170 | "Substituing this into the previous equation, we obtain the gradient expression\n", 171 | "\n", 172 | "$$ \\begin{align}\n", 173 | " \\nabla_{\\theta_i}f(x; \\theta) = & \\hphantom{-} \\tfrac{1}{2} \\langle \\psi_{i-1} | U_i^\\dagger\\left(\\theta_i + \\tfrac{\\pi}{2} \\right) \\hat{B}_{i+1} U_i\\left(\\theta_i + \\tfrac{\\pi}{2} \\right) | \\psi_{i-1} \\rangle \\\\\n", 174 | " & - \\tfrac{1}{2} \\langle \\psi_{i-1} | U_i^\\dagger\\left(\\theta_i - \\tfrac{\\pi}{2} \\right) \\hat{B}_{i+1} U_i\\left(\\theta_i - \\tfrac{\\pi}{2} \\right) | \\psi_{i-1} \\rangle.\n", 175 | "\\end{align}$$\n", 176 | "\n", 177 | "Finally, we can rewrite this in terms of quantum function:\n", 178 | "\n", 179 | "$$ \\nabla_{\\theta}f(x; \\theta) = \\tfrac{1}{2}\\left[ f(x; \\theta + \\tfrac{\\pi}{2}) - f(x; \\theta - \\tfrac{\\pi}{2}) \\right].$$\n", 180 | "\n", 181 | "\n", 182 | "\n" 183 | ], 184 | "metadata": { 185 | "id": "y8utPvYdeEHB" 186 | } 187 | }, 188 | { 189 | "cell_type": "markdown", 190 | "source": [ 191 | "------\n", 192 | "------\n", 193 | "\n", 194 | "## Gaussina gate example\n", 195 | "\n", 196 | "For a quantum device with continuous-valued operators, such as photonic quantum computers, it is convenient to employ the Heisenberg picture i.e to track how the gates $U_i(\\theta_i)$ transform the final measurement operator $\\hat{B}$\n", 197 | "\n", 198 | "As an example,we consider the [Squeezing gate](https://en.wikipedia.org/wiki/Squeeze_operator). In the Heisenberg picture, the Squeezing gate causes the quadrature operators $\\hat{x}$ and $\\hat{p}$ to become rescaled:\n", 199 | "\n", 200 | "$$\\begin{align}\n", 201 | " \\mathcal{M}^S_r(\\hat{x}) = & S^\\dagger(r)\\hat{x}S(r) \\\\\n", 202 | " = & e^{-r}\\hat{x}\n", 203 | "\\end{align}$$\n", 204 | "\n", 205 | "and \n", 206 | "\n", 207 | "$$\\begin{align}\n", 208 | " \\mathcal{M}^S_r(\\hat{p}) = & S^\\dagger(r)\\hat{p}S(r) \\\\\n", 209 | " = & e^{r}\\hat{p}.\n", 210 | "\\end{align} $$\n", 211 | "\n", 212 | "\n", 213 | "-----\n", 214 | "\n", 215 | "Expressing this in matrix notation, we have\n", 216 | "\n", 217 | "$$ \\begin{align}\n", 218 | " \\begin{bmatrix}\n", 219 | " \\hat{x} \\\\\n", 220 | " \\hat{p}\n", 221 | " \\end{bmatrix}\n", 222 | " \\rightarrow\n", 223 | " \\begin{bmatrix}\n", 224 | " e^{-r} & 0 \\\\\n", 225 | " 0 & e^r\n", 226 | " \\end{bmatrix}\n", 227 | " \\begin{bmatrix}\n", 228 | " \\hat{x} \\\\\n", 229 | " \\hat{p}\n", 230 | " \\end{bmatrix}.\n", 231 | "\\end{align}$$\n", 232 | "\n", 233 | "The gradient of this transformation can easily be found:\n", 234 | "\n", 235 | "$$\\begin{align}\n", 236 | " \\nabla_r\n", 237 | " \\begin{bmatrix}\n", 238 | " e^{-r} & 0 \\\\\n", 239 | " 0 & e^r\n", 240 | " \\end{bmatrix}\n", 241 | " =\n", 242 | " \\begin{bmatrix}\n", 243 | " -e^{-r} & 0 \\\\\n", 244 | " 0 & e^r\n", 245 | " \\end{bmatrix}.\n", 246 | "\\end{align} $$\n", 247 | "\n", 248 | "We notice that this can be rewritten as a linear combination of the squeeze operations:\n", 249 | "\n", 250 | "$$ \\begin{align}\n", 251 | " \\begin{bmatrix}\n", 252 | " -e^{-r} & 0 \\\\\n", 253 | " 0 & e^r\n", 254 | " \\end{bmatrix}\n", 255 | " =\n", 256 | " \\frac{1}{2\\sinh(s)}\n", 257 | " \\left(\n", 258 | " \\begin{bmatrix}\n", 259 | " e^{-(r+s)} & 0 \\\\\n", 260 | " 0 & e^{r+s}\n", 261 | " \\end{bmatrix}\n", 262 | " -\n", 263 | " \\begin{bmatrix}\n", 264 | " e^{-(r-s)} & 0 \\\\\n", 265 | " 0 & e^{r-s}\n", 266 | " \\end{bmatrix}\n", 267 | " \\right),\n", 268 | "\\end{align}$$\n", 269 | "\n", 270 | "where $s$ is an arbitrary nonzero shift.\n", 271 | "\n", 272 | "-----\n", 273 | " As before, assume that an input $y$ has already been embedded into a quantum state $|y\\rangle = U_0(y)|0\\rangle$ before we apply the squeeze gate. If we measure the $\\hat{x}$ operator, we will have the following quantum circuit function:\n", 274 | "\n", 275 | " $$f(y;r) = \\langle y | \\mathcal{M}^S_r (\\hat{x}) | y \\rangle. $$\n", 276 | "\n", 277 | "\n", 278 | "\n", 279 | "Finally, its gradient can be expressed as:\n", 280 | "\n", 281 | "\n", 282 | "$$\\begin{align}\n", 283 | " \\nabla_r f(y;r) = & \\frac{1}{2\\sinh(s)} \\left[\n", 284 | " \\langle y | \\mathcal{M}^S_{r+s} (\\hat{x}) | y \\rangle\n", 285 | " -\\langle y | \\mathcal{M}^S_{r-s} (\\hat{x}) | y \\rangle \\right] \\\\\n", 286 | " = & \\frac{1}{2\\sinh(s)}\\left[f(y; r+s) - f(y; r-s)\\right].\n", 287 | "\\end{align} $$" 288 | ], 289 | "metadata": { 290 | "id": "3gE0C378fJD0" 291 | } 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": null, 296 | "metadata": { 297 | "id": "5n7wp88dY9Ek" 298 | }, 299 | "outputs": [], 300 | "source": [] 301 | } 302 | ] 303 | } -------------------------------------------------------------------------------- /Qubit_Rotation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "provenance": [], 7 | "authorship_tag": "ABX9TyNpdtP2tmeNtfyduINrt7WY", 8 | "include_colab_link": true 9 | }, 10 | "kernelspec": { 11 | "name": "python3", 12 | "display_name": "Python 3" 13 | }, 14 | "language_info": { 15 | "name": "python" 16 | } 17 | }, 18 | "cells": [ 19 | { 20 | "cell_type": "markdown", 21 | "metadata": { 22 | "id": "view-in-github", 23 | "colab_type": "text" 24 | }, 25 | "source": [ 26 | "\"Open" 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "source": [ 32 | "## Qubit Rotation" 33 | ], 34 | "metadata": { 35 | "id": "bixomjaTwiZW" 36 | } 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "source": [ 41 | "### The Quantum Circuit\n", 42 | "\n", 43 | "---\n", 44 | "In the qubit rotation example, we wish to implement the following quantum circuit:\n", 45 | "\n", 46 | "![alt text](https://pennylane.ai/qml/_images/rotation_circuit.png)\n", 47 | "\n", 48 | "---\n", 49 | "Breaking this down step-by-step, we first start with a qubit in the ground state $|0\\rangle = \\begin{bmatrix}1 & 0 \\end{bmatrix}^T$\n", 50 | ", and rotate it around the x-axis by applying the gate:\n", 51 | "\n", 52 | "$$\\begin{split}R_x(\\phi_1) = e^{-i \\phi_1 \\sigma_x /2} =\n", 53 | "\\begin{bmatrix} \\cos \\frac{\\phi_1}{2} & -i \\sin \\frac{\\phi_1}{2} \\\\\n", 54 | " -i \\sin \\frac{\\phi_1}{2} & \\cos \\frac{\\phi_1}{2}\n", 55 | "\\end{bmatrix},\\end{split}$$\n", 56 | "\n", 57 | "\n", 58 | "and then around the y-axis via the gate:\n", 59 | "\n", 60 | "$$\\begin{split}R_y(\\phi_2) = e^{-i \\phi_2 \\sigma_y/2} =\n", 61 | "\\begin{bmatrix} \\cos \\frac{\\phi_2}{2} & - \\sin \\frac{\\phi_2}{2} \\\\\n", 62 | " \\sin \\frac{\\phi_2}{2} & \\cos \\frac{\\phi_2}{2}\n", 63 | "\\end{bmatrix}.\\end{split}$$\n", 64 | "\n", 65 | "After these operations the qubit is now in the state:\n", 66 | "\n", 67 | "$$| \\psi \\rangle = R_y(\\phi_2) R_x(\\phi_1) | 0 \\rangle$$\n", 68 | "\n", 69 | "Finally, we measure the expectation value $\\langle \\psi \\mid \\sigma_z \\mid \\psi \\rangle$ of the Pauli-Z operator\n", 70 | "\n", 71 | "$$\\begin{split}\\sigma_z =\n", 72 | "\\begin{bmatrix} 1 & 0 \\\\\n", 73 | " 0 & -1\n", 74 | "\\end{bmatrix}.\\end{split}$$\n", 75 | "\n", 76 | "\n", 77 | "Using the above calculate the exact expectation value, we find that\n", 78 | "\n", 79 | "$$\\langle \\psi \\mid \\sigma_z \\mid \\psi \\rangle\n", 80 | "= \\langle 0 \\mid R_x(\\phi_1)^\\dagger R_y(\\phi_2)^\\dagger \\sigma_z R_y(\\phi_2) R_x(\\phi_1) \\mid 0 \\rangle\n", 81 | "= \\cos(\\phi_1)\\cos(\\phi_2)$$\n", 82 | "\n", 83 | "\n", 84 | "Depending on the circuit parameters $\\phi_1$ and $\\phi_2$, the output expecatation lies between $1$ if $|\\psi\\rangle = |0\\rangle$ and $-1$ if $|\\psi⟩ = |1⟩$" 85 | ], 86 | "metadata": { 87 | "id": "SmQlKt0ywktA" 88 | } 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "source": [ 93 | "## Importing Pennylane and Numpy\n", 94 | "\n", 95 | "The first thing we need to do is import PennyLane, as well as the wrapped version of NumPy provided by PennyLane." 96 | ], 97 | "metadata": { 98 | "id": "lXYzgus3x59W" 99 | } 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": 3, 104 | "metadata": { 105 | "id": "-EJf-KDauS6m" 106 | }, 107 | "outputs": [], 108 | "source": [ 109 | "import pennylane as qml\n", 110 | "from pennylane import numpy as np" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "source": [ 116 | "#### Important\n", 117 | "\n", 118 | "When constructing a hybrid quantum/classical computational model with PennyLane, it is important to always import NumPy from PennyLane, not the standard NumPy!\n", 119 | "\n", 120 | "By importing the wrapped version of NumPy provided by PennyLane, you can combine the power of NumPy with PennyLane:\n", 121 | "\n", 122 | "* continue to use the classical NumPy functions and arrays you know and love\n", 123 | "\n", 124 | "* combine quantum functions (evaluated on quantum hardware/simulators) and classical functions (provided by NumPy)\n", 125 | "\n", 126 | "* allow PennyLane to automatically calculate gradients of both classical and quantum functions" 127 | ], 128 | "metadata": { 129 | "id": "22CjZdL3yfk7" 130 | } 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "source": [ 135 | "## Creating a device\n", 136 | "\n", 137 | "Before we can construct our quantum node, we need to initialize a device.\n", 138 | "\n", 139 | "-----\n", 140 | "-----\n", 141 | "\n", 142 | " **Definition**\n", 143 | "\n", 144 | "Any computational object that can apply quantum operations and return a measurement value is called a quantum device.\n", 145 | "\n", 146 | "\n", 147 | "In PennyLane, a device could be a hardware device (such as the IBM QX4, via the PennyLane-PQ plugin), or a software simulator (such as Strawberry Fields, via the PennyLane-SF plugin).\n", 148 | "\n", 149 | "\n", 150 | "\n", 151 | "``` Device are loaded in Pennylane via the function device()```\n", 152 | "\n", 153 | "\n", 154 | "For this tutorial, we are using qubit model, so let's initialize the `default.qubit` device provided" 155 | ], 156 | "metadata": { 157 | "id": "WTWvl2cuys2U" 158 | } 159 | }, 160 | { 161 | "cell_type": "code", 162 | "source": [ 163 | "dev1 = qml.device(\"default.qubit\", wires=1)" 164 | ], 165 | "metadata": { 166 | "id": "3b0vxroIyCp4" 167 | }, 168 | "execution_count": 4, 169 | "outputs": [] 170 | }, 171 | { 172 | "cell_type": "markdown", 173 | "source": [ 174 | "For all devices, `device()` accepts the following arguments:\n", 175 | "\n", 176 | "1. `name` : the name of the device to be loaded\n", 177 | "2. `wires` : the number of subsystems to initialize the devices with\n", 178 | "\n", 179 | "\n", 180 | "Here, as we only require a single qubit for this example, we set `wires=1`" 181 | ], 182 | "metadata": { 183 | "id": "bTeOt0v5zMBl" 184 | } 185 | }, 186 | { 187 | "cell_type": "markdown", 188 | "source": [ 189 | "## Constructing the QNode\n", 190 | "\n", 191 | "Now that we have intialized pur device, we can begin to construct a **quantum node**\n", 192 | "\n", 193 | "----\n", 194 | "----\n", 195 | " **Definition**\n", 196 | "\n", 197 | " QNodes are an abstract encapsulation of a quantum function, described by a quantum circuit. QNodes are bound to a particular quantum device, which is used to evaluate expectation and variance values of this circuit.\n", 198 | "\n", 199 | "\n", 200 | "\n", 201 | "\n", 202 | "\n", 203 | "```QNodes can be constructed via the QNode class, or by using the provided qnode() decorator.```\n", 204 | "\n", 205 | "-----\n", 206 | "\n", 207 | "\n" 208 | ], 209 | "metadata": { 210 | "id": "t2jldfQizhGB" 211 | } 212 | }, 213 | { 214 | "cell_type": "markdown", 215 | "source": [ 216 | "We need to define the quantum function that will be evaluated in the QNode:\n", 217 | "\n", 218 | "\n", 219 | "```python\n", 220 | "def circuit(params):\n", 221 | " qml.RX(params[0], wires=0)\n", 222 | " qml.RY(params[1], wires=0)\n", 223 | " return qml.expval(qml.PauliZ(0))\n", 224 | "```\n", 225 | "\n", 226 | "This is a simple circuit, matching the one described above. Notice that the function `circuit()` is constructed as if it were any other Python function; it accepts a positional argument `params`, which may be a list, tuple, or array, and uses the individual elements for gate parameters.\n", 227 | "\n", 228 | "\n", 229 | "------\n", 230 | "\n", 231 | "\n", 232 | "However, quantum functions are a **restricted subset** of Python functions. For a Python function to also be a valid quantum function, there are some important restrictions:\n", 233 | "\n", 234 | "\n", 235 | "1. **Quantum functions must contain quantum operations, one operation per line, in the order in which they are to be applied.**\n", 236 | "\n", 237 | " In addition, we must always specify the subsystem the operation applies to, by passing the wires argument; this may be a list or an integer, depending on how many wires the operation acts on.\n", 238 | " \n", 239 | " See the [documentation](https://docs.pennylane.ai/en/stable/introduction/operations.html)\n", 240 | "\n", 241 | "\n", 242 | "\n", 243 | "\n", 244 | "2. **Quantum functions must return either a single or a tuple of measured observables.**\n", 245 | " \n", 246 | " As a result, the quantum function always returns a classical quantity, allowing the QNode to interface with other classical functions (and also other QNodes).\n", 247 | "\n", 248 | "\n", 249 | "-----\n", 250 | "\n", 251 | "Once we have written the quantum function, we convert it into a `QNode` running on device `dev1` by applying the `qnode()` decorator. directly above the function definition:\n", 252 | "\n" 253 | ], 254 | "metadata": { 255 | "id": "0ORqXK230ec5" 256 | } 257 | }, 258 | { 259 | "cell_type": "code", 260 | "source": [ 261 | "@qml.qnode(dev1)\n", 262 | "def circuit(params):\n", 263 | " qml.RX(params[0], wires=0)\n", 264 | " qml.RY(params[1], wires=0)\n", 265 | "\n", 266 | " return qml.expval(qml.PauliZ(0))" 267 | ], 268 | "metadata": { 269 | "id": "pRwJerDGzLHf" 270 | }, 271 | "execution_count": 5, 272 | "outputs": [] 273 | }, 274 | { 275 | "cell_type": "markdown", 276 | "source": [ 277 | "Thus, our `circuit()` quantum function is now a `QNode`, which will run on devoce `dev1` every time it is evaluated.\n", 278 | "\n", 279 | "----\n", 280 | "To evaluate , we simply call the function with some appropriate numerical inputs:\n", 281 | "\n" 282 | ], 283 | "metadata": { 284 | "id": "tCoUxdSN1lMP" 285 | } 286 | }, 287 | { 288 | "cell_type": "code", 289 | "source": [ 290 | "print(circuit([0.54,0.12]))" 291 | ], 292 | "metadata": { 293 | "colab": { 294 | "base_uri": "https://localhost:8080/" 295 | }, 296 | "id": "POQngFxH1jTW", 297 | "outputId": "13787f86-be49-4cf1-a7c8-0a6e12079cc9" 298 | }, 299 | "execution_count": 6, 300 | "outputs": [ 301 | { 302 | "output_type": "stream", 303 | "name": "stdout", 304 | "text": [ 305 | "0.8515405859048367\n" 306 | ] 307 | } 308 | ] 309 | }, 310 | { 311 | "cell_type": "markdown", 312 | "source": [ 313 | "## Calculating quantum gradients\n", 314 | "\n", 315 | "The gradient of the function `circuit`, encapsulated within the `QNode`, can be evaluated by utilizing the same quantum device (`dev1`) that we used to evaluate the function itself.\n", 316 | "\n", 317 | "---\n", 318 | "\n", 319 | "PennyLane incorporates both analytic differentiation, as well as numerical methods (such as the method of finite differences). Both of these are done automatically.\n", 320 | "\n", 321 | "---\n", 322 | "\n", 323 | "We can differentiate by using the built-in `grad()` function. This returns another function, representing the gradient (i.e., the vector of partial derivatives) of `circuit`. The gradient can be evaluated in the same way as the original function:" 324 | ], 325 | "metadata": { 326 | "id": "kTrH2PuV2LFy" 327 | } 328 | }, 329 | { 330 | "cell_type": "code", 331 | "source": [ 332 | "dcircuit = qml.grad(circuit, argnum = 0)" 333 | ], 334 | "metadata": { 335 | "id": "nvMOt5sN13wG" 336 | }, 337 | "execution_count": 7, 338 | "outputs": [] 339 | }, 340 | { 341 | "cell_type": "markdown", 342 | "source": [ 343 | "The function `grad()` itself **returns a function**, representing the derivative of the QNode with respect to the argument specified in `argnum`. In this case, the function `circuit` takes one argument (`params`), so we specify `argnum=0`. Because the argument has two elements, the returned gradient is two-dimensional. We can then evaluate this gradient function at any point in the parameter space." 344 | ], 345 | "metadata": { 346 | "id": "ydbaUwGt2mKd" 347 | } 348 | }, 349 | { 350 | "cell_type": "code", 351 | "source": [ 352 | "print(dcircuit([0.54,0.12]))" 353 | ], 354 | "metadata": { 355 | "colab": { 356 | "base_uri": "https://localhost:8080/" 357 | }, 358 | "id": "gbqfMyds2lBb", 359 | "outputId": "bd635142-dd53-43d9-a88d-27172f08ece8" 360 | }, 361 | "execution_count": 8, 362 | "outputs": [ 363 | { 364 | "output_type": "stream", 365 | "name": "stdout", 366 | "text": [ 367 | "[array(-0.51043865), array(-0.1026782)]\n" 368 | ] 369 | } 370 | ] 371 | }, 372 | { 373 | "cell_type": "markdown", 374 | "source": [ 375 | "##### A Note on arguments\n", 376 | "\n", 377 | "Quantum circuit functions, being a restricted subset of Python functions, can also make use of multiple positional arguments and keyword arguments. For example, we could have defined the above quantum circuit function using two positional arguments, instead of one array argument:" 378 | ], 379 | "metadata": { 380 | "id": "ryBfA5CX242r" 381 | } 382 | }, 383 | { 384 | "cell_type": "code", 385 | "source": [ 386 | "@qml.qnode(dev1)\n", 387 | "def circuit2(phi1, phi2):\n", 388 | " qml.RX(phi1, wires=0)\n", 389 | " qml.RY(phi2, wires=0)\n", 390 | " return qml.expval(qml.PauliZ(0))" 391 | ], 392 | "metadata": { 393 | "id": "Qsew9WEd22cg" 394 | }, 395 | "execution_count": 9, 396 | "outputs": [] 397 | }, 398 | { 399 | "cell_type": "markdown", 400 | "source": [ 401 | "When we calculate the gradient for such a function, the usage of `argnum` will be slightly different. In this case, `argnum=0` will return the gradient with respect to only the first parameter (`phi1`), and `argnum=1` will give the gradient for `phi2`. To get the gradient with respect to both parameters, we can use `argnum=[0,1]`:" 402 | ], 403 | "metadata": { 404 | "id": "FOn5PjWY3D9S" 405 | } 406 | }, 407 | { 408 | "cell_type": "code", 409 | "source": [ 410 | "dcircuit = qml.grad(circuit2, argnum=[0, 1])\n", 411 | "print(dcircuit(0.54, 0.12))" 412 | ], 413 | "metadata": { 414 | "colab": { 415 | "base_uri": "https://localhost:8080/" 416 | }, 417 | "id": "zYotIUZT3AJ1", 418 | "outputId": "b1526366-b4aa-4621-ebf5-d001f886ad71" 419 | }, 420 | "execution_count": 10, 421 | "outputs": [ 422 | { 423 | "output_type": "stream", 424 | "name": "stdout", 425 | "text": [ 426 | "(array(-0.51043865), array(-0.1026782))\n" 427 | ] 428 | } 429 | ] 430 | }, 431 | { 432 | "cell_type": "markdown", 433 | "source": [ 434 | "## Optimization\n", 435 | "\n", 436 | " **Definition**\n", 437 | "\n", 438 | " If using the default NumPy/Autograd interface, PennyLane provides a collection of optimizers based on gradient descent. These optimizers accept a cost function and initial parameters, and utilize PennyLane’s automatic differentiation to perform gradient descent.\n", 439 | "\n", 440 | "-----\n", 441 | "\n", 442 | "Next, let’s make use of PennyLane’s built-in optimizers to optimize the two circuit parameters $\\phi_1$ and $\\phi_2$ such that the qubit, originally in state $|0⟩$ , is rotated to be in state $|1⟩$. This is equivalent to measuring a Pauli-Z expectation value of $-1$, since the state $|1⟩$ is an eigenvector of the Pauli-Z matrix with eigenvalue $λ=−1$.\n", 443 | "\n", 444 | "\n", 445 | "-----\n", 446 | "\n", 447 | "In other words, the optimization procedure will find the weights $\\phi_1$ and $\\phi_2$ that result in the following rotation on the Bloch sphere:\n", 448 | "\n", 449 | "![alt text](https://pennylane.ai/qml/_images/bloch.png)\n", 450 | "\n", 451 | "To do so, we need to define a cost function. By minimizing the cost function, the optimizer will determine the values of the circuit parameters that produce the desired outcome.\n", 452 | "\n", 453 | "\n", 454 | "In this case, our desired outcome is a Pauli-Z expectation value of $-1$ Since we know that the Pauli-Z expectation is bound between $[−1,1]$\n", 455 | ", we can define our cost directly as the output of the QNode:\n", 456 | "\n", 457 | "\n", 458 | "\n" 459 | ], 460 | "metadata": { 461 | "id": "rZUgzoeX3QYK" 462 | } 463 | }, 464 | { 465 | "cell_type": "code", 466 | "source": [ 467 | "def cost(x):\n", 468 | " return circuit(x)" 469 | ], 470 | "metadata": { 471 | "id": "FjDpYtnB3Nmm" 472 | }, 473 | "execution_count": 11, 474 | "outputs": [] 475 | }, 476 | { 477 | "cell_type": "markdown", 478 | "source": [ 479 | "To begin our optimization, let's choose small initial values of $\\phi_1$ and $\\phi_2$" 480 | ], 481 | "metadata": { 482 | "id": "nqGw5a2G4NXm" 483 | } 484 | }, 485 | { 486 | "cell_type": "code", 487 | "source": [ 488 | "init_params = np.array([0.011, 0.012], requires_grad=True)\n", 489 | "print(cost(init_params))" 490 | ], 491 | "metadata": { 492 | "colab": { 493 | "base_uri": "https://localhost:8080/" 494 | }, 495 | "id": "uQ0SApBF4MEv", 496 | "outputId": "92621acc-41c5-4bd9-e130-842f9ba035b2" 497 | }, 498 | "execution_count": 12, 499 | "outputs": [ 500 | { 501 | "output_type": "stream", 502 | "name": "stdout", 503 | "text": [ 504 | "0.9998675058299389\n" 505 | ] 506 | } 507 | ] 508 | }, 509 | { 510 | "cell_type": "markdown", 511 | "source": [ 512 | "We can see that, for these initial parameter values, the cost function is close to $1$\n", 513 | "\n", 514 | "\n", 515 | "Finally, we use an optimizer to update the circuit parameters for $100$ steps. We can use the built-in `GradientDescentOptimizer` class:" 516 | ], 517 | "metadata": { 518 | "id": "r5BxvQQm4W8M" 519 | } 520 | }, 521 | { 522 | "cell_type": "code", 523 | "source": [ 524 | "# initialise the optimizer\n", 525 | "opt = qml.GradientDescentOptimizer(stepsize=0.4)\n", 526 | "\n", 527 | "# set the number of steps\n", 528 | "steps = 100\n", 529 | "# set the initial parameter values\n", 530 | "params = init_params\n", 531 | "\n", 532 | "for i in range(steps):\n", 533 | " # update the circuit parameters\n", 534 | " params = opt.step(cost, params)\n", 535 | "\n", 536 | " if (i + 1) % 5 == 0:\n", 537 | " print(\"Cost after step {:5d}: {: .7f}\".format(i + 1, cost(params)))\n", 538 | "\n", 539 | "print(\"Optimized rotation angles: {}\".format(params))" 540 | ], 541 | "metadata": { 542 | "colab": { 543 | "base_uri": "https://localhost:8080/" 544 | }, 545 | "id": "1u80oqFR4Uqs", 546 | "outputId": "8d2553cc-1756-4762-ab5f-b3d0d647c004" 547 | }, 548 | "execution_count": 13, 549 | "outputs": [ 550 | { 551 | "output_type": "stream", 552 | "name": "stdout", 553 | "text": [ 554 | "Cost after step 5: 0.9961778\n", 555 | "Cost after step 10: 0.8974944\n", 556 | "Cost after step 15: 0.1440490\n", 557 | "Cost after step 20: -0.1536720\n", 558 | "Cost after step 25: -0.9152496\n", 559 | "Cost after step 30: -0.9994046\n", 560 | "Cost after step 35: -0.9999964\n", 561 | "Cost after step 40: -1.0000000\n", 562 | "Cost after step 45: -1.0000000\n", 563 | "Cost after step 50: -1.0000000\n", 564 | "Cost after step 55: -1.0000000\n", 565 | "Cost after step 60: -1.0000000\n", 566 | "Cost after step 65: -1.0000000\n", 567 | "Cost after step 70: -1.0000000\n", 568 | "Cost after step 75: -1.0000000\n", 569 | "Cost after step 80: -1.0000000\n", 570 | "Cost after step 85: -1.0000000\n", 571 | "Cost after step 90: -1.0000000\n", 572 | "Cost after step 95: -1.0000000\n", 573 | "Cost after step 100: -1.0000000\n", 574 | "Optimized rotation angles: [7.15266381e-18 3.14159265e+00]\n" 575 | ] 576 | } 577 | ] 578 | }, 579 | { 580 | "cell_type": "markdown", 581 | "source": [ 582 | "We can see that the optimization converges after approximately 40 steps.\n", 583 | "\n", 584 | "\n", 585 | "----\n", 586 | "\n", 587 | "Substituting this into the theoretical result $\\langle \\psi \\mid \\sigma_z \\mid \\psi \\rangle = \\cos\\phi_1\\cos\\phi_2$ , we can verify that this is indeed one possible value of the circuit parameters that produces $\\langle \\psi \\mid \\sigma_z \\mid \\psi \\rangle=-1$ , resulting in the qubit being rotated to the state $|1⟩$\n", 588 | "\n", 589 | "------\n", 590 | "\n", 591 | "\n", 592 | "##### Note\n", 593 | "\n", 594 | "Some optimizers, such as `AdagradOptimizer`, have internal hyperparameters that are stored in the optimizer instance. These can be reset using the reset() method.\n" 595 | ], 596 | "metadata": { 597 | "id": "IwhED5Cz4kyU" 598 | } 599 | } 600 | ] 601 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [←←Back to Homepage](https://monitsharma.github.io/) 2 | 3 | 4 | # A course on [Quantum Machine Learning](https://github.com/MonitSharma/Learn-Quantum-Machine-Learning/) 5 | 6 | This course will take you through the basic theory required to understand [quantum machine learning.](https://github.com/MonitSharma/Learn-Quantum-Machine-Learning/) 7 | 8 | 9 | 10 | 11 | ## Getting Started (Notes and Coding tutorials) 12 | 13 | Here you can discover the basic tools needed to use PennyLane through simple demonstrations. Learn about training a circuit to rotate a qubit, machine learning tools to optimize quantum circuits, and introductory examples of photonic quantum computing. 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 59 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 72 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 139 | 140 | 141 | 142 | 143 | 144 | 145 |
Sr. NoTitleDescriptionNotebookMedium
1.What is Quantum Machine LearningReading Material related to QML and background 30 | 31 | 33 | 34 |
2.Basic Qubit RotationWish to implement the rotation quantum circuit: 44 | 45 | 47 | 48 |
3.Quantum Gradient and BackpropagationTheory related to the Parameter-Shift rule 57 | 58 | 60 | 61 |
4.Quantum Gradient and BackpropagationTutorial related to the Parameter-Shift rule 70 | 71 | 73 | 74 |
5.Adjoint DifferentiationAdjoint differentiation straddles two strategies, taking benefits from each. 83 | 84 | 85 | 86 |
6.Gaussian Transformation Basic principles of continuous variable (CV) photonic devices. 96 | 97 | 98 | 99 |
7.Plugins and Hybrid Computation Introduces the notion of hybrid computation by combining several PennyLane plugins. 108 | 109 | 110 | 111 |
8.Noisy Circuits Learn how to simulate noisy circuits using built-in functionality in PennyLane 121 | 122 | 123 | 124 |
9.Penny Lane + AWS braket Computing gradients with Pennylane and AWS Braket 135 | 136 | 137 | 138 |
146 | 147 | 148 | 149 | ------ 150 | 151 | ## Optimization 152 | Here you will find demonstrations showcasing quantum optimization. Explore various topics and ideas, such as the shots-frugal Rosalin optimizer, the variational quantum thermalizer, or barren plateaus in quantum neural networks. 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 170 | 173 | 174 | 175 | 176 |
Sr. NoTitleDescriptionNotebookMedium
1.Introduction to QAOAThe applications of QAOA are broad and far-reaching, the performance of the algorithm is of great interest to the quantum computing research community 168 | 169 | 171 | 172 |
177 | 178 | 179 | --------------- 180 | 181 | ## Quantum Machine Learning 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 200 | 203 | 204 | 205 |
Sr. NoTitleDescriptionNotebookMedium
1.Quantum models as Fourier seriesThis demonstration is based on the paper The effect of data encoding on the expressive power of variational quantum machine learning models 198 | 199 | 201 | 202 |
206 | 207 | 208 | ------ 209 | 210 | ## Quantum Machine Learning Tutorials and Worked Examples 211 | 212 | Check the [repository](https://github.com/MonitSharma/Quantum-Machine-Learning-Projects) with full details regarding some of the worked examples. 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 232 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 245 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 258 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 271 | 274 | 275 | 276 | 277 | 278 | 279 | 280 |
Sr. NoTitleDescriptionNotebookMedium
1.Quantum Variational ClassifierUsing variational approach to classify Iris dataset 230 | 231 | 233 | 234 |
2.Data Re-Uploading ClassifierMaking a quantum classifier by only using single qubit 243 | 244 | 246 | 247 |
3.Galaxy Detection using QMLDeveloping galaxy detection technique from the telescope image via QML. 256 | 257 | 259 | 260 |
4.QCD Equation of State Classification using QSVMDeveloping a Quantum Support Vector Machine model for Quantum Chromodynamics equation of state. 269 | 270 | 272 | 273 |
281 | 282 | 283 | ----- 284 | 285 | ## Quantum Machine Learning with Qiskit 286 | 287 | | Sr.No | Title | Description | Notebook | Medium | 288 | |-------|------------------------------------------------|--------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------| ---------------------------------------------------------------------------------------| 289 | | 1 | Mathematical Introduction | Introduction to mathematical concepts used in quantum computing | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/MonitSharma/Learn-Quantum-Machine-Learning/blob/main/Mathematical_Introduction.ipynb) | [![Medium](https://img.shields.io/badge/Medium-12100E?style=for-the-badge&logo=medium&logoColor=white)]() 290 | | 2 | Introduction to Qiskit | Overview of the Qiskit framework and its components | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://github.com/MonitSharma/Learn-Quantum-Machine-Learning/blob/main/Introduction_to_Qiskit.ipynb) | 291 | | 3 | Classical and Quantum Probability Distribution | Comparison of classical and quantum probability distributions, including the Bloch sphere | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://github.com/MonitSharma/Learn-Quantum-Machine-Learning/blob/main/Classical_and_Quantum_Probabilities.ipynb) | 292 | | 4 | Measurement and Mixed states | Understanding quantum measurement and mixed states | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://github.com/MonitSharma/Learn-Quantum-Machine-Learning/blob/main/Measurement_and_Mixed_States.ipynb) | 293 | | 5 | Evolution in closed and open systems | Dynamics of quantum systems in closed and open systems | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://github.com/MonitSharma/Learn-Quantum-Machine-Learning/blob/main/Evolution_in_Closed_and_Open_Systems.ipynb) | 294 | | 6 | Classical and Quantum Many body physics | Study of many-body systems from classical and quantum perspectives, including entanglement | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/) | 295 | | 7 | Gate model quantum computing | Implementation of quantum algorithms using gate operations and circuit models | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/) | 296 | | 8 | Adiabatic quantum computing | Introduction to adiabatic quantum computing, including its physical principles and algorithms | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/) | 297 | | 9 | Variational circuits | Overview of variational circuits and their applications | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/) | 298 | | 10 | Sampling a thermal state | Explanation of thermal states in quantum systems and sampling techniques | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/) | 299 | | 11 | Discrete optimization and ensemble learning | Application of quantum computing to discrete optimization and ensemble learning problems | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/) | 300 | | 12 | Kernel methods | Introduction to kernel methods and their application in quantum computing | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/) | 301 | | 13 | Training a probabilistic model | Explanation of probabilistic models and how to train them using quantum computing techniques | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/) | 302 | | 14 | Quantum phase estimation | Quantum algorithm for estimating the eigenvalues of a unitary operator | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/) | 303 | | 15 | Quantum matrix inversion | Introduction to quantum matrix inversion and its applications | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/) | 304 | 305 | -------------------------------------------------------------------------------- /What_is_Quantum_Machine_Learning.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "view-in-github" 8 | }, 9 | "source": [ 10 | "\"Open" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": { 16 | "id": "1E4Vn0jWPWUM" 17 | }, 18 | "source": [ 19 | "# What is Quantum Machine Learning?" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": { 25 | "id": "wQx6GC9DPZxj" 26 | }, 27 | "source": [ 28 | "Quantum machine learning is a research area that **explores the interplay of ideas from quantum computing and machine learning**\n", 29 | "\n", 30 | "For example, we might want to find out whether quantum computers can speed up the time it takes to train or evaluate a machine learning model. On the other hand, we can leverage techniques from machine learning to help us uncover quantum error-correcting codes, estimate the properties of quantum systems, or develop new quantum algorithms." 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": { 36 | "id": "Ra8A9zg0PqlA" 37 | }, 38 | "source": [ 39 | "## Quantum Computers as AI accelarators\n", 40 | "\n", 41 | "The limits of what machines can learn have always been defined by the computer hardware we run our algorithms on—for example, the success of modern-day deep learning with neural networks is enabled by parallel GPU clusters.\n", 42 | "\n", 43 | "**Quantum machine learning extends the pool of hardware for machine learning by an entirely new type of computing device—the quantum computer.** Information processing with quantum computers relies on substantially different laws of physics known as quantum theory.\n" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": { 49 | "id": "_avoabvQP3Qs" 50 | }, 51 | "source": [ 52 | "![alt text](https://pennylane.ai/qml/_images/gpu_to_qpu.png)\n" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": { 58 | "id": "oTfP2F8gQAdY" 59 | }, 60 | "source": [ 61 | "## Machine Learning on near-term quantum devices\n", 62 | "\n", 63 | "![alt text](https://pennylane.ai/qml/_images/quantum_devices_ai.png)\n", 64 | "\n", 65 | "\n", 66 | "\n", 67 | "Some research focuses on ideal, universal quantum computers (“fault-tolerant QPUs”) which are still years away. **But there is rapidly-growing interest in quantum machine learning on near-term [quantum devices](https://www.cornell.edu/video/john-preskill-quantum-computing-nisq-era-beyond)**.\n", 68 | "\n", 69 | "We can understand these deivces as special purpose hardware like Application-Specific Integrated Circuits (ASICs) and Field-Programmable Gate Arrays (FPGAs) which are more limited in their functonality" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "metadata": { 75 | "id": "w8RWCSucQ6Hn" 76 | }, 77 | "source": [ 78 | "### Quantum Devices as special-purpose AI accelarators\n", 79 | "\n", 80 | "![alt text](https://miro.medium.com/max/828/1*1qJmyhJ9mkpiF69Tko7vPg.png)\n", 81 | "\n", 82 | "An example of \"quantum ASIC\" is an integrated nanophotonics chip that implements Boson sampling for a fixed number of modes. Boson sampling quickly becomes intractable on classical computers if we increase number of modes.\n", 83 | "\n" 84 | ] 85 | }, 86 | { 87 | "cell_type": "markdown", 88 | "metadata": { 89 | "id": "D-0-xCBSRVBb" 90 | }, 91 | "source": [ 92 | "Many current quantum technologies resemble special-purpose hardware like **Application-Specific Integrated Circuits** (ASICs) rather than a general purpose CPU. They are hardwired to implement alimited class of quantum algorithms.\n", 93 | "\n", 94 | "-----\n", 95 | "\n", 96 | "\n", 97 | "### Application Specific Integrated Circuit\n", 98 | "\n", 99 | "![alt text](https://upload.wikimedia.org/wikipedia/commons/thumb/7/79/SSDTR-ASIC_technology.jpg/330px-SSDTR-ASIC_technology.jpg)\n", 100 | "\n", 101 | "A tray of application-specific integrated circuit chips.\n", 102 | "\n", 103 | "\n", 104 | "\n", 105 | "\n", 106 | "\n", 107 | "----\n", 108 | "An ASIC is an integrated circuit chip customized for a particular use, rather than intended for general-purpose use. For example, a chip designed to run in a digital voice recoder or a high-efficiency video codec is an ASIC." 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": { 114 | "id": "Zom3tzcfSTnA" 115 | }, 116 | "source": [ 117 | "----\n", 118 | "More advanced quantum devices can be programmed to run simole quantum circuits, which makes them more similar to **Field-Programmable Gate Arrays** (FPGAs), integrated circuits that are programmed using a low-level, hardware-specific Hardware Description Langauge.\n", 119 | "\n", 120 | "-----\n", 121 | "#### Field-Programmable gate array\n", 122 | "A FPGA is an integrated circuit designed to be configured by a customer or a designer after manufacturing.\n", 123 | "\n", 124 | "![alt text](https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Altera_StratixIVGX_FPGA.jpg/330px-Altera_StratixIVGX_FPGA.jpg)\n", 125 | "\n", 126 | "\n", 127 | "----\n" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "metadata": { 133 | "id": "lK340fjSS-e1" 134 | }, 135 | "source": [ 136 | "In both the cases, an intimate knowledge of the hardware design and limitations is needed to run effective algorithms.\n", 137 | "\n", 138 | "![alt text](https://miro.medium.com/max/828/1*OF2zPLtuX6146AJOC8oPGA.png)\n", 139 | "\n", 140 | "Schematic drawing of an FPGA(left) and IBM's $16$ qubit quantum chip(right). Near-term quantum devices can be compared to FPGAs, in that they also consist of locally programmable gates with a hardware-oriented programming language.\n", 141 | "\n", 142 | "----\n", 143 | "----\n", 144 | "ASICs and FPGAs find growing use in machine learning and artificial intelligence, where their slim architectures reduce the overhead of a central processor and naturally suit the task they specialize in. If current quantum technologies resemble this classical special-purpose hardware, they could find applications in machine learning in a similar fashion. And this even without universal quantum computing and exponential quantum speedups." 145 | ] 146 | }, 147 | { 148 | "cell_type": "markdown", 149 | "metadata": { 150 | "id": "ZshVG-THT89x" 151 | }, 152 | "source": [ 153 | "## Using quantum computers like neural networks\n", 154 | "\n", 155 | "In the modern viewpoint, quantum computers can be used and trained like neural networks. We can systematically adapt the physical control parameters, such as an electromagnetic field strength or a laser pulse frequency, to solve a problem.\n", 156 | "\n", 157 | "\n", 158 | "![alt text](https://pennylane.ai/qml/_images/trainable_circuit.png)\n", 159 | "\n", 160 | "For example, a trained circuit can be used to classify the content of images, by encoding the image into the physical state of the device and taking measurements." 161 | ] 162 | }, 163 | { 164 | "cell_type": "markdown", 165 | "metadata": { 166 | "id": "YQoR9dutUO6I" 167 | }, 168 | "source": [ 169 | "## The Bigger Picture : differentiable programming\n", 170 | "\n", 171 | "But the story is bigger than just using quantum computers to tackle machine learning problems. Quantum circuits are differentiable, and a quantum computer itself can compute the change in control parameters needed to become better at a given task.\n", 172 | "\n", 173 | "---\n", 174 | "\n", 175 | "### What is Differentiable Programming?\n", 176 | "\n", 177 | "**Differentiable programming** is a programming paradigm in which a numeric computer program can be differentiated throughout via automatic differentiation.This allows for gradient-based optimization of parameters in the program, often via gradient descent. Differentiable programming has found use in a wide variety of areas, particularly scientific computing and artificial intelligence.\n", 178 | "\n", 179 | "-----\n", 180 | "\n", 181 | "**Differentiable programming** is the very basis of deep learning, implemented in software libraries such as TensorFlow and PyTorch. **Differentiable programming is more than deep learning: it is a programming paradigm where the algorithms are not hand-coded, but learned.**\n", 182 | "\n", 183 | "\n", 184 | "![alt text](https://pennylane.ai/qml/_images/applications.png)\n", 185 | "\n", 186 | "\n", 187 | "Similarly, the idea of training quantum computers is larger than quantum machine learning. Trainable quantum circuits can be leveraged in other fields like **quantum chemistry** or **quantum optimization**. It can help in a variety of applications such as the **design of quantum algorithms**, the discovery of **quantum error correction schemes**, and the **understanding of physical systems**.\n", 188 | "\n", 189 | "\n", 190 | "\n", 191 | "\n" 192 | ] 193 | }, 194 | { 195 | "cell_type": "markdown", 196 | "metadata": { 197 | "id": "QY2andadVOFW" 198 | }, 199 | "source": [ 200 | "![alt text](https://pennylane.ai/qml/_images/jigsaw.png)\n", 201 | "\n", 202 | "\n", 203 | "\n" 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "metadata": { 209 | "id": "qTF1cv5tVSXI" 210 | }, 211 | "source": [ 212 | "The source material is taken from Pennylane's website, they share amazing resources and visuals related to quantum computing and quantum machine leanring. Do check them out.\n", 213 | "\n" 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": null, 219 | "metadata": { 220 | "id": "Qz7jccYzOx3p" 221 | }, 222 | "outputs": [], 223 | "source": [] 224 | } 225 | ], 226 | "metadata": { 227 | "colab": { 228 | "authorship_tag": "ABX9TyMsDU5+w5JKL9UCFjNhoxkh", 229 | "include_colab_link": true, 230 | "provenance": [] 231 | }, 232 | "kernelspec": { 233 | "display_name": "Python 3", 234 | "name": "python3" 235 | }, 236 | "language_info": { 237 | "name": "python" 238 | } 239 | }, 240 | "nbformat": 4, 241 | "nbformat_minor": 0 242 | } 243 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | qiskit 3 | qiskit-aer 4 | pylatexenc 5 | matplotlib 6 | pennylane 7 | jax --------------------------------------------------------------------------------