{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Gate String Lists Tutorial\n", "This tutorial will show you how to create and use `GateString` objects, which represent an ordered sequence (or \"string\") of quantum gate operations. In almost all cases, you'll be using a list (or even a list of lists!) of `GateString`s, so we'll often be talking about \"gate string lists\". You may have noticed in Tutorial 00 that we had to generate gate string lists of \"fiducials\" and \"germs\" in order to populate the data template file which GST analyzes to produce its estimates.\n", "\n", "A `GateString` object is nearly identical, and sometimes interchangeable, with a Python tuple of gate labels (i.e. the names beginning with `G` that label the gate operations in a `GateSet`). They can be accessed and operated upon just as a standard Python tuple. The primary difference between a `GateString` and a tuple is that a `GateString` also contains a \"string representation\" of the gate sequence. This string representation gets carried along for the ride until it's needed, typically when writing a the gate string to a file. The string representation must *evaluate*, using pyGSTi's allowed text format for gate strings (see below), to the tuple-of-gate-labels, or \"tuple representation\". The string representation is intended to contain a compact and intuitive human-readable form of the gate sequence that is used for display purposes. For example, the gate string `('Gx','Gx','Gx','Gx','Gx')` might have the string representation `\"Gx^5\"`. If needed, the tuple and string representations of any `GateString` can be accessed via `.tup` and `.str` respectively.\n", "\n", "Gate strings are central to Gate Set Tomography, as they describe both real and \"simulated\" experiments. A `GateString`'s ordered sequence tells the experimentalist which gates they must execute on their hardware and likewise what order to compose (i.e. multiply) the gate matrices contained in a `GateSet`. The outcomes of an experiment correspond to different **SPAM labels** (c.f. the gate set tutorial), and so by repeating an experiment one obtains counts and thereby frequencies for each SPAM label. Given a `GateSet` one can obtain corresponding probabilities by muliplying gate matrices and contracting the product between the state preparation and POVM effect vectors associated with each SPAM label. \n", "\n", "The **ordering direction** is important. The elements of a `GateString` are read from **left-to-right**, meaning the first (left-most) gate label is performed first. This is very natural for experiments since one can read the gate string as a script, executing each gate as one reads from left to right. However, since we insist on \"normal\" matrix multiplication conventions, the ordering of the matrix product is *reversed* from that of the gate string. For example, the gate string `('Ga','Gb','Gc')`, in which Ga is performed first, corresponds to the matrix product $G_c G_b G_a$. The probability of this gate string for a SPAM label associated with the (column) vectors ($\\rho_0$,$E_0$) is given by $E_0^T G_c G_b G_a \\rho_0$, which can be interpreted as \"prepare state 0 first, then apply gate A, then B, then C, and finally measure effect 0\". While this nuance is typically hidden from the user (the `GateSet` functions which compute products and probabilities from `GateString`s perform the order reversal internally), it becomes very important if you plan to perform such products by hand. \n", "\n", "We'll now go over some examples of how to create and use a single `GateString`." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from __future__ import print_function" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import pygsti # the main pyGSTi module\n", "import pygsti.construction as pc #shorthand\n", "from pygsti.construction import std1Q_XY #a standard gateset & peripherals" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## A simple example: the single `GateString`\n", "The cell below show how to create a `GateString` object from a tuple, optionally with a corresponding string representation. It demonstrates how to access the tuple and string representations directly, and the tuple-like operations that can be performed on a `GateString`." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Printing\n", "s1 = GxGx\n", "s2 = Gx^2\n", "s3 = Gx^2\n", "\n", "Printing tuple(.)\n", "s1 = ('Gx', 'Gx')\n", "s2 = ('Gx', 'Gx')\n", "s3 = ('Gx', 'Gx')\n", "\n", "s1.tup = ('Gx', 'Gx') , s1.str = GxGx\n", "\n", "s1 + s2 = GxGxGx^2 , tuple = ('Gx', 'Gx', 'Gx', 'Gx')\n", "s1*3 = (GxGx)^3 , tuple = ('Gx', 'Gx', 'Gx', 'Gx', 'Gx', 'Gx')\n", "\n" ] } ], "source": [ "#Construction of a GateString\n", "s1 = pygsti.objects.GateString( ('Gx','Gx') ) # from a tuple\n", "s2 = pygsti.objects.GateString( ('Gx','Gx'), \"Gx^2\" ) # from tuple and string representations (must match!)\n", "s3 = pygsti.objects.GateString( None, \"Gx^2\" ) # from just a string representation\n", "\n", "#All of these are equivalent (even though their string representations aren't -- only tuples are compared)\n", "assert(s1 == s2 == s3)\n", "\n", "#Printing displays the string representation\n", "print(\"Printing\")\n", "print(\"s1 = %s\" % s1)\n", "print(\"s2 = %s\" % s2)\n", "print(\"s3 = %s\" % s3, end='\\n\\n')\n", "\n", "#Casting to tuple displays the tuple representation\n", "print(\"Printing tuple(.)\")\n", "print(\"s1 =\", tuple(s1))\n", "print(\"s2 =\", tuple(s2))\n", "print(\"s3 =\", tuple(s3), end='\\n\\n')\n", "\n", "#Access to tuple or string representation directly:\n", "print(\"s1.tup =\", s1.tup, \", s1.str = \", s1.str, end='\\n\\n')\n", "\n", "#Operations\n", "assert(s1 == ('Gx','Gx')) #can compare with tuples\n", "s4 = s1+s2 #addition (note this concatenates string reps)\n", "s5 = s1*3 #integer-multplication (note this exponentiates in string rep)\n", "print(\"s1 + s2 = \",s4, \", tuple = \", tuple(s4))\n", "print(\"s1*3 = \",s5, \", tuple = \", tuple(s5), end='\\n\\n')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## List Construction Functions: `pygsti.construction` and `create_gatestring_list`\n", "Usually you'll be working with entire lists of `GateString` objects which define some part of the experiments utilized by Gate Set Tomography. pyGSTi provides several functions for constructing gate string lists, which we not demonstrate.\n", "\n", "The workhorse function is `pygsti.construction.create_gatestring_list`, which executes its positional arguments within a nested loop given by iterable keyword arguments. That's a mouthful, so let's look at a few examples:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "list1 = [('a1',), ('a2',)]\n", "list2 = [GateString(a1b1b2), GateString(a1b3b4), GateString(a2b1b2), GateString(a2b3b4)]\n", "list3 = ['a1a1c', 'a2a2c']\n" ] } ], "source": [ "As = [('a1',),('a2',)]\n", "Bs = [('b1','b2'), ('b3','b4')]\n", "\n", "def rep2(x):\n", " return x+x\n", "\n", "list1 = pc.create_gatestring_list(\"a\", a=As)\n", "list2 = pc.create_gatestring_list(\"a+b\", a=As, b=Bs, order=['a','b'])\n", "list3 = pc.create_gatestring_list(\"R(a)+c\", a=As, c=[('c',)], R=rep2)\n", "\n", "print(\"list1 = %s\" % list(map(tuple, list1)))\n", "print(\"list2 = %s\" % list2)\n", "print(\"list3 = %s\" % list(map(str,list3)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Many of the gate sequences used by Gate Set Tomography are composed of three parts. A \"preparation fiducial\" sequence is followed by a \"repeated germ\" sequence, which is followed by a \"measurement fiducial\" sequence. We won't get into why this structure is used, but simply use this fact to motivate looking at gate strings of the form $f_1 + R(g) + f_2$, where the $f_1$ and $f_2$ fiducial sequences are simple short sequences are $R(g)$ is a possibly long sequence that is generated by repeating a short sequence $g$ called a \"germ\".\n", "\n", "It is possible to generate \"repeated germ\" sequences in several ways using the functions **`pygsti.construction.repeat_`*xxx* **. In modern GST, germ sequences are always repeated an *integer* number of times rather than being truncated to a precise length, so `repeat_with_max_length` is used instead of `repeat_and_truncate`. Below we demonstrate the use of these functions." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "('A', 'B', 'C', 'A', 'B')\n", "('A', 'B', 'C')\n", "1\n" ] } ], "source": [ "print(pc.repeat_and_truncate(('A', 'B', 'C'), 5)) #args (x,N): repeat x until it is exactly length N\n", "\n", "print(pc.repeat_with_max_length(('A', 'B', 'C'), 5)) #args (x,N): repeat x the maximum integer number of times so len(x) < N\n", "\n", "print(pc.repeat_count_with_max_length(('A', 'B', 'C'), 5)) #args (x,N): the maximum integer number of times so len(x) < N" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can combine a repeated germ sequence between two fiducial sequences using `create_gatestring_list`. This demonstrates the power of the `create_gatestring_list` to perform nested loops. We also introduce the \"bulk-conversion\" function `gatestring_list`, which creates a list of `GateString` objects from a list of tuples." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "gateStrings1 = \n", " Gf0(G0)^2Gf0\n", "Gf0(G0)^2Gf1\n", "Gf1(G0)^2Gf0\n", "Gf1(G0)^2Gf1\n", "Gf0(G1aG1b)^2Gf0\n", "Gf0(G1aG1b)^2Gf1\n", "Gf1(G1aG1b)^2Gf0\n", "Gf1(G1aG1b)^2Gf1 \n", "\n", "gateStrings2 = \n", " Gf0G0G0G0Gf0\n", "Gf0G0G0G0Gf1\n", "Gf1G0G0G0Gf0\n", "Gf1G0G0G0Gf1\n", "Gf0G1aG1bG1aGf0\n", "Gf0G1aG1bG1aGf1\n", "Gf1G1aG1bG1aGf0\n", "Gf1G1aG1bG1aGf1 \n", "\n", "gateStrings3 = \n", " Gf0(G0)^3Gf0\n", "Gf0(G0)^3Gf1\n", "Gf1(G0)^3Gf0\n", "Gf1(G0)^3Gf1\n", "Gf0(G1aG1b)Gf0\n", "Gf0(G1aG1b)Gf1\n", "Gf1(G1aG1b)Gf0\n", "Gf1(G1aG1b)Gf1 \n", "\n" ] } ], "source": [ "fids = pc.gatestring_list( [ ('Gf0',), ('Gf1',) ] ) #fiducial strings\n", "germs = pc.gatestring_list( [ ('G0',), ('G1a','G1b')] ) #germ strings\n", "\n", "gateStrings1 = pc.create_gatestring_list(\"f0+germ*e+f1\", f0=fids, f1=fids,\n", " germ=germs, e=2, order=[\"germ\",\"f0\",\"f1\"])\n", "print(\"gateStrings1 = \\n\", \"\\n\".join(map(str,gateStrings1)),\"\\n\")\n", "\n", "gateStrings2 = pc.create_gatestring_list(\"f0+T(germ,N)+f1\", f0=fids, f1=fids,\n", " germ=germs, N=3, T=pc.repeat_and_truncate,\n", " order=[\"germ\",\"f0\",\"f1\"])\n", "\n", "print(\"gateStrings2 = \\n\", \"\\n\".join(map(str,gateStrings2)),\"\\n\")\n", "\n", "gateStrings3 = pc.create_gatestring_list(\"f0+T(germ,N)+f1\", f0=fids, f1=fids,\n", " germ=germs, N=3, T=pc.repeat_with_max_length,\n", " order=[\"germ\",\"f0\",\"f1\"])\n", "print(\"gateStrings3 = \\n\", \"\\n\".join(map(str,gateStrings3)), \"\\n\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In addition to `create_gatestring_list`, the **`pygsti.construction.list_`*xxx* ** functions provide ways of constructing common gate string lists. The example below shows how to construct all possible gate strings within a certain length range, as well as how to construct the set of gate strings needed to run Linear Gate Set Tomography given a set of fiducial strings. " ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "All strings using ['Gx', 'Gy'] up to length 2 = \n", " {}\n", "Gx\n", "Gy\n", "GxGx\n", "GxGy\n", "GyGx\n", "GyGy\n" ] } ], "source": [ "myGates = [ 'Gx', 'Gy' ] #gate labels -- often just gateset.gates.keys()\n", "allStringsInLengthRange = pc.list_all_gatestrings(myGates, minlength=0, maxlength=2)\n", "print(\"\\nAll strings using %s up to length 2 = \\n\" \\\n", " % str(myGates), \"\\n\".join(map(str,allStringsInLengthRange)))" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "LGST strings = \n", " Gf1\n", "Gf2\n", "Gf1Gf1\n", "Gf1Gf2\n", "Gf2Gf1\n", "Gf2Gf2\n", "Gf1(Gx)Gf1\n", "Gf1(Gx)Gf2\n", "Gf2(Gx)Gf1\n", "Gf2(Gx)Gf2\n", "Gf1(Gy)Gf1\n", "Gf1(Gy)Gf2\n", "Gf2(Gy)Gf1\n", "Gf2(Gy)Gf2\n" ] } ], "source": [ "myFiducialList = pc.gatestring_list([ ('Gf1',), ('Gf2',) ]) #list of fiducials\n", "\n", "#Create spam specs which is just a tuple of two lists SpamSpec objects: one list \n", "# for preparation, the other for measurment. Each SpamSpec object associates a \n", "# fiducial gate string with a state prep (for preparation fiducials)\n", "# or a POVM effect (for measurement fiducials). In this example, since we're \n", "# just interested in the LGST strings, the state preps and POVM effects do not\n", "# enter and are irrelevant.\n", "mySpecs = pc.build_spam_specs(fiducialGateStrings=myFiducialList) \n", "\n", "lgstStrings = pc.list_lgst_gatestrings(mySpecs,myGates)\n", "\n", "print(\"\\nLGST strings = \\n\",\"\\n\".join(map(str,lgstStrings)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Generating gate string lists for GST (and further tutorials)\n", "As a final full-fledged example we demonstrate functions which generate gate string lists for running extended LGST (eLGST or EXLGST) and long-sequence GST (LSGST) from lists of gates, fiducials, germs, and maximum lengths. eLGST and LSGST are two different algorithms for performing Gate Set Tomography (more detail will be given on these in the Algorithms tutorial). The important different between the two for our purposes is that eLGST does *not* include fiducial-string prefixes or postfixes in its lists whereas LSGST does. The following example functions are very similar to `pygsti.construction.make_lsgst_lists`, `pygsti.construction.make_elgst_lists`, and can be copied verbatim then modified in many circumstances to provide customized gate string generation.\n", "\n", "Both functions product a *list* of lists of `GateString` objects. As we'll see in later tutorials, eLGST and LSGST Gate Set Tomography algorithms utilize an iterative approach whereby longer and longer gate strings are used in each successive iteration. Each list in the list-of-lists returned by these functions specifies the gate sequences to use during the corresponding iteration. Thus, each successive list contains longer gate strings. Each list is generated using a maximum length, and the *list* of maximum lengths, `maxLengthList` below, specifies the maximum length of each list in the lists-of-lists. Thus, `maxLengthList` should be an *increasing* list of integers (in practice, increasing by powers of two seems good) and the length of `maxLengthList` determines the length of the returned list-of-lists, i.e. the number of gate string lists." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def make_lsgst_lists(gateLabels, fiducialList, germList, maxLengthList):\n", " singleGates = pc.gatestring_list([(g,) for g in gateLabels])\n", " lgstStrings = pc.list_lgst_gatestrings(pc.build_spam_specs(fiducialList), gateLabels)\n", " lsgst_list = pc.gatestring_list([ () ]) #running list of all strings so far\n", " \n", " if maxLengthList[0] == 0:\n", " lsgst_listOfLists = [ lgstStrings ]\n", " maxLengthList = maxLengthList[1:]\n", " else: lsgst_listOfLists = [ ]\n", " \n", " for maxLen in maxLengthList:\n", " lsgst_list += pc.create_gatestring_list(\"f0+R(germ,N)+f1\", f0=fiducialList,\n", " f1=fiducialList, germ=germList, N=maxLen,\n", " R=pc.repeat_with_max_length,\n", " order=('germ','f0','f1'))\n", " lsgst_listOfLists.append( pygsti.remove_duplicates(lgstStrings + lsgst_list) )\n", "\n", " print(\"%d LSGST sets w/lengths\" % len(lsgst_listOfLists), map(len,lsgst_listOfLists))\n", " return lsgst_listOfLists\n", "\n", "def make_elgst_lists(gateLabels, fiducialList, germList, maxLengthList):\n", " singleGates = pc.gatestring_list([(g,) for g in gateLabels])\n", " lgstStrings = pc.list_lgst_gatestrings(pc.build_spam_specs(fiducialList), gateLabels)\n", " elgst_list = pc.gatestring_list([ () ]) #running list of all strings so far\n", " \n", " if maxLengthList[0] == 0:\n", " elgst_listOfLists = [ singleGates ]\n", " maxLengthList = maxLengthList[1:]\n", " else: elgst_listOfLists = [ ]\n", " \n", " for maxLen in maxLengthList:\n", " elgst_list += pc.create_gatestring_list(\"R(germ,N)\", germ=germList, N=maxLen,\n", " R=pc.repeat_with_max_length)\n", " elgst_listOfLists.append( pygsti.remove_duplicates(singleGates + elgst_list) )\n", "\n", " print(\"%d eLGST sets w/lengths\" % len(elgst_listOfLists),map(len,elgst_listOfLists))\n", " return elgst_listOfLists" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll now use these functions to generate some lists we'll use in other tutorials. To do this, we'll use `pygsti.io.write_gatestring_list` to write the lists to text files with one gate string (in it's string representation) per line." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10 eLGST sets w/lengths \n", "10 LSGST sets w/lengths \n", "\n", "First 20 items for dataset generation in label : string format\n", "{} : ()\n", "Gx : ('Gx',)\n", "Gy : ('Gy',)\n", "GxGx : ('Gx', 'Gx')\n", "GxGxGx : ('Gx', 'Gx', 'Gx')\n", "GyGyGy : ('Gy', 'Gy', 'Gy')\n", "GxGy : ('Gx', 'Gy')\n", "GxGxGxGx : ('Gx', 'Gx', 'Gx', 'Gx')\n", "GxGyGyGy : ('Gx', 'Gy', 'Gy', 'Gy')\n", "GyGx : ('Gy', 'Gx')\n", "GyGy : ('Gy', 'Gy')\n", "GyGxGx : ('Gy', 'Gx', 'Gx')\n", "GyGxGxGx : ('Gy', 'Gx', 'Gx', 'Gx')\n", "GyGyGyGy : ('Gy', 'Gy', 'Gy', 'Gy')\n", "GxGxGy : ('Gx', 'Gx', 'Gy')\n", "GxGxGxGxGx : ('Gx', 'Gx', 'Gx', 'Gx', 'Gx')\n", "GxGxGyGyGy : ('Gx', 'Gx', 'Gy', 'Gy', 'Gy')\n", "GxGxGxGy : ('Gx', 'Gx', 'Gx', 'Gy')\n", "GxGxGxGxGxGx : ('Gx', 'Gx', 'Gx', 'Gx', 'Gx', 'Gx')\n", "GxGxGxGyGyGy : ('Gx', 'Gx', 'Gx', 'Gy', 'Gy', 'Gy')\n", "GyGyGyGx : ('Gy', 'Gy', 'Gy', 'Gx')\n", "GyGyGyGxGx : ('Gy', 'Gy', 'Gy', 'Gx', 'Gx')\n", "GyGyGyGxGxGx : ('Gy', 'Gy', 'Gy', 'Gx', 'Gx', 'Gx')\n", "GyGyGyGyGyGy : ('Gy', 'Gy', 'Gy', 'Gy', 'Gy', 'Gy')\n", "(Gi) : ('Gi',)\n", "(Gi)Gx : ('Gi', 'Gx')\n", "(Gi)Gy : ('Gi', 'Gy')\n", "(Gi)GxGx : ('Gi', 'Gx', 'Gx')\n", "(Gi)GxGxGx : ('Gi', 'Gx', 'Gx', 'Gx')\n", "(Gi)GyGyGy : ('Gi', 'Gy', 'Gy', 'Gy')\n" ] } ], "source": [ "gates = ['Gi','Gx','Gy']\n", "fiducials = pc.gatestring_list([ (), ('Gx',), ('Gy',), ('Gx','Gx'), ('Gx','Gx','Gx'), ('Gy','Gy','Gy') ]) # fiducials for 1Q MUB\n", "germs = pc.gatestring_list( [('Gx',), ('Gy',), ('Gi',), ('Gx', 'Gy',),\n", " ('Gx', 'Gy', 'Gi',), ('Gx', 'Gi', 'Gy',),('Gx', 'Gi', 'Gi',), ('Gy', 'Gi', 'Gi',),\n", " ('Gx', 'Gx', 'Gi', 'Gy',), ('Gx', 'Gy', 'Gy', 'Gi',),\n", " ('Gx', 'Gx', 'Gy', 'Gx', 'Gy', 'Gy',)] )\n", "maxLengths = [0,1,2,4,8,16,32,64,128,256] \n", "elgst_lists = make_elgst_lists(gates, fiducials, germs, maxLengths)\n", "lsgst_lists = make_lsgst_lists(gates, fiducials, germs, maxLengths) \n", "\n", "print(\"\\nFirst 20 items for dataset generation in label : string format\")\n", "for gateString in lsgst_lists[-1][0:30]:\n", " print(str(gateString).ljust(20), \": \", tuple(gateString))" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [], "source": [ "#Write example gatestring list files for later use\n", "pygsti.io.write_gatestring_list(\"tutorial_files/Example_FiducialList.txt\", fiducials,\"#My fiducial strings\")\n", "pygsti.io.write_gatestring_list(\"tutorial_files/Example_GermsList.txt\", germs,\"#My germ strings\")\n", "\n", "pygsti.io.write_gatestring_list(\"tutorial_files/Example_GatestringList.txt\",lsgst_lists[-1],\"#All the gate strings to be in my dataset\")\n", "pygsti.io.write_empty_dataset(\"tutorial_files/Example_DatasetTemplate.txt\",lsgst_lists[-1])\n", "\n", "for l,lst in zip(maxLengths,elgst_lists):\n", " pygsti.io.write_gatestring_list(\"tutorial_files/Example_eLGSTlist%d.txt\" % l,lst,\n", " \"# eLGST gate strings for max length %d\" % l)\n", "\n", "for l,lst in zip(maxLengths,lsgst_lists):\n", " pygsti.io.write_gatestring_list(\"tutorial_files/Example_LSGSTlist%d.txt\" % l,lst,\n", " \"# LSGST gate strings for max length %d\" % l)\n", " \n", "#Also write the max lengths we used to file\n", "import json\n", "json.dump(maxLengths, open(\"tutorial_files/Example_maxLengths.json\",\"w\"))" ] } ], "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.5.2" } }, "nbformat": 4, "nbformat_minor": 0 }