{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "49f6841f-a9e9-4a44-a162-a0147bd2f300",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Diagonalização quântica baseada em amostras\"\n",
        "description: \"Os componentes importantes da diagonalização quântica baseada em amostras (SQD) são discutidos, assim como as melhores práticas. O método é implementado na lição seguinte.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore PVDZ eigvals eigvecs eigh */}\n",
        "\n",
        "<span id=\"sample-based-quantum-diagonalization-sqd\" />\n",
        "\n",
        "# Diagonalização Quântica Baseada em Amostra (SQD)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "65614f74-fe5b-4b8b-8118-ef9ac0847f34",
      "metadata": {},
      "source": [
        "A diagonalização quântica baseada em amostras (SQD) combina a álgebra linear clássica e o poder da computação quântica para diagonalizar um Hamiltoniano (matriz) e calcular seus valores e vetores próprios. A diagonalização de matrizes é uma operação matemática importante, pois muitos problemas em ciência, computação e otimização usam o método.\n",
        "\n",
        "O vídeo abaixo apresenta uma visão geral do SQD, o que determina sua utilidade e o que o torna mais rápido do que muitas outras abordagens. O texto subsequente fornece mais detalhes.\n",
        "\n",
        "<IBMVideo id=\"134325501\" title=\"Neste vídeo, Chris Porter apresenta uma visão geral da diagonalização quântica do subespaço. Ele descreve quais fatores influenciam sua eficiência e utilidade. Ele explica o que o torna mais rápido do que outras abordagens. O texto abaixo fornece mais detalhes.\" />\n",
        "\n",
        "<span id=\"1-introduction-and-motivation\" />\n",
        "\n",
        "## 1. Introdução e motivação\n",
        "\n",
        "Considere a equação de valor próprio de energia, famosa por Schrödinger, como exemplo.\n",
        "\n",
        "$$\n",
        "H \\vert \\psi \\rangle = E \\vert \\psi \\rangle\n",
        "$$\n",
        "\n",
        "$H$ é o Hamiltoniano de um sistema, $|\\psi\\rangle$ é a função de onda (também conhecida como estado próprio) e $E$ é um valor próprio. Os valores próprios da matriz $H$ representam os níveis de energia do sistema. Por exemplo, se o sistema for uma molécula, o menor valor próprio representa a energia do estado fundamental da molécula. Em muitos problemas, estamos interessados na estimativa de energia do estado fundamental.\n",
        "\n",
        "Ao aplicar técnicas de diagonalização exata da álgebra linear, podemos diagonalizar a matriz $H$ completa. No entanto, a abordagem se torna computacionalmente cara (até mesmo impossível) à medida que a matriz se torna maior. Por exemplo, mesmo para moléculas químicas pequenas, $H$ pode ser proibitivamente grande (por exemplo, o Hamiltoniano para a molécula $N_2$ com uma base cc-PVDZ tem uma dimensão de $65780 \\times 65780).$\n",
        "\n",
        "Felizmente, nem sempre precisamos de todos os valores e vetores próprios de um Hamiltoniano $H$ e, portanto, a diagonalização da matriz completa não é necessária em muitos casos práticos. Por exemplo, no caso da estimativa do estado do solo, estamos interessados no menor valor próprio e no vetor próprio correspondente. Isso nos permite aplicar o conceito de projeção em um subespaço (útil).\n",
        "\n",
        "Considere uma matriz $N \\times N$, $H$, em que o espaço vetorial completo (espaço de Hilbert) tem uma dimensão de $N$ ( $N$ é grande). Em seguida, selecionamos um subespaço ( $\\mathcal{S}$ ) - que é um subconjunto do espaço de Hilbert completo - de dimensão $M$, em que $M$ é suficientemente pequeno. Após projetar $H$ nesse subespaço, a matriz projetada (digamos, $H_\\mathcal{S}$ ) será menor ( $M \\times M$ ). A $H_\\mathcal{S}$ menor pode ser diagonalizada usando um método numérico clássico adequado e gerar valores e vetores próprios para esse subespaço.\n",
        "\n",
        "**Observe que o subespaço deve ser *compatível com* o nosso estado próprio alvo (por exemplo, solo).** Em outras palavras, o Hamiltoniano projetado $H_\\mathcal{S}$ deve estar em um subespaço que inclua o menor valor próprio.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7f39fb3a",
      "metadata": {},
      "source": [
        "<span id=\"2-projection-and-diagonalization\" />\n",
        "\n",
        "## 2. Projeção e diagonalização\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f8afe151-c2cf-4184-92c7-6a679f6be6e0",
      "metadata": {},
      "source": [
        "Considere que queremos encontrar o menor valor próprio e o vetor próprio correspondente para a seguinte matriz $8 \\times 8$ Hamiltoniana $H$.\n",
        "\n",
        "$$\n",
        "H = \\begin{bmatrix}\n",
        " 0.2235 & -0.0390 & -0.1035 & -0.0818 &  0.1746 &  0.1091 &  0.1165 & -0.0104 \\\\\n",
        "-0.0390 &  0.6621 &  0.0706 & -0.1964 & -0.0782 &  0.2619 &  0.1095 &  0.0029 \\\\\n",
        "-0.1035 &  0.0706 &  0.9961 &  0.1724 &  0.1067 & -0.2299 & -0.1817 &  0.1571 \\\\\n",
        "-0.0818 & -0.1964 &  0.1724 & -0.1773 &  0.1019 & -0.4778 & -0.1272 & -0.0414 \\\\\n",
        " 0.1746 & -0.0782 &  0.1067 &  0.1019 &  0.1418 & -0.1359 & -0.1793 & -0.0766 \\\\\n",
        " 0.1091 &  0.2619 & -0.2299 & -0.4778 & -0.1359 &  0.1014 &  0.1696 &  0.0552 \\\\\n",
        " 0.1165 &  0.1095 & -0.1817 & -0.1272 & -0.1793 &  0.1696 &  0.4227 &  0.2702 \\\\\n",
        "-0.0104 &  0.0029 &  0.1571 & -0.0414 & -0.0766 &  0.0552 &  0.2702 &  0.4456 \\\\\n",
        "\\end{bmatrix}\n",
        "$$\n",
        "\n",
        "Diagonalizaremos a matriz completa juntamente com diferentes versões projetadas ( $H_\\mathcal{S}$ ) para diferentes subespaços a fim de demonstrar a escalabilidade e a importância da escolha do subespaço.\n",
        "\n",
        "A energia do estado fundamental (valor próprio mínimo) da matriz $H$ é $-0.5357$ e a função de onda *exata* do estado fundamental (vetor próprio) é:\n",
        "\n",
        "$$\n",
        "\\text{GS}_{\\text{exact}} = 0.8 * |011\\rangle + 0.6 * |101\\rangle.\n",
        "$$\n",
        "\n",
        "ou seja, o estado fundamental da matriz é abrangido por dois estados (vetores) da base computacional $\\vert 011 \\rangle$ e $\\vert 101 \\rangle$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "b663cbd4-7836-4ec5-9236-fd8299f86fac",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Eigenvalues:\n",
            "[-0.5357 -0.1321  0.1049  0.1258  0.3616  0.6405  0.947   1.3039]\n",
            "Minimum eigenvalue: -0.5356560029438817\n",
            "\n",
            "Eigenvectors (columns represent vectors):\n",
            "[[-0.     -0.5612  0.098  -0.0024  0.8051 -0.0806  0.0643  0.1288]\n",
            " [-0.     -0.1403 -0.1985 -0.4249 -0.0092  0.585  -0.5952  0.2526]\n",
            " [ 0.      0.0416  0.3041  0.2122  0.1509 -0.0139 -0.5794 -0.7086]\n",
            " [ 0.8    -0.1936 -0.0127 -0.4376 -0.1081 -0.0838  0.1557 -0.2966]\n",
            " [ 0.      0.6716 -0.3535 -0.2552  0.5395  0.0954  0.1449 -0.1941]\n",
            " [ 0.6     0.258   0.017   0.5834  0.1441  0.1118 -0.2076  0.3954]\n",
            " [ 0.      0.3088  0.5504 -0.4197  0.0626 -0.468  -0.2625  0.3657]\n",
            " [-0.     -0.1146 -0.6559  0.0356 -0.0394 -0.6352 -0.3856  0.0418]]\n",
            "\n",
            "Eigenvector for the minimum eigenvalue (ground state)\n",
            "[-0.  -0.   0.   0.8  0.   0.6  0.  -0. ]\n"
          ]
        }
      ],
      "source": [
        "import numpy as np\n",
        "from scipy.linalg import eigh\n",
        "\n",
        "np.set_printoptions(precision=4, sign=\"-\", suppress=True, linewidth=100)\n",
        "\n",
        "H = np.array(\n",
        "    [\n",
        "        [0.2235, -0.039, -0.1035, -0.0818, 0.1746, 0.1091, 0.1165, -0.0104],\n",
        "        [-0.0390, 0.6621, 0.0706, -0.1964, -0.0782, 0.2619, 0.1095, 0.0029],\n",
        "        [-0.1035, 0.0706, 0.9961, 0.1724, 0.1067, -0.2299, -0.1817, 0.1571],\n",
        "        [-0.0818, -0.1964, 0.1724, -0.1773, 0.1019, -0.4778, -0.1272, -0.0414],\n",
        "        [0.1746, -0.0782, 0.1067, 0.1019, 0.1418, -0.1359, -0.1793, -0.0766],\n",
        "        [0.1091, 0.2619, -0.2299, -0.4778, -0.1359, 0.1014, 0.1696, 0.0552],\n",
        "        [0.1165, 0.1095, -0.1817, -0.1272, -0.1793, 0.1696, 0.4227, 0.2702],\n",
        "        [-0.0104, 0.0029, 0.1571, -0.0414, -0.0766, 0.0552, 0.2702, 0.4456],\n",
        "    ]\n",
        ")\n",
        "eigvals, eigvecs = eigh(H)\n",
        "\n",
        "print(\"Eigenvalues:\")\n",
        "print(eigvals)\n",
        "print(f\"Minimum eigenvalue: {eigvals.min()}\")\n",
        "\n",
        "print(\"\\nEigenvectors (columns represent vectors):\")\n",
        "print(eigvecs)\n",
        "print(\"\\nEigenvector for the minimum eigenvalue (ground state)\")\n",
        "print(eigvecs[:, np.argmin(eigvals)])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cfaa9d24",
      "metadata": {},
      "source": [
        "Em seguida, projetaremos a matriz $H$ em diferentes subespaços e verificaremos se podemos obter o estado fundamental exato. Em particular, projetaremos a matriz em um subespaço abrangido por:\n",
        "\n",
        "1. vetores exatos do estado fundamental ( $\\vert 011 \\rangle$ e $\\vert 101 \\rangle$ ).\n",
        "2. vetores que excluem alguns ou todos os vetores exatos do estado fundamental (por exemplo, $\\vert 000 \\rangle$, $\\vert 011 \\rangle$ e $\\vert 110 \\rangle$ ).\n",
        "3. vetores que incluem tanto o estado exato do solo quanto o estado fora do solo (mas não todos os vetores possíveis no espaço de Hilbert).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8dc65713-f90d-4376-a3e3-d5a91d1c48a9",
      "metadata": {},
      "source": [
        "<span id=\"21-case-1-subspace-includes-ground-state\" />\n",
        "\n",
        "### 2.1 Case-1: O subespaço inclui o estado fundamental\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ceaef659-9aff-436b-a3bb-67a7e71f86e6",
      "metadata": {},
      "source": [
        "Suponha que queiramos projetar $H$ em um subespaço ( $\\mathcal{S}$ ) abrangido por dois vetores $x_1 = |011\\rangle$ e $x_2 = |101\\rangle$. O Hamiltoniano projetado é definido por:\n",
        "\n",
        "$$\n",
        "H_\\mathcal{S} = \\begin{bmatrix}\n",
        "\\langle x1 | H | x1 \\rangle & \\langle x1 | H | x2 \\rangle \\\\\n",
        "\\langle x2 | H | x1 \\rangle & \\langle x2 | H | x2 \\rangle\n",
        "\\end{bmatrix}\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "284bcb6a-1e59-4d8c-a2bb-ac8c6c6ee480",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[-0.1773 -0.4778]\n",
            " [-0.4778  0.1014]]\n"
          ]
        }
      ],
      "source": [
        "x1 = np.zeros(8)\n",
        "x1[3] = 1  # binary 011 is 3 in decimal. |011> = |3> = [0,0,0,1,0,0,0,0]\n",
        "\n",
        "x2 = np.zeros(8)\n",
        "x2[5] = 1  # binary 101 is 5 in decimal\n",
        "\n",
        "Hs = np.array([[x1 @ H @ x1.T, x1 @ H @ x2.T], [x2 @ H @ x1.T, x2 @ H @ x2.T]])\n",
        "print(Hs)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "86324b23-c718-4d58-afda-528b25fc1907",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Minimum eigenvalue: -0.535656000064295\n",
            "Eigenvector for minimum eigenvalue: [-0.8 -0.6]\n"
          ]
        }
      ],
      "source": [
        "eigvals, eigvecs = eigh(Hs)\n",
        "print(f\"Minimum eigenvalue: {eigvals.min()}\")\n",
        "print(f\"Eigenvector for minimum eigenvalue: {eigvecs[:,np.argmin(eigvals)]}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f86d7235-a177-40c5-9852-7a1652c90997",
      "metadata": {},
      "source": [
        "Podemos fazer várias observações importantes aqui.\n",
        "\n",
        "* Como abrangemos o subespaço com dois vetores, a dimensão da matriz projetada ( $H_\\mathcal{S}$ ) é $2 \\times 2$, que é menor do que a matriz completa $H$ ( $8 \\times 8$ ).\n",
        "* O autovalor mínimo da matriz projetada corresponde ao autovalor exato do estado fundamental.\n",
        "* Os valores na variável `eigvecs` denotam a amplitude dos vetores de abrangência do subespaço e, com eles, podemos reconstruir o estado próprio (estado fundamental). Nesse caso, terminamos com o estado fundamental exato (até uma fase global):\n",
        "\n",
        "$$\n",
        "|\\psi \\rangle = - (0.8 |011\\rangle + 0.6 |101\\rangle)\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "927650d6-0b89-4356-b7a1-9106c43123a0",
      "metadata": {},
      "source": [
        "<span id=\"22-case-2-subspace-excludes-some-or-all-ground-state-vectors\" />\n",
        "\n",
        "### 2.2 Case-2: O subespaço exclui alguns ou todos os vetores do estado fundamental\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8d88696d-bc3d-4df1-9475-760ddaf89341",
      "metadata": {},
      "source": [
        "Em seguida, projetamos $H$ em um subespaço abrangido por três vetores $x_1 = |000\\rangle$, $x_2 = |011\\rangle$ e $x_3 = |110\\rangle$. Escolhemos deliberadamente os vetores de modo a excluir um vetor de estado fundamental ( $\\vert 101 \\rangle$ ). O Hamiltoniano projetado é definido por:\n",
        "\n",
        "$$\n",
        "H_\\mathcal{S} = \\begin{bmatrix}\n",
        "\\langle x1 | H | x1 \\rangle & \\langle x1 | H | x2 \\rangle & \\langle x1 | H | x3 \\rangle\\\\\n",
        "\\langle x2 | H | x1 \\rangle & \\langle x2 | H | x2 \\rangle & \\langle x2 | H | x3 \\rangle \\\\\n",
        "\\langle x3 | H | x1 \\rangle & \\langle x3 | H | x2 \\rangle & \\langle x3 | H | x3 \\rangle \\\\\n",
        "\\end{bmatrix}\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "bb6a7677-d840-4d89-bf46-8522774f071f",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[ 0.2235 -0.0818  0.1165]\n",
            " [-0.0818 -0.1773 -0.1272]\n",
            " [ 0.1165 -0.1272  0.4227]]\n"
          ]
        }
      ],
      "source": [
        "x1 = np.zeros(8)\n",
        "x1[0] = 1\n",
        "\n",
        "x2 = np.zeros(8)\n",
        "x2[3] = 1\n",
        "\n",
        "x3 = np.zeros(8)\n",
        "x3[6] = 1\n",
        "\n",
        "Hs = np.array(\n",
        "    [\n",
        "        [x1 @ H @ x1.T, x1 @ H @ x2.T, x1 @ H @ x3.T],\n",
        "        [x2 @ H @ x1.T, x2 @ H @ x2.T, x2 @ H @ x3.T],\n",
        "        [x3 @ H @ x1.T, x3 @ H @ x2.T, x3 @ H @ x3.T],\n",
        "    ]\n",
        ")\n",
        "print(Hs)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "48472db8-6d73-46f6-a332-c0185e2b6792",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Minimum eigenvalue: -0.21108858736702252\n"
          ]
        }
      ],
      "source": [
        "eigvals, eigvecs = eigh(Hs)\n",
        "print(f\"Minimum eigenvalue: {eigvals.min()}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "224ec6f4-2f0d-49bc-8810-bf9b34c59f71",
      "metadata": {},
      "source": [
        "O valor próprio $-0.2111$ nesse caso não corresponde ao valor próprio mínimo $-0.5357$ do Hamiltoniano completo. A principal observação aqui é: se projetarmos em um subespaço que exclui os estados da base em nosso estado-alvo (fundamental) - parcial ou totalmente - o estado fundamental estimado será diferente do exato.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a50be3fa-dade-4c76-ba07-29cea5a4cc64",
      "metadata": {},
      "source": [
        "<span id=\"23-case-3-subspace-includes-both-ground-state-and-non-ground-state-vectors\" />\n",
        "\n",
        "### 2.3 Case-3: O subespaço inclui vetores no estado fundamental e fora do estado fundamental\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f954b02e-a807-4c7f-9ec8-02d825430311",
      "metadata": {},
      "source": [
        "Em seguida, mostramos um caso em que o subespaço é abrangido por vetores que incluem vetores exatos do estado fundamental junto com vetores indesejados. Suponha que nosso subespaço seja abrangido por $x_1 = |011\\rangle$, $x_2 = |101\\rangle$ (presente no estado fundamental exato) e $x_3 = |111\\rangle$ (ausente no estado fundamental exato).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "a317bd5b-c8ad-4578-b288-e5dd234a9330",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[-0.1773 -0.4778 -0.0414]\n",
            " [-0.4778  0.1014  0.0552]\n",
            " [-0.0414  0.0552  0.4456]]\n"
          ]
        }
      ],
      "source": [
        "x1 = np.zeros(8)\n",
        "x1[3] = 1\n",
        "\n",
        "x2 = np.zeros(8)\n",
        "x2[5] = 1\n",
        "\n",
        "x3 = np.zeros(8)\n",
        "x3[7] = 1\n",
        "\n",
        "Hs = np.array(\n",
        "    [\n",
        "        [x1 @ H @ x1.T, x1 @ H @ x2.T, x1 @ H @ x3.T],\n",
        "        [x2 @ H @ x1.T, x2 @ H @ x2.T, x2 @ H @ x3.T],\n",
        "        [x3 @ H @ x1.T, x3 @ H @ x2.T, x3 @ H @ x3.T],\n",
        "    ]\n",
        ")\n",
        "print(Hs)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "f3e65657-b373-4a97-8b9b-42c47aaaca24",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Minimum eigenvalue: -0.53565600006461\n",
            "Eigenvector for minimum eigenvalue: [ 0.8  0.6 -0. ]\n"
          ]
        }
      ],
      "source": [
        "eigvals, eigvecs = eigh(Hs)\n",
        "print(f\"Minimum eigenvalue: {eigvals.min()}\")\n",
        "print(f\"Eigenvector for minimum eigenvalue: {eigvecs[:,np.argmin(eigvals)]}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "88dfda4c-102e-4597-8a81-243df6a03641",
      "metadata": {},
      "source": [
        "Nesse caso, obtemos novamente $-0.5357$ como o valor próprio mínimo que corresponde à matriz completa (ou seja, o estado fundamental exato). Outro resultado interessante é a amplitude de $x_3$ retornada pelo processo de projeção e diagonalização. A amplitude é $0$, e quando reconstruímos a função de onda (estado próprio) com amplitudes e vetores computados, obtemos:\n",
        "\n",
        "$$\n",
        "\\vert \\psi \\rangle = 0.8 |011\\rangle + 0.6 |101\\rangle + 0.0 |111\\rangle = 0.8 |011\\rangle + 0.6 |101\\rangle \\left( \\text{exact ground state} \\right)\n",
        "$$\n",
        "\n",
        "Assim, mesmo que nosso subespaço inclua alguns vetores não-alvo (junto com o conjunto completo de vetores-alvo), podemos calcular o valor próprio e o estado próprio corretos, já que o processo de projeção e diagonalização filtra os vetores não-alvo definindo suas amplitudes para $0$. Essa propriedade do SQD proporciona uma tolerância inerente ao ruído.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4730d906-3503-4a6c-951c-a1984bfb4238",
      "metadata": {},
      "source": [
        "<span id=\"3-role-of-quantum-in-sqd\" />\n",
        "\n",
        "## 3. Papel da mecânica quântica no SQD\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3aa30fe6",
      "metadata": {},
      "source": [
        "As análises acima estabelecem a importância dos vetores de abrangência do subespaço, que devem ser *compatíveis com o estado-alvo*. Isso levanta uma questão importante: **Como escolhemos os vetores com suporte ao estado-alvo para a construção do subespaço?**\n",
        "\n",
        "É nesse ponto que **os computadores quânticos** entram em ação. A sinergia quântica-clássica funciona da seguinte forma no paradigma SQD:\n",
        "\n",
        "1. Usando um circuito quântico adequado, tentamos preparar um estado em um computador quântico que gerará estados de base nos quais a função de onda alvo (por exemplo, estado fundamental) tem suporte significativo. Os estados da base amostrada (bitstrings) abrangerão o subespaço para a projeção hamiltoniana.\n",
        "2. Um computador clássico projeta o Hamiltoniano no subespaço (abrangido por amostras/vetores do computador quântico) e o diagonaliza para calcular os valores e vetores próprios usando métodos numéricos adequados.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2fdcc9e5-ca63-4a97-8a28-330cd81c95dc",
      "metadata": {},
      "source": [
        "![Um diagrama dos componentes quânticos e clássicos do SQD. Na mecânica quântica, você prepara e faz uma amostragem do seu suporte-alvo; em seguida, projeta classicamente sua matriz no subespaço amostrado e diagonaliza a matriz projetada.](https://quantum.cloud.ibm.com/learning/images/courses/quantum-diagonalization-algorithms/sqd1/sqd1-fig1.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4ffbfb55-863f-480e-a21c-d929c810396b",
      "metadata": {},
      "source": [
        "Pode haver várias maneiras de preparar esse estado quântico, e elas podem ser variacionais ou não variacionais, dependendo do problema.\n",
        "\n",
        "Nas próximas duas lições, mostraremos dois exemplos específicos de preparação de estados e amostragem a partir deles.\n",
        "\n",
        "1. Na lição 4, usaremos um ansatz de Jastrow acoplado unitário local parametrizado (LUCJ) para gerar amostras para um problema químico (estimativa da energia do estado fundamental da molécula $N_2$ ). Inicializaremos o ansatz LUCJ com parâmetros da computação clássica de cluster singles e doubles (CCSD) acoplados.\n",
        "2. Na lição 5, faremos uma amostragem dos estados da base de Krylov para abranger o subespaço de um problema de física de matéria condensada. Essa abordagem é de natureza não-variacional.\n",
        "\n",
        "Além das abordagens específicas do problema acima, uma abordagem genérica para a preparação do estado envolve um ansatz variacional, em que atualizaremos iterativamente os parâmetros do ansatz usando um otimizador clássico.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1aa0044f-d798-4510-bd67-736a3e574695",
      "metadata": {},
      "source": [
        "![Um fluxograma que ilustra o processo desde um circuito quântico variacional, passando pela amostragem quântica, até a computação clássica, na qual a matriz é projetada e diagonalizada. Em seguida, os resultados são inseridos em um otimizador clássico, que seleciona novos parâmetros variacionais, e voltamos ao circuito quântico variacional.](https://quantum.cloud.ibm.com/learning/images/courses/quantum-diagonalization-algorithms/sqd1/sqd1-fig2.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5addd026",
      "metadata": {},
      "source": [
        "As amostras de computadores quânticos tolerantes a falhas anteriores podem ser ruidosas. O SQD emprega um processo de recuperação de configuração autoconsistente para corrigir amostras com ruído [\\[1\\]](#references). Discutiremos o processo de recuperação de configuração em mais detalhes e o aplicaremos para corrigir amostras ruidosas iterativamente para refinar a estimativa de energia do estado fundamental para um problema químico na Lição 4.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4af71b3d-2749-417f-b42d-4d7bbc4930b6",
      "metadata": {},
      "source": [
        "<span id=\"31-notes-on-ground-state-support\" />\n",
        "\n",
        "### 3.1 Notas sobre o suporte ao estado fundamental\n",
        "\n",
        "Vamos explicar melhor o conceito de *suporte de estado básico*. O suporte do estado fundamental pode ser definido como o conjunto de estados básicos em que o estado fundamental tem amplitude diferente de zero (até um limite de corte).\n",
        "\n",
        "Suponha que o estado fundamental exato de um problema de $3$ -qubit seja\n",
        "\n",
        "$$\n",
        "\\vert \\psi \\rangle = \\frac{1}{\\sqrt{2}} \\vert 000 \\rangle + \\frac{1}{\\sqrt{2}} \\vert 111 \\rangle\n",
        "$$\n",
        "\n",
        "Se fizermos uma amostragem do estado acima, devemos obter um conjunto de estados de base computacional $\\{\\vert 000 \\rangle$, $\\vert 111 \\rangle \\}$ (outros estados de base computacional têm amplitude zero no estado fundamental e, portanto, idealmente não aparecerão durante a amostragem).\n",
        "\n",
        "Idealmente, o conjunto de vetores de base para esses estados consiste em $\\{ \\vert 000 \\rangle, \\vert 111 \\rangle \\}$ (em outras palavras, o subespaço desse estado é abrangido por esses dois vetores de base).\n",
        "\n",
        "Na prática, não precisamos preparar o estado fundamental exato, pois a amostragem de muitos outros estados pode nos dar o mesmo conjunto de vetores. Por exemplo:\n",
        "\n",
        "$$\n",
        "\\begin{align}\n",
        "\\vert \\psi_a \\rangle = 0.8 \\vert 000 \\rangle + 0.6 \\vert 111 \\rangle &\\xrightarrow{\\text{Sampling}} \\{ \\vert 000 \\rangle, \\vert 111 \\rangle \\} \\\\\n",
        "\\vert \\psi_b \\rangle = \\frac{1}{2} \\vert 000 \\rangle + \\frac{\\sqrt{3}}{2} \\vert 111 \\rangle &\\xrightarrow{\\text{Sampling}} \\{ \\vert 000 \\rangle, \\vert 111 \\rangle \\} \\\\\n",
        "\\vert \\psi_c \\rangle = \\frac{1}{2} \\vert 000 \\rangle + \\frac{1}{2} \\vert 111 \\rangle + \\frac{1}{\\sqrt{2}} \\vert 101 \\rangle &\\xrightarrow{\\text{Sampling}} \\{ \\vert 000 \\rangle, \\vert 101 \\rangle, \\vert 111 \\rangle \\}\n",
        "\\end{align}\n",
        "$$\n",
        "\n",
        "A preparação e a amostragem de qualquer um dos estados acima gerarão vetores que têm amplitude diferente de zero no estado fundamental, e todos eles se qualificam como tendo *suporte do estado fundamental*. Observe que a amostragem $\\vert \\psi_c \\rangle$ inclui um vetor extra $\\vert 101 \\rangle$ que tem $0$ amplitude no estado fundamental exato. No entanto, mostramos anteriormente que a inclusão desses vetores no subespaço não é problemática, pois a operação de projeção e diagonalização define a amplitude dos vetores indesejados como $0$, e podemos obter o valor próprio esperado e reconstruir o estado próprio correto.\n",
        "\n",
        "![Diagramas de apoios corretos e incorretos para o ansatz. Um bom ansatz possui um suporte que contém integralmente o suporte do estado fundamental. Um ansatz inadequado contém apenas parte ou nenhuma do suporte do estado fundamental.](https://quantum.cloud.ibm.com/learning/images/courses/quantum-diagonalization-algorithms/sqd1/sqd1-fig3.avif)\n",
        "\n",
        "Portanto, a preparação e a amostragem do estado fundamental exato não são necessárias. Na verdade, isso pode ser difícil, pois o estado fundamental exato não é conhecido *a priori* e, muitas vezes, é vantajoso **não** preparar e coletar amostras do estado fundamental exato, especialmente se a função de onda (estado) for distorcida, com alguns estados básicos tendo probabilidades muito altas. Considere a seguinte função de onda:\n",
        "\n",
        "$$\n",
        "\\vert \\psi \\rangle = 0.7 \\vert 000 \\rangle - 0.7 \\vert 010 \\rangle + 0.1 \\vert 101 \\rangle - 0.01 \\vert 111 \\rangle\n",
        "$$\n",
        "\n",
        "Essa é uma função de onda distorcida em que os estados básicos $\\vert 000 \\rangle$ e $\\vert 010 \\rangle$ têm amplitudes muito maiores em comparação com $\\vert 101 \\rangle$ e $\\vert 111 \\rangle$. Quando amostrados, obteremos $\\vert 000 \\rangle$ e $\\vert 010 \\rangle$ com mais frequência ( $\\text{sampling probability} = \\vert \\text{amplitude} \\vert^{2}$ $\\approx 49\\%$ para $\\vert 000 \\rangle$ e $\\vert 010 \\rangle$ cada, $\\approx 1\\%$ para $\\vert 101 \\rangle$, e $\\approx 0.01\\%$ para $\\vert 111 \\rangle$ ). Com um orçamento de amostragem finito (*disparos* ), é muito provável que nosso conjunto de amostragem contenha apenas $\\vert 000 \\rangle$ e $\\vert 010 \\rangle$. Conforme mostrado anteriormente, se abrangermos o subespaço com esse conjunto de vetores ausentes, não conseguiremos encontrar o valor próprio mínimo verdadeiro. Portanto, será benéfico (e necessário) coletar amostras de um estado com suporte de estado fundamental.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "127754c4-74e1-4583-b252-ddd07cef23cd",
      "metadata": {},
      "source": [
        "<span id=\"32-a-case-against-uniform-sampling\" />\n",
        "\n",
        "### 3.2 Um caso contra a amostragem uniforme\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5a51c9ce-356d-4ff0-956e-3fd42654d974",
      "metadata": {},
      "source": [
        "Pode ser tentador extrair amostras de uma distribuição uniforme para abranger o subespaço. Embora isso possa funcionar para problemas pequenos, começará a falhar em problemas maiores e mais práticos. Para problemas grandes com muitos qubits, o espaço de Hilbert pode ser proibitivamente grande. Por exemplo, um espaço de Hilbert de 32 qubits tem mais de $4$ bilhões de vetores de base possíveis ( $2^{32} = 4,294,967,296$ ). Se fizermos uma amostragem uniforme desse espaço com um orçamento de amostragem finito (digamos, $10000$ vetores para manter o processo de diagonalização viável), o subespaço poderá excluir vetores com suporte de estado fundamental com mais frequência, pois o processo será aleatório. **Portanto, precisamos de uma maneira sistemática de obter amostras do suporte do estado fundamental, aproveitando os circuitos quânticos.**\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7ad257c8-834e-4343-b37a-5ff7d082b778",
      "metadata": {},
      "source": [
        "<span id=\"4-sqd-and-sparsity-of-the-wave-function\" />\n",
        "\n",
        "## 4. SQD e esparsidade da função de onda\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5094d2ae-c6fc-4e13-b476-ca7ebfd0c356",
      "metadata": {},
      "source": [
        "A lacuna entre o espaço de Hilbert completo e as dimensões do subespaço viável traz outro aspecto importante da SQD, que é a esparsidade da função de onda. A abordagem SQD funciona bem para funções de onda esparsas ou concentradas em que uma pequena fração dos estados da base tem amplitudes não negligenciáveis. Há dois motivos por trás disso:\n",
        "\n",
        "1. Se a função de onda for ampla (ou seja, muitos estados da base têm amplitudes não negligenciáveis) e não incluirmos vetores com suporte de estado-alvo no subespaço, poderemos acabar com valores e vetores próprios incorretos.\n",
        "2. Para evitar o problema acima, precisamos incluir muitos vetores no subespaço. No entanto, a dimensão do Hamiltoniano projetado está diretamente relacionada à dimensão do subespaço. Um subespaço maior significará um Hamiltoniano maior, cuja diagonalização pode se tornar inviável.\n",
        "\n",
        "Mostramos o problema com a seguinte matriz ( $H_{new}$ ). O menor valor próprio do $H_{new}$ é $-2.2081$, e a função de onda correspondente (estado próprio) é ampla:\n",
        "\n",
        "$$\n",
        "|\\psi\\rangle = \\frac{|000\\rangle + |001\\rangle + |010\\rangle + |011\\rangle + |100\\rangle + |101\\rangle + |110\\rangle + |111\\rangle}{\\sqrt{8}}\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "0a53cd27-478c-44e8-907e-9dc67093bc95",
      "metadata": {},
      "outputs": [],
      "source": [
        "H_new = np.array(\n",
        "    [\n",
        "        [-0.958, 0.1853, -0.2663, -0.3875, -0.0524, -0.3779, -0.0145, -0.3369],\n",
        "        [0.1853, -0.4081, -0.8549, -0.2312, 0.0615, -0.2493, -0.3804, -0.3312],\n",
        "        [-0.2663, -0.8549, -0.6929, -0.0063, -0.0478, -0.0236, -0.2494, -0.0669],\n",
        "        [-0.3875, -0.2312, -0.0063, -0.4468, -0.6301, -0.4627, -0.1188, 0.0753],\n",
        "        [-0.0524, 0.0615, -0.0478, -0.6301, -0.6664, -0.1514, -0.3571, -0.3644],\n",
        "        [-0.3779, -0.2493, -0.0236, -0.4627, -0.1514, -0.9605, 0.0137, 0.0035],\n",
        "        [-0.0145, -0.3804, -0.2494, -0.1188, -0.3571, 0.0137, -1.1449, 0.0433],\n",
        "        [-0.3369, -0.3312, -0.0669, 0.0753, -0.3644, 0.0035, 0.0433, -1.2307],\n",
        "    ]\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "751bd77e",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Minimum eigenvalue: -2.208137504726661\n",
            "Eigenvector for minimum eigenvalue: [0.3536 0.3536 0.3536 0.3536 0.3535 0.3536 0.3535 0.3535]\n"
          ]
        }
      ],
      "source": [
        "eigvals, eigvecs = eigh(H_new)\n",
        "print(f\"Minimum eigenvalue: {eigvals.min()}\")\n",
        "print(f\"Eigenvector for minimum eigenvalue: {eigvecs[:,np.argmin(eigvals)]}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "331030ee-7831-4b41-8863-e9c8647124a9",
      "metadata": {},
      "source": [
        "Suponha que projetemos o $H_{new}$ em um subespaço abrangido por quatro vetores: $|000\\rangle$, $|010\\rangle$, $|101\\rangle$, e $|110\\rangle$ e calcular o valor próprio.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "2591384f-ba2e-46ee-a607-4ac401f342a9",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[-0.958  -0.2663 -0.3779 -0.0145]\n",
            " [-0.2663 -0.6929 -0.0236 -0.2494]\n",
            " [-0.3779 -0.0236 -0.9605  0.0137]\n",
            " [-0.0145 -0.2494  0.0137 -1.1449]]\n"
          ]
        }
      ],
      "source": [
        "x1 = np.zeros(8)\n",
        "x1[0] = 1\n",
        "\n",
        "x2 = np.zeros(8)\n",
        "x2[2] = 1\n",
        "\n",
        "x3 = np.zeros(8)\n",
        "x3[5] = 1\n",
        "\n",
        "x4 = np.zeros(8)\n",
        "x4[6] = 1\n",
        "\n",
        "H_new_s = np.array(\n",
        "    [\n",
        "        [x1 @ H_new @ x1.T, x1 @ H_new @ x2.T, x1 @ H_new @ x3.T, x1 @ H_new @ x4.T],\n",
        "        [x2 @ H_new @ x1.T, x2 @ H_new @ x2.T, x2 @ H_new @ x3.T, x2 @ H_new @ x4.T],\n",
        "        [x3 @ H_new @ x1.T, x3 @ H_new @ x2.T, x3 @ H_new @ x3.T, x3 @ H_new @ x4.T],\n",
        "        [x4 @ H_new @ x1.T, x4 @ H_new @ x2.T, x4 @ H_new @ x3.T, x4 @ H_new @ x4.T],\n",
        "    ]\n",
        ")\n",
        "print(H_new_s)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "0f388339-cf0b-4584-bf83-90afb3ec13cc",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Minimum eigenvalue: -1.4266552340586673\n"
          ]
        }
      ],
      "source": [
        "eigvals, eigvecs = eigh(H_new_s)\n",
        "print(f\"Minimum eigenvalue: {eigvals.min()}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "27b0d944-57a8-4cf5-a207-2363fa69a298",
      "metadata": {},
      "source": [
        "O exemplo acima mostra que, quando a função de onda é ampla e não incluímos estados básicos no subespaço, o cálculo do valor próprio fica incorreto.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9cbdb4ce-fb6f-49e4-8eff-649d40345366",
      "metadata": {},
      "source": [
        "<span id=\"5-sqd-vs-vqe\" />\n",
        "\n",
        "## 5. SQD vs. VQE\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8abdcd50-617b-40a6-82db-f1495078ca4e",
      "metadata": {},
      "source": [
        "Conforme observado anteriormente, o SQD pode precisar de um circuito quântico variacional e de atualizações iterativas de parâmetros para preparar e obter amostras do suporte do estado fundamental. Como essa rotina de atualização iterativa de parâmetros é semelhante à VQE, pode-se perguntar como esses métodos são diferentes e quais são as vantagens do SQD em relação à VQE? Nesta seção, comparamos os métodos e discutimos as vantagens do SQD com uma molécula $N_2$ descrita com um conjunto de base mínima (`sto-3g`) como exemplo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0de2cec1-65b2-4186-9ea1-fab2fd48f413",
      "metadata": {},
      "source": [
        "|                             | VQE                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | SQD                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |\n",
        "| :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n",
        "| Custos indiretos de medição | **Muitos termos de Pauli, muitos circuitos de medição:** O Hamiltoniano da molécula tem $2951$ termos de Pauli exclusivos. Como os termos de Pauli podem conter termos $X$ e $Y$, e as medições quânticas típicas são feitas na base $Z$, precisamos mudar a base de medição para avaliar esses termos. Quando otimizados para medições, os termos de $2951$ podem ser agrupados em $1187$ grupos, onde cada grupo pode ser avaliado usando um único circuito. Portanto, precisamos de pelo menos $1187$ circuitos exclusivos para avaliar todos os termos de Pauli. **Muitos disparos por circuito para uma variação mais estreita**. Novamente, o valor da expectativa avaliada de cada termo de Pauli tem uma variação associada a ele que depende inversamente de $\\sqrt{shots}$. Portanto, para estimar com precisão cada termo, precisamos alocar muitas fotos por circuito. Por exemplo, para obter precisão química ( $1$ kcal/mol), normalmente precisamos de disparos da ordem de $10^5$ - $10^7$ por circuito. Portanto, o VQE precisa de muitos circuitos de medição e cada circuito com um determinado número de disparos. Em casos práticos, essa sobrecarga de medição pode ser restritiva. | No SQD, não precisamos de circuitos de medição diferentes para cada termo de Pauli agrupado. Normalmente, medimos um único circuito para um número fixo de disparos. Embora possamos definir o número de disparos para um valor grande, dependendo do problema, a sobrecarga permanece muito menor do que a do VQE. Além disso, as estimativas de energia que usam o processo de diagonalização são exatas, o que significa que os autovalores computados são exatos nesse subespaço e não têm uma variância associada a eles, como o VQE. (No caso da amostragem de estado da base de Krylov (Lição 5), precisamos medir vários circuitos, mas o número de circuitos permanece muito menor do que o VQE). |\n",
        "| Limite de energia estimado  | No VQE, as estimativas de energia não são limitadas e podem ser inferiores aos valores mínimos reais devido ao ruído.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | O processo de estimativa de energia no SQD sempre produz um limite superior para a energia do estado fundamental, e a energia estimada nunca será menor do que a verdadeira energia do estado fundamental.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\n",
        "| Tolerância ao ruído         | A estimativa de energia VQE é suscetível ao ruído de computadores quânticos tolerantes a falhas.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | A SQD tem tolerância inerente a ruídos. Os computadores quânticos tolerantes a falhas podem produzir amostras com ruídos. Mesmo se incluirmos essas amostras no subespaço, a diagonalização subsequente pode suprimir essas amostras, definindo suas amplitudes como zero. Além disso, discutiremos um método chamado *recuperação de configuração* em relação ao SQD que melhora ainda mais a tolerância ao ruído do SQD.                                                                                                                                                                                                                                                                                 |\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "de8f1df2-1ef9-4eea-9621-e17db8489c4c",
      "metadata": {},
      "source": [
        "<span id=\"6-summary\" />\n",
        "\n",
        "## 6. Resumo\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "103dfaf0-89c9-4b5d-bd70-fcdba4b5b4da",
      "metadata": {},
      "source": [
        "1. Na SQD, um computador quântico gera amostras e um computador clássico projeta um Hamiltoniano em um subespaço abrangido pelas amostras e o diagonaliza para calcular valores e vetores próprios.\n",
        "2. As amostras geradas devem ser do suporte do estado-alvo (solo).\n",
        "3. Dependendo do problema, a preparação do estado quântico e o fluxo de geração de amostras podem ser iterativos ou não iterativos.\n",
        "4. O SQD funciona melhor para funções de onda esparsas. Uma função de onda ampla exigirá um subespaço grande para soluções precisas, o que torna a projeção clássica e a operação de diagonalização caras.\n",
        "5. O SQD tem várias vantagens sobre o VQE, como menor sobrecarga de medição e limite superior para a energia estimada do estado fundamental, o que o torna mais escalável.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "56c2ca63",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Referências\n",
        "\n",
        "\\[1] J. Robledo-Moreno et al, \"Química além das soluções exatas em um supercomputador centrado em quantum\" (2024). [arXiv:quant-ph/2405.05068](https://arxiv.org/abs/2405.05068).\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": 5
}