{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# This cell is added by sphinx-gallery\n# It can be customized to whatever you like\n%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\nSampling quadratures of non-Gaussian states using the bosonic backend\n=====================================================================\n\n

#### Note

This tutorial is second in a series on the bosonic backend. For an introduction\n to the backend, see :doc:part 1 . Once finished this tutorial,\n check out :doc:part 3  for a deep dive into using the bosonic\n backend to simulate qubits encoded in photonic modes. These tutorials accompany \n our research paper [[#bourassa2021]_].

\n\nIn a previous :doc:tutorial  we introduced\nthe bosonic backend, the fourth and latest software backend of Strawberry Fields.\nIn this tutorial we continue investigating some of its functionality\nby exploring how to sample quadrature (homodyne) measurements in non-Gaussian\nstates. While these simulations could be done in the fock backend, it is a lot\nmore convenient to use the new backend, since simulations in the fock backend need to\nremain under an energy cutoff.\n\nWe first return to our old friend, the cat state, and see how it looks when projected\n(measured) along different quadratures of phase space.\nThen we investigate a useful class of non-Gaussian states known as grid or GKP states.\nThese states are named after Daniel Gottesman _ ,\nAlexei Kitaev _ and\nJohn Preskill _\nwho introduced them in a seminal paper in 2001 [[#gottesman2001]_].\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cat quadratures\n---------------\nIn a previous tutorial we investigated the Wigner function of the cat\nstate. Now, we will explore the distributions of its quadratures.\nThe distributions of the position $q$ and momentum $p$\nquadratures can be obtained by integrating the Wigner function:\n\n\\begin{align}\\text{Pr}(q=x) = \\int_{-\\infty}^\\infty dp~ W(x,p), \\\\\n \\text{Pr}(p=y) = \\int_{-\\infty}^\\infty dq~ W(q,y).\\end{align}\n\nNote that even though the Wigner function can be negative (as it is a quasiprobability distribution _), the quadrature\ndistributions $\\text{Pr}(q=x)$ and $\\text{Pr}(p=y)$ are proper\nprobability distributions, i.e., they are non-negative and their integrals\nequal unity.\n\nWe will use Strawberry Fields to investigate how these distributions look\nfor a cat state.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# We do the usual imports\nimport strawberryfields as sf\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport matplotlib as mpl\nfrom matplotlib import cm\n\n# Create cat state\nprog_cat = sf.Program(1)\n\nwith prog_cat.context as q:\n sf.ops.Catstate(alpha=2) | q\n\neng = sf.Engine(\"bosonic\")\ncat = eng.run(prog_cat).state" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Strawberry Fields provides handy functions for accessing the quadrature\ndistributions. These can retrieved using the marginal method of the\nstate object:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Calculate the quadrature distributions\nscale = np.sqrt(sf.hbar)\nquad_axis= np.linspace(-6, 6, 1000) * scale\ncat_prob_x = cat.marginal(0, quad_axis) # This is the q quadrature\ncat_prob_p = cat.marginal(0, quad_axis, phi=np.pi / 2) # This is the p quadrature" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also use Strawberry Fields to generate samples of a given quadrature.\nIn this case, we do it for the $q$ quadrature:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Run the program again, collecting q samples this time\nshots = 2000 # Number of samples\nprog_cat_x = sf.Program(1)\n\nwith prog_cat_x.context as q:\n sf.ops.Catstate(alpha=2) | q\n sf.ops.MeasureX | q\n\neng = sf.Engine(\"bosonic\")\ncat_samples_x = eng.run(prog_cat_x, shots=shots).samples[:, 0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also sample the $p$ quadrature:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Run the program again, collecting p samples this time\nprog_cat_p = sf.Program(1)\n\nwith prog_cat_p.context as q:\n sf.ops.Catstate(alpha=2) | q\n sf.ops.MeasureP | q\n\neng = sf.Engine(\"bosonic\")\ncat_samples_p = eng.run(prog_cat_p, shots=shots).samples[:, 0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can verify that the samples distribute according to the expected distribution:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Plot the results\nfig, axs = plt.subplots(1, 2, figsize=(10, 4))\nfig.suptitle(r\"$|0^{\\alpha}\\rangle_{cat}$, $\\alpha=2$\", fontsize=18)\n\naxs.hist(cat_samples_x / scale, bins=100, density=True, label=\"Samples\", color=\"cornflowerblue\")\naxs.plot(quad_axis/ scale, cat_prob_x * scale, \"--\", label=\"Ideal\", color=\"tab:red\")\naxs.set_xlabel(r\"q (units of $\\sqrt{\\hbar}$)\", fontsize=15)\naxs.set_ylabel(\"Pr(q)\", fontsize=15)\n\naxs.hist(cat_samples_p / scale, bins=100, density=True, label=\"Samples\", color=\"cornflowerblue\")\naxs.plot(quad_axis/ scale, cat_prob_p * scale, \"--\", label=\"Ideal\", color=\"tab:red\")\naxs.set_xlabel(r\"p (units of $\\sqrt{\\hbar}$)\", fontsize=15)\naxs.set_ylabel(\"Pr(p)\", fontsize=15)\n\naxs.legend()\naxs.tick_params(labelsize=13)\naxs.tick_params(labelsize=13)\nfig.tight_layout(rect=[0, 0.03, 1, 0.95])\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As we can see, we obtain a positive-valued probability distribution. \nThe two lobes arising from the coherent states superposed in the cat state \nare clearly visible in $q$ distribution.\n\n\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "GKP quadratures\n---------------\nAs mentioned before, the bosonic backend can easily handle a number\nof nonclassical states of light. A particularly useful family of states\nare the so-called GKP or grid states. In the same way that cat states\nare linear superpositions of two coherent states, GKP states can be understood\nas superpositions of multiple squeezed states.\nWe can generate a GKP state and plot its Wigner function using Strawberry Fields in\nthe same way we did for cat states.\n\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Create GKP state\nepsilon = 0.1\nprog_gkp = sf.Program(1)\nwith prog_gkp.context as q:\n sf.ops.GKP(epsilon=epsilon) | q\neng = sf.Engine(\"bosonic\")\ngkp = eng.run(prog_gkp).state\n\nWgkp = gkp.wigner(mode=0, xvec=quad_axis, pvec=quad_axis)\nscale = np.max(Wgkp.real)\nnrm = mpl.colors.Normalize(-scale, scale)\nplt.axes().set_aspect(\"equal\")\nplt.contourf(quad_axis, quad_axis, Wgkp, 60, cmap=cm.RdBu, norm=nrm)\nplt.xlabel(r\"q (units of $\\sqrt{\\hbar}$)\", fontsize=15)\nplt.ylabel(r\"p (units of $\\sqrt{\\hbar}$)\", fontsize=15)\nplt.tight_layout()\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see that GKP states in phase space are made of multiple narrow peaks of\nalternating signs.\nAfter getting an idea of how a GKP state looks in phase-space we can now\nturn our attention to understanding its quadrature distribution.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# As before, we can directly calculate the expected quadrature distributions\nscale = np.sqrt(sf.hbar * np.pi)\nquad_axis= np.linspace(-6, 6, 1000) * scale\ngkp_prob_x = gkp.marginal(0, quad_axis)\ngkp_prob_p = gkp.marginal(0, quad_axis, phi=np.pi / 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Like before, we can collect samples of the quadratures to\nsimulate the state being probed with homodyne measurements:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Run the program again, collecting q samples this time\nprog_gkp_x = sf.Program(1)\nwith prog_gkp_x.context as q:\n sf.ops.GKP(epsilon=0.1) | q\n sf.ops.MeasureX | q\neng = sf.Engine(\"bosonic\")\ngkp_samples_x = eng.run(prog_gkp_x, shots=shots).samples[:, 0]\n\n# Run the program again, collecting p samples this time\nprog_gkp_p = sf.Program(1)\nwith prog_gkp_p.context as q:\n sf.ops.GKP(epsilon=0.1) | q\n sf.ops.MeasureP | q\neng = sf.Engine(\"bosonic\")\ngkp_samples_p = eng.run(prog_gkp_p, shots=shots).samples[:, 0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and can compare the results:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Plot the results\nfig, axs = plt.subplots(1, 2, figsize=(10, 4))\nfig.suptitle(r\"$|0^\\epsilon\\rangle_{GKP}$, $\\epsilon=0.1$ (10 dB)\", fontsize=18)\n\naxs.hist(gkp_samples_x / scale, bins=100, density=True, label=\"Samples\", color=\"cornflowerblue\")\naxs.plot(quad_axis/ scale, gkp_prob_x * scale, \"--\", label=\"Ideal\", color=\"tab:red\")\naxs.set_xlabel(r\"q (units of $\\sqrt{\\pi\\hbar}$)\", fontsize=15)\naxs.set_ylabel(\"Pr(q)\", fontsize=15)\n\naxs.hist(gkp_samples_p / scale, bins=100, density=True, label=\"Samples\", color=\"cornflowerblue\")\naxs.plot(quad_axis/ scale, gkp_prob_p * scale, \"--\", label=\"Ideal\", color=\"tab:red\")\naxs.set_xlabel(r\"p (units of $\\sqrt{\\pi\\hbar}$)\", fontsize=15)\naxs.set_ylabel(\"Pr(p)\", fontsize=15)\n\naxs.legend()\naxs.tick_params(labelsize=13)\naxs.tick_params(labelsize=13)\nfig.tight_layout(rect=[0, 0.03, 1, 0.95])\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The comb of peaks are clearly visible in both the quadratures, as are \nvisible the Gaussian spread of the individual peaks and the Gaussian \nenvelope on the height of all the peaks. \n\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Conclusion and Outlook\n----------------------\nIn this tutorial we have explored the sampling functionality\nof the bosonic backend by investigating the quadrature distributions\nof both cat and GKP states. For advanced readers, we encourage you to\nread :doc:part 3  to take a dive \ninto how GKP states can be used to encode a qubit in a photonic mode.\n\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "References\n----------\n\n.. [#bourassa2021]\n\n J. Eli Bourassa, Nicol\u00e1s Quesada, Ilan Tzitrin, Antal Sz\u00e1va, Theodor Isacsson, \n Josh Izaac, Krishna Kumar Sabapathy, Guillaume Dauphinais, and Ish Dhand.\n Fast simulation of bosonic qubits via Gaussian functions in phase space.\n arXiv:2103.05530 _, 2021.\n\n.. [#gottesman2001]\n\n Daniel Gottesman, Alexei Kitaev, and John Preskill. Encoding a qubit in an oscillator.\n doi:10.1103/PhysRevA.64.012310.\n\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.2" } }, "nbformat": 4, "nbformat_minor": 0 }