{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "3b140909-ace6-4665-a0bf-f3bb9bd094c0",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Simulation exacte avec des primitives d' Qiskit SDK\"\n",
        "description: \"Comment réaliser une simulation exacte de circuits quantiques à l'aide de primitives dans Qiskit.\"\n",
        "---\n",
        "\n",
        "<span id=\"exact-simulation-with-qiskit-sdk-primitives\" />\n",
        "\n",
        "# Simulation exacte avec des primitives d' Qiskit SDK\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b820ca5f-60cc-41c7-98db-4b41df4534cc",
      "metadata": {
        "tags": [
          "version-info"
        ]
      },
      "source": [
        "{/*\n",
        "  DO NOT EDIT THIS CELL!!!\n",
        "  This cell's content is generated automatically by a script. Anything you add\n",
        "  here will be removed next time the notebook is run. To add new content, create\n",
        "  a new cell before or after this one.\n",
        "  */}\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Versions de package\">\n",
        "    Le code de cette page a été développé en tenant compte des exigences suivantes.\n",
        "    Nous recommandons d'utiliser ces versions ou des versions plus récentes.\n",
        "\n",
        "    ```\n",
        "    qiskit[all]~=2.5.1\n",
        "    ```\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "867f73c6-dd57-4755-9bbd-55ffffe7e09e",
      "metadata": {},
      "source": [
        "Les primitives de référence de l' Qiskit SDK e effectuent des simulations de vecteurs d'état locaux. Ces simulations ne permettent pas\n",
        "de modéliser le bruit des appareils, mais elles sont utiles pour prototyper rapidement des algorithmes avant de se tourner vers des techniques de simulation\n",
        "plus avancées ( [à l'aide de Qiskit Aer](/docs/guides/simulate-stabilizer-circuits) ) ou de les exécuter sur de vrais appareils ( [primitives IBM Quantum](primitives) ).\n",
        "\n",
        "La primitive Estimateur peut calculer les valeurs d'espérance des circuits, et la primitive Échantillonneur peut échantillonner les distributions de sortie des circuits des distributions de sortie des circuits.\n",
        "\n",
        "Les sections suivantes montrent comment utiliser les primitives de référence pour exécuter votre flux de travail localement.\n",
        "\n",
        "<span id=\"use-the-reference-estimator\" />\n",
        "\n",
        "## Utilisez l'estimateur de référence\n",
        "\n",
        "L'implémentation de référence de `EstimatorV2` dans `qiskit.primitives` , qui fonctionne sur un simulateur local de vecteur d'état, est la suivante est la classe [`StatevectorEstimator`](../api/qiskit/qiskit.primitives.StatevectorEstimator) classe. Il peut prendre des circuits, des observables et des paramètres en entrée et renvoie les valeurs d'espérance calculées localement.\n",
        "\n",
        "Le code suivant prépare les données d'entrée qui seront utilisées dans les exemples suivants. Le type d'entrée attendu pour les\n",
        "observables est [`qiskit.quantum_info.SparsePauliOp`](../api/qiskit/qiskit.quantum_info.SparsePauliOp). Notez que\n",
        "le circuit de l'exemple est paramétré, mais vous pouvez également utiliser Estimator sur des circuits non paramétrés.\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  Tout circuit transmis à un estimateur **ne** doit inclure aucune **mesure**.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "5b41a52d-8f15-4ce4-b3f6-effd91946d9c",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/simulate-with-qiskit-sdk-primitives/extracted-outputs/5b41a52d-8f15-4ce4-b3f6-effd91946d9c-0.svg\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "from qiskit.circuit import Parameter\n",
        "\n",
        "# circuit for which you want to obtain the expected value\n",
        "circuit = QuantumCircuit(2)\n",
        "circuit.ry(Parameter(\"theta\"), 0)\n",
        "circuit.h(0)\n",
        "circuit.cx(0, 1)\n",
        "circuit.draw(\"mpl\", style=\"iqp\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "18658518-304a-49a7-8958-82adef366de6",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.quantum_info import SparsePauliOp\n",
        "import numpy as np\n",
        "\n",
        "# observable(s) whose expected values you want to compute\n",
        "\n",
        "observable = SparsePauliOp([\"II\", \"XX\", \"YY\", \"ZZ\"], coeffs=[1, 1, -1, 1])\n",
        "\n",
        "# value(s) for the circuit parameter(s)\n",
        "parameter_values = [[0], [np.pi / 6], [np.pi / 2]]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4c39904c-d586-41b9-ade0-e6a508ef7c2e",
      "metadata": {},
      "source": [
        "<Admonition type=\"tip\" title=\"Transpile vers les circuits ISA et les observables\">\n",
        "  Le workflow des primitives « IBM Quantum » nécessite que les circuits et les observables soient transformés de manière à n'utiliser que des instructions prises en charge par le QPU (on parle alors de circuits et d'observables *« ISA » (architecture de jeu d'instructions)* ). Les primitives de référence acceptent toujours les instructions abstraites, car elles s'appuient sur des simulations de vecteurs d'état locaux, mais la transcompilation du circuit pourrait tout de même s'avérer bénéfique en termes d'optimisation du circuit.\n",
        "\n",
        "  ```python\n",
        "  # Generate a pass manager without providing a backend\n",
        "  from qiskit.transpiler import generate_preset_pass_manager\n",
        "\n",
        "  pm = generate_preset_pass_manager(optimization_level=1)\n",
        "  isa_circuit = pm.run(circuit)\n",
        "  isa_observable = observable.apply_layout(isa_circuit.layout)\n",
        "  ```\n",
        "</Admonition>\n",
        "\n",
        "<span id=\"initialize-estimator\" />\n",
        "\n",
        "### Initialiser l'estimateur\n",
        "\n",
        "Instancier un [`qiskit.primitives.StatevectorEstimator`](../api/qiskit/qiskit.primitives.StatevectorEstimator).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "56f39026-7874-4f14-8529-b97df373eaf5",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.primitives import StatevectorEstimator\n",
        "\n",
        "estimator = StatevectorEstimator()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9c4c81b1-8b87-450d-9a93-4f209364ee83",
      "metadata": {},
      "source": [
        "<span id=\"run-and-get-results\" />\n",
        "\n",
        "### Courir et obtenir des résultats\n",
        "\n",
        "Cet exemple n'utilise qu'un seul circuit (de type [`QuantumCircuit`](../api/qiskit/qiskit.circuit.QuantumCircuit)) et un observable.\n",
        "\n",
        "Exécutez l'estimation en appelant la méthode [`StatevectorEstimator.run`](../api/qiskit/qiskit.primitives.StatevectorEstimator#run) qui renvoie une instance d'un objet [`PrimitiveJob`](/docs/api/qiskit/qiskit.primitives.PrimitiveJob) objet. Vous pouvez obtenir les résultats du travail (sous la forme d'un [`qiskit.primitives.PrimitiveResult`](../api/qiskit/qiskit.primitives.PrimitiveResult) objet) avec la méthode [`qiskit.primitives.PrimitiveJob.result`](../api/qiskit/qiskit.primitives.PrimitiveJob#result) méthode.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "0c424291-abb3-420c-80e1-a09ecbd6c035",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            " > Result class: <class 'qiskit.primitives.containers.primitive_result.PrimitiveResult'>\n"
          ]
        }
      ],
      "source": [
        "job = estimator.run([(circuit, observable, parameter_values)])\n",
        "result = job.result()\n",
        "print(f\" > Result class: {type(result)}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "871ce7d6-402c-417f-8a83-805d92fa0298",
      "metadata": {},
      "source": [
        "<span id=\"get-the-expected-value-from-the-result\" />\n",
        "\n",
        "#### Obtenir la valeur attendue à partir du résultat\n",
        "\n",
        "Le résultat des primitives génère un tableau [`PubResult`](/docs/api/qiskit/qiskit.primitives.PubResult#pubresult) d'objets, où chaque élément du tableau est un `PubResult` objet qui contient dans ses données le tableau des évaluations correspondant à chaque combinaison observable du circuit dans l' PUB e.\n",
        "\n",
        "Pour récupérer les valeurs d'attente et les métadonnées de la première (et dans ce cas, de la seule) évaluation du circuit, nous devons accéder à l'évaluation [`data`](/docs/api/qiskit/qiskit.primitives.PubResult#data) pour PUB 0 :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "145b3f62-dfaf-4288-8764-f2ecb90e38a1",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            " > Expectation value: [4.         3.73205081 2.        ]\n",
            " > Metadata: {'target_precision': 0.0, 'circuit_metadata': {}}\n"
          ]
        }
      ],
      "source": [
        "print(f\" > Expectation value: {result[0].data.evs}\")\n",
        "print(f\" > Metadata: {result[0].metadata}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e29ec8cc-2c38-464a-a8b3-8f05b282c807",
      "metadata": {},
      "source": [
        "<span id=\"set-estimator-run-options\" />\n",
        "\n",
        "### Définir les options d'exécution de l'estimateur\n",
        "\n",
        "Par défaut, l'estimateur de référence effectue un calcul exact du vecteur d'état sur la base de l'estimateur de référence [`quantum_info.Statevector`](../api/qiskit/qiskit.quantum_info.Statevector) classe.\n",
        "Toutefois, cette méthode peut être modifiée pour introduire l'effet de la surcharge d'échantillonnage (également connue sous le nom de \"bruit de tir\").\n",
        "\n",
        "Estimator accepte un argument `precision` qui exprime les barres d'erreur que l'implémentation primitive doit cibler pour les estimations des valeurs d'espérance devrait cibler pour les estimations des valeurs d'espérance.  Il s'agit de la charge d'échantillonnage, définie exclusivement dans la méthode `.run()` . Cela vous permet d'affiner l'option jusqu'au niveau PUB.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "04047e7a-23f4-431b-8e3a-11edf035e8fc",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Estimate expectation values for two PUBs, both with 0.05 precision.\n",
        "precise_job = estimator.run(\n",
        "    [(circuit, observable, parameter_values)], precision=0.05\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d54f110f-004a-4337-8b4d-7d4287f22be9",
      "metadata": {},
      "source": [
        "Pour un exemple complet, consultez la page [d'exemples d'Estimator](/docs/guides/estimator-examples).\n",
        "\n",
        "<span id=\"use-the-reference-sampler\" />\n",
        "\n",
        "## Utilisez l'échantillonneur de référence\n",
        "\n",
        "L'implémentation de référence de `SamplerV2` dans `qiskit.primitives` est la classe [`StatevectorSampler`](../api/qiskit/qiskit.primitives.StatevectorSampler) classe. Il prend les circuits et les paramètres en entrée et renvoie les résultats de l'échantillonnage des distributions de probabilité de sortie sous la forme d'une distribution de quasi-probabilité des états de sortie.\n",
        "\n",
        "Le code suivant prépare les données d'entrée utilisées dans les exemples qui suivent. Notez que\n",
        "ces exemples exécutent un seul circuit paramétré, mais vous pouvez également utiliser Sampler\n",
        "avec des circuits non paramétrés.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "d4c0ac3b-8e5b-4cde-bb26-256324982c2c",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/simulate-with-qiskit-sdk-primitives/extracted-outputs/d4c0ac3b-8e5b-4cde-bb26-256324982c2c-0.svg\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "\n",
        "circuit = QuantumCircuit(2)\n",
        "circuit.h(0)\n",
        "circuit.cx(0, 1)\n",
        "circuit.measure_all()\n",
        "circuit.draw(\"mpl\", style=\"iqp\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b34ae490-9efb-45f5-937d-3ce86afa445f",
      "metadata": {},
      "source": [
        "<Admonition type=\"note\">\n",
        "  Tout circuit quantique transmis à un échantillonneur **doit** comporter des mesures.\n",
        "</Admonition>\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Transpile vers les circuits ISA et les observables\">\n",
        "  Le workflow des primitives « IBM Quantum » nécessite que les circuits soient transformés de manière à n'utiliser que des instructions prises en charge par le QPU (appelés «circuits ISA»). Les primitives de référence acceptent toujours les instructions abstraites, car elles s'appuient sur des simulations de vecteurs d'état locaux, mais la transcompilation du circuit pourrait tout de même s'avérer bénéfique en termes d'optimisation du circuit.\n",
        "\n",
        "  ```python\n",
        "  # Generate a pass manager without providing a backend\n",
        "  from qiskit.transpiler import generate_preset_pass_manager\n",
        "\n",
        "  pm = generate_preset_pass_manager(optimization_level=1)\n",
        "  isa_circuit = pm.run(qc)\n",
        "  ```\n",
        "</Admonition>\n",
        "\n",
        "<span id=\"initialize-samplerv2\" />\n",
        "\n",
        "### Initialiser `SamplerV2`\n",
        "\n",
        "Instancier [`qiskit.primitives.StatevectorSampler`](../api/qiskit/qiskit.primitives.StatevectorSampler):\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "626177e7-f06a-4216-89c8-daf703520457",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.primitives import StatevectorSampler\n",
        "\n",
        "sampler = StatevectorSampler()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "626fb8d6-75ae-47b2-ac0b-00acc4ce5afe",
      "metadata": {},
      "source": [
        "<span id=\"run-and-get-results\" />\n",
        "\n",
        "### Courir et obtenir des résultats\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "19659756-a01d-42ec-8fa7-d7a1bf2303d5",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            " > Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>\n"
          ]
        }
      ],
      "source": [
        "# execute 1 circuit with Sampler\n",
        "job = sampler.run([circuit])\n",
        "pub_result = job.result()[0]\n",
        "print(f\" > Result class: {type(pub_result)}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ee6d88f3-0115-4ea9-a2c5-633906841d9f",
      "metadata": {},
      "source": [
        "Les primitives acceptent plusieurs PUB comme entrées, et chaque PUB obtient son propre résultat. Par conséquent, vous pouvez faire fonctionner différents circuits avec diverses combinaisons de paramètres/observables et récupérer les résultats sur le site PUB :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "fb91dbfc-0340-4ea6-8d33-95357d7907e3",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            " > Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>\n"
          ]
        }
      ],
      "source": [
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "\n",
        "# create two circuits\n",
        "circuit1 = circuit.copy()\n",
        "circuit2 = circuit.copy()\n",
        "\n",
        "# transpile circuits\n",
        "pm = generate_preset_pass_manager(optimization_level=1)\n",
        "isa_circuit1 = pm.run(circuit1)\n",
        "isa_circuit2 = pm.run(circuit2)\n",
        "# execute 2 circuits using Sampler\n",
        "job = sampler.run([(isa_circuit1), (isa_circuit2)])\n",
        "pub_result_1 = job.result()[0]\n",
        "pub_result_2 = job.result()[1]\n",
        "print(f\" > Result class: {type(pub_result)}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6e7b2199-3c00-477a-a248-824b442431b5",
      "metadata": {},
      "source": [
        "<span id=\"get-the-probability-distribution-or-measurement-outcome\" />\n",
        "\n",
        "### Obtenir la distribution de probabilité ou le résultat de la mesure\n",
        "\n",
        "Les échantillons de résultats de mesure sont renvoyés sous forme de **chaînes de bits** ou de **décomptes**. Les chaînes de bits indiquent les résultats des mesures, en conservant l'ordre des tirs dans lequel ils ont été mesurés. Les objets de résultat de l'échantillonneur organisent les données en termes de noms de registres classiques de leurs circuits d'entrée, pour des raisons de compatibilité avec les circuits dynamiques.\n",
        "\n",
        "<Admonition>\n",
        "  Le nom du registre classique est par défaut `\"meas\"`. Ce nom sera utilisé ultérieurement pour accéder aux chaînes de bits des mesures.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "1dc395b4-5716-44be-9622-7c99df95616b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "        ┌───┐      ░ ┌─┐   \n",
              "   q_0: ┤ H ├──■───░─┤M├───\n",
              "        └───┘┌─┴─┐ ░ └╥┘┌─┐\n",
              "   q_1: ─────┤ X ├─░──╫─┤M├\n",
              "             └───┘ ░  ║ └╥┘\n",
              "meas: 2/══════════════╩══╩═\n",
              "                      0  1 "
            ]
          },
          "execution_count": 11,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Define quantum circuit with 2 qubits\n",
        "circuit = QuantumCircuit(2)\n",
        "circuit.h(0)\n",
        "circuit.cx(0, 1)\n",
        "circuit.measure_all()\n",
        "circuit.draw()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "27a2847b-6553-4c73-9b8a-85ba28725ed8",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The number of bitstrings is: 1024\n",
            "The counts are: {'00': 538, '11': 486}\n"
          ]
        }
      ],
      "source": [
        "# Transpile circuit\n",
        "pm = generate_preset_pass_manager(optimization_level=1)\n",
        "isa_circuit = pm.run(circuit)\n",
        "# Run using Sampler\n",
        "result = sampler.run([circuit]).result()\n",
        "# Access result data for PUB 0\n",
        "data_pub = result[0].data\n",
        "# Access bitstring for the classical register \"meas\"\n",
        "bitstrings = data_pub.meas.get_bitstrings()\n",
        "print(f\"The number of bitstrings is: {len(bitstrings)}\")\n",
        "# Get counts for the classical register \"meas\"\n",
        "counts = data_pub.meas.get_counts()\n",
        "print(f\"The counts are: {counts}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "de705fab-e718-4924-a03f-f19bdf6578d8",
      "metadata": {},
      "source": [
        "<span id=\"change-run-options\" />\n",
        "\n",
        "### Modifier les options d'exécution\n",
        "\n",
        "Par défaut, l'échantillonneur de référence effectue un calcul exact du vecteur d'état en se basant sur la méthode [`quantum_info.Statevector`](../api/qiskit/qiskit.quantum_info.Statevector) classe.\n",
        "Toutefois, cette méthode peut être modifiée pour introduire l'effet de la surcharge d'échantillonnage (également connue sous le nom de \"bruit de tir\"). Pour aider à gérer cette surcharge, l'interface de l'échantillonneur accepte un argument `shots` qui peut être défini au niveau PUB.\n",
        "\n",
        "Cet exemple suppose que vous avez défini deux circuits.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "927faaab-60c0-4b73-bf53-72f7c4c9ad65",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<qiskit.primitives.primitive_job.PrimitiveJob at 0x7f94d59db010>"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Sample two circuits at 128 shots each.\n",
        "sampler.run([isa_circuit1, isa_circuit2], shots=128)\n",
        "# Sample two circuits at different amounts of shots. The \"None\"s are necessary\n",
        "# as placeholders\n",
        "# for the lack of parameter values in this example.\n",
        "sampler.run([(isa_circuit1, None, 123), (isa_circuit2, None, 456)])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1c0e76fe-4b5d-4fd4-9eec-da5332d76cfb",
      "metadata": {},
      "source": [
        "Pour un exemple complet, consultez la page [d'exemples de Sampler](/docs/guides/sampler-examples).\n",
        "\n",
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Etapes suivantes\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recommandations\">\n",
        "  * Pour une simulation plus performante capable de gérer des circuits plus grands, ou pour incorporer des modèles de bruit dans votre simulation, voir [Simulation exacte et bruyante avec les primitives Qiskit Aer.](simulate-with-qiskit-aer)\n",
        "  * Pour savoir comment utiliser Quantum Composer pour la simulation, consultez le guide [IBM Quantum Composer](/docs/guides/composer).\n",
        "  * Lire la référence de l ['API de Qiskit Estimator](/docs/api/qiskit/1.4/qiskit.primitives.Estimator).\n",
        "  * Lisez la référence de l ['API Qiskit Sampler](/docs/api/qiskit/1.4/qiskit.primitives.Sampler).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "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"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 4
}