{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "e0cf0747",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Algoritmos quânticos variacionais\"\n",
        "description: \"Este tutorial fornece uma visão geral de um algoritmo híbrido quântico-clássico, VQE e QAOA\"\n",
        "---\n",
        "\n",
        "<span id=\"quantum-algorithms-variational-quantum-algorithms\" />\n",
        "\n",
        "# Algoritmos quânticos: Algoritmos quânticos variacionais\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  Takashi Imamichi (24 de maio de 2024)\n",
        "\n",
        "  [Baixe o pdf](https://ibm.ent.box.com/s/blnffu0pd7yzxarq3zc3w0jv90365ny2) da palestra original. Observe que alguns trechos de código podem se tornar obsoletos, pois são imagens estáticas.\n",
        "\n",
        "  *O tempo aproximado da QPU para executar esse experimento é de 9 minutos (testado em um processador Eagle).*\n",
        "\n",
        "  (este notebook pode não ser avaliado no tempo permitido no Open Plan. Por favor, use os recursos de computação quântica com sabedoria)\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "59fbaadc",
      "metadata": {},
      "source": [
        "<span id=\"1-introduction\" />\n",
        "\n",
        "## 1. Introdução\n",
        "\n",
        "Este tutorial fornece uma visão geral de um algoritmo híbrido quântico-clássico, concentrando-se especificamente no eigensolver quântico variacional (VQE) e no algoritmo de otimização aproximada quântica (QAOA). O principal objetivo desses algoritmos é lidar com problemas de otimização empregando circuitos quânticos com portas quânticas parametrizadas.\n",
        "\n",
        "Apesar dos avanços na computação quântica, a presença de ruído nos dispositivos quânticos atuais dificulta a extração de resultados significativos dos circuitos quânticos profundos. Para superar esse desafio, o VQE e o QAOA adotam uma abordagem híbrida quântica-clássica, que envolve a execução iterativa de circuitos quânticos relativamente curtos usando a computação quântica e a otimização dos parâmetros dos circuitos quânticos parametrizados de destino usando a computação clássica.\n",
        "\n",
        "O QAOA tem o potencial de fornecer as soluções ideais para os problemas-alvo em escala de serviços públicos, graças à aplicação de várias técnicas de atenuação e supressão de erros. A VQE tem muitas aplicações (como a química quântica) nas quais é menos dimensionável. Mas várias abordagens relacionadas a valores próprios surgiram para complementar e aumentar a VQE, incluindo a diagonalização do subespaço de Krylov e a diagonalização quântica baseada em amostragem (SQD). Entender o VQE é uma primeira etapa importante para compreender a ampla gama de algoritmos híbridos clássico-quânticos que surgiram.\n",
        "\n",
        "Este módulo descreve os conceitos fundamentais e a implementação do VQE e do QAOA. Outros tutoriais explorarão tópicos avançados e técnicas para aumentar o tamanho desses algoritmos.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d3f4500f",
      "metadata": {},
      "source": [
        "Você precisa da seguinte biblioteca em seu ambiente para executar este notebook.\n",
        "Se ainda não o tiver instalado, você poderá instalá-lo retirando o comentário e executando a seguinte célula.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2a8e650f",
      "metadata": {},
      "outputs": [],
      "source": [
        "# % pip install 'qiskit[visualization]' qiskit-ibm-runtime"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "be601762",
      "metadata": {},
      "source": [
        "<span id=\"2-computing-the-minimum-eigenvalue-of-a-simple-hamiltonian\" />\n",
        "\n",
        "## 2. Cálculo do valor próprio mínimo de um hamiltoniano simples\n",
        "\n",
        "Começaremos aplicando o VQE a um caso muito simples, para ver como ele funciona. Calcularemos o valor próprio mínimo da matriz Pauli $Z$ com VQE. Começaremos importando alguns pacotes gerais.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "e8f398b2",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "from qiskit.circuit import ParameterVector, QuantumCircuit\n",
        "from qiskit.primitives import StatevectorEstimator, StatevectorSampler\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from scipy.optimize import minimize"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d4395012",
      "metadata": {},
      "source": [
        "Agora definimos o operador de interesse e o visualizamos em forma de matriz.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "a4f374a2",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[ 1.+0.j,  0.+0.j],\n",
              "       [ 0.+0.j, -1.+0.j]])"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "op = SparsePauliOp(\"Z\")\n",
        "op.to_matrix()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0a001e71",
      "metadata": {},
      "source": [
        "É fácil obter os valores próprios de forma clássica, portanto, podemos verificar nosso trabalho. Isso pode se tornar difícil à medida que nos aproximamos da utilidade. Aqui usamos o numpy.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "c9188662",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Eigenvalues: [-1.  1.]\n"
          ]
        }
      ],
      "source": [
        "# compute eigenvalues with numpy\n",
        "result = np.linalg.eigh(op.to_matrix())\n",
        "print(\"Eigenvalues:\", result.eigenvalues)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8f9af345",
      "metadata": {},
      "source": [
        "Para obter valores próprios usando um algoritmo quântico variacional, construímos um circuito com portas que recebem parâmetros variacionais:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "99d5d36b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/99d5d36b-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# define a variational form\n",
        "param = ParameterVector(\"a\", 3)\n",
        "qc = QuantumCircuit(1, 1)\n",
        "qc.u(param[0], param[1], param[2], 0)\n",
        "qc_estimator = qc.copy()\n",
        "qc.measure(0, 0)\n",
        "qc.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9607ab51",
      "metadata": {},
      "source": [
        "Se quisermos estimar o valor da expectativa de um operador (como $Z$ ), devemos usar o Estimator. Se quisermos examinar os estados do sistema, usaremos o Sampler.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "df8ed8d3",
      "metadata": {},
      "outputs": [],
      "source": [
        "sampler = StatevectorSampler()\n",
        "estimator = StatevectorEstimator()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "880bccfb",
      "metadata": {},
      "source": [
        "Podemos calcular contagens de cadeias de bits 0 e 1 com valores de parâmetros aleatórios `[1, 2, 3]` usando o Sampler.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "5301ee71",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'0': 783, '1': 241}"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# compute counts of bitstrings with random parameter values by Sampler\n",
        "result = sampler.run([(qc, [1, 2, 3])]).result()\n",
        "counts = result[0].data.c.get_counts()\n",
        "counts"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "30ef9b8f",
      "metadata": {},
      "source": [
        "Sabemos que podemos calcular o valor da expectativa de Z pelo site $\\langle Z \\rangle = p_0 - p_1$ com probabilidades $\\{0: p_0, 1: p_1\\}$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "0f220c8d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "0.529296875"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# compute the expectation value of Z based on the counts\n",
        "(counts.get(\"0\", 0) - counts.get(\"1\", 0)) / sum(counts.values())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "695ee4bd",
      "metadata": {},
      "source": [
        "Esse circuito funcionou, mas os valores dos parâmetros escolhidos não corresponderam a um estado de energia muito baixa (ou de baixo valor próprio). O valor próprio obtido é um pouco maior que o mínimo. O resultado é semelhante quando se usa o estimador.\n",
        "\n",
        "Observe que o Estimator usa circuitos quânticos sem medições.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "c9e05530",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array(0.54030231)"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "result = estimator.run([(qc_estimator, op, [1, 2, 3])]).result()\n",
        "result[0].data.evs"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b538e8b5",
      "metadata": {},
      "source": [
        "Será necessário pesquisar os parâmetros e encontrar aqueles que produzem o menor valor próprio.\n",
        "Criamos uma função para receber os valores dos parâmetros da forma variacional e retornar o valor esperado $\\langle Z \\rangle$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "97fead46",
      "metadata": {},
      "outputs": [],
      "source": [
        "# define a cost function to look for the minimum eigenvalue of Z\n",
        "def cost(x):\n",
        "    result = sampler.run([(qc, x)]).result()\n",
        "    counts = result[0].data.c.get_counts()\n",
        "    expval = (counts.get(\"0\", 0) - counts.get(\"1\", 0)) / sum(counts.values())\n",
        "    # the following line shows the trajectory of the optimization\n",
        "    print(expval, counts)\n",
        "    return expval"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a97eaeba",
      "metadata": {},
      "source": [
        "Vamos aplicar a função SciPy's `minimize` para encontrar o valor próprio mínimo de Z.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "44f56300",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "1.0 {'0': 1024}\n",
            "0.494140625 {'0': 765, '1': 259}\n",
            "0.466796875 {'0': 751, '1': 273}\n",
            "0.564453125 {'0': 801, '1': 223}\n",
            "-0.4296875 {'1': 732, '0': 292}\n",
            "-0.984375 {'1': 1016, '0': 8}\n",
            "-0.8984375 {'1': 972, '0': 52}\n",
            "-0.990234375 {'1': 1019, '0': 5}\n",
            "-0.892578125 {'1': 969, '0': 55}\n",
            "-0.986328125 {'1': 1017, '0': 7}\n",
            "-0.861328125 {'1': 953, '0': 71}\n",
            "-1.0 {'1': 1024}\n",
            "-0.982421875 {'1': 1015, '0': 9}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-0.986328125 {'1': 1017, '0': 7}\n",
            "-1.0 {'1': 1024}\n",
            "-0.990234375 {'1': 1019, '0': 5}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-1.0 {'1': 1024}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.994140625 {'1': 1021, '0': 3}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              " message: Optimization terminated successfully.\n",
              " success: True\n",
              "  status: 1\n",
              "     fun: -1.0\n",
              "       x: [ 3.182e+00  1.338e+00  1.664e-01]\n",
              "    nfev: 63\n",
              "   maxcv: 0.0"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# minimize the cost function with scipy's minimize\n",
        "min_result = minimize(cost, [0, 0, 0], method=\"COBYLA\", tol=1e-8)\n",
        "min_result"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "4198224e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'0': 1, '1': 1023}"
            ]
          },
          "execution_count": 11,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# check counts of bitstrings with the optimal parameters\n",
        "result = sampler.run([(qc, min_result.x)]).result()\n",
        "result[0].data.c.get_counts()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d4e77e29",
      "metadata": {},
      "source": [
        "<span id=\"21-exercise\" />\n",
        "\n",
        "### 2.1 Exercício\n",
        "\n",
        "Calcule o valor próprio mínimo de $Z \\otimes Z$ com VQE.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "86a2d76d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "SparsePauliOp(['ZZ'],\n",
            "              coeffs=[1.+0.j])\n",
            "[[ 1.+0.j  0.+0.j  0.+0.j  0.+0.j]\n",
            " [ 0.+0.j -1.+0.j  0.+0.j  0.+0.j]\n",
            " [ 0.+0.j  0.+0.j -1.+0.j  0.+0.j]\n",
            " [ 0.+0.j  0.+0.j  0.+0.j  1.+0.j]]\n"
          ]
        }
      ],
      "source": [
        "z2 = SparsePauliOp(\"ZZ\")\n",
        "print(z2)\n",
        "print(z2.to_matrix())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "76092648-3b28-442e-8319-8a8416e0c9d5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# compute eigenvalues with numpy"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "f92a23b5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# define a variational form\n",
        "# qc = ..."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "496572fb",
      "metadata": {
        "scrolled": true
      },
      "outputs": [],
      "source": [
        "# compute counts of bitstrings with a random parameter values by Sampler\n",
        "# result = sampler.run(...)\n",
        "# result"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "b4cad5d0",
      "metadata": {},
      "outputs": [],
      "source": [
        "# compute the expectation value of ZZ based on the counts"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "5ce555fe",
      "metadata": {},
      "outputs": [],
      "source": [
        "# verify the expectation value of ZZ with Estimator"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "c210c435",
      "metadata": {},
      "outputs": [],
      "source": [
        "# define a cost function to look for the minimum eigenvalue of ZZ\n",
        "# def cost(x):\n",
        "#    expval = ...\n",
        "#    return expval"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "a75f3303",
      "metadata": {},
      "outputs": [],
      "source": [
        "# minimize the cost function with scipy's minimize\n",
        "# min_result = minimize(cost, [...], method=\"COBYLA\", tol=1e-8)\n",
        "# min_result"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "8c476943",
      "metadata": {},
      "outputs": [],
      "source": [
        "# check counts of bitstrings with the optimal parameter values\n",
        "# result = sampler.run(qc, min_result.x).result()\n",
        "# result"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b82ba33a",
      "metadata": {},
      "source": [
        "<span id=\"solutions-of-the-exercise\" />\n",
        "\n",
        "#### Soluções do exercício\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7f2e0a4e",
      "metadata": {},
      "source": [
        "Definimos o operador de interesse e o exibimos em forma de matriz.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "a1f9d371",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "SparsePauliOp(['ZZ'],\n",
            "              coeffs=[1.+0.j])\n",
            "[[ 1.+0.j  0.+0.j  0.+0.j  0.+0.j]\n",
            " [ 0.+0.j -1.+0.j  0.+0.j  0.+0.j]\n",
            " [ 0.+0.j  0.+0.j -1.+0.j  0.+0.j]\n",
            " [ 0.+0.j  0.+0.j  0.+0.j  1.+0.j]]\n"
          ]
        }
      ],
      "source": [
        "z2 = SparsePauliOp(\"ZZ\")\n",
        "print(z2)\n",
        "print(z2.to_matrix())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b123e9d9",
      "metadata": {},
      "source": [
        "Para obter valores próprios usando um algoritmo quântico variacional, construímos um circuito com portas que recebem parâmetros variacionais:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "7d5e894a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/7d5e894a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# define a variational form\n",
        "param = ParameterVector(\"a\", 6)\n",
        "qc = QuantumCircuit(2, 2)\n",
        "qc.u(param[0], param[1], param[2], 0)\n",
        "qc.u(param[3], param[4], param[5], 1)\n",
        "qc_estimator = qc.copy()\n",
        "qc.measure([0, 1], [0, 1])\n",
        "qc.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f1d86563",
      "metadata": {},
      "source": [
        "Se quisermos estimar o valor da expectativa de um operador (como $Z \\otimes Z$ ), usaremos o Estimator. Se quisermos examinar os estados do sistema, usaremos o Sampler.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "e3bda5ff",
      "metadata": {},
      "outputs": [],
      "source": [
        "sampler = StatevectorSampler()\n",
        "estimator = StatevectorEstimator()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "aeac09f7",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'10': 661, '11': 203, '01': 47, '00': 113}"
            ]
          },
          "execution_count": 16,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# compute counts of bitstrings with random parameter values by Sampler\n",
        "result = sampler.run([(qc, [1, 2, 3, 4, 5, 6])]).result()\n",
        "counts = result[0].data.c.get_counts()\n",
        "counts"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "c96acbdf",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "-0.3828125"
            ]
          },
          "execution_count": 17,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# compute the expectation value of ZZ based on the counts\n",
        "(\n",
        "    counts.get(\"00\", 0)\n",
        "    - counts.get(\"01\", 0)\n",
        "    - counts.get(\"10\", 0)\n",
        "    + counts.get(\"11\", 0)\n",
        ") / sum(counts.values())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d2cc2ed8",
      "metadata": {},
      "source": [
        "Esse circuito funcionou, mas os valores dos parâmetros escolhidos não corresponderam a um estado de energia muito baixa (ou de baixo valor próprio). O valor próprio obtido é um pouco maior que o mínimo. O resultado é semelhante quando se usa o estimador.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "474b84c5",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array(-0.35316516)"
            ]
          },
          "execution_count": 18,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# verify the expectation value of ZZ with Estimator\n",
        "result = estimator.run([(qc_estimator, z2, [1, 2, 3, 4, 5, 6])]).result()\n",
        "result[0].data.evs"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "25f4a655",
      "metadata": {},
      "source": [
        "Será necessário pesquisar os parâmetros e encontrar aqueles que produzem o menor valor próprio.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "f29322f3",
      "metadata": {},
      "outputs": [],
      "source": [
        "# define a cost function to look for the minimum eigenvalue of ZZ\n",
        "def cost(x):\n",
        "    result = sampler.run([(qc, x)]).result()\n",
        "    counts = result[0].data.c.get_counts()\n",
        "    expval = (\n",
        "        counts.get(\"00\", 0)\n",
        "        - counts.get(\"01\", 0)\n",
        "        - counts.get(\"10\", 0)\n",
        "        + counts.get(\"11\", 0)\n",
        "    ) / sum(counts.values())\n",
        "    print(expval, counts)\n",
        "    return expval"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "12d8ba03",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "1.0 {'00': 1024}\n",
            "0.578125 {'00': 808, '01': 216}\n",
            "0.5234375 {'00': 780, '01': 244}\n",
            "0.548828125 {'00': 793, '01': 231}\n",
            "0.3515625 {'00': 637, '10': 164, '11': 55, '01': 168}\n",
            "0.3359375 {'00': 638, '11': 46, '10': 174, '01': 166}\n",
            "0.283203125 {'00': 602, '10': 181, '01': 186, '11': 55}\n",
            "-0.087890625 {'01': 414, '00': 184, '10': 143, '11': 283}\n",
            "0.236328125 {'10': 27, '11': 623, '01': 364, '00': 10}\n",
            "-0.0625 {'11': 261, '01': 403, '00': 219, '10': 141}\n",
            "0.248046875 {'01': 366, '11': 628, '00': 11, '10': 19}\n",
            "-0.0625 {'10': 145, '11': 254, '01': 399, '00': 226}\n",
            "0.228515625 {'01': 373, '11': 609, '00': 20, '10': 22}\n",
            "0.0546875 {'11': 376, '10': 273, '01': 211, '00': 164}\n",
            "-0.447265625 {'01': 731, '10': 10, '11': 267, '00': 16}\n",
            "-0.71484375 {'01': 871, '11': 99, '00': 47, '10': 7}\n",
            "-0.46484375 {'01': 741, '00': 253, '10': 9, '11': 21}\n",
            "-0.87890625 {'01': 962, '00': 39, '11': 23}\n",
            "-0.640625 {'00': 176, '01': 837, '11': 8, '10': 3}\n",
            "-0.88671875 {'01': 966, '00': 41, '11': 17}\n",
            "-0.994140625 {'01': 1021, '11': 3}\n",
            "-0.91796875 {'01': 982, '11': 35, '00': 7}\n",
            "-0.994140625 {'01': 1021, '11': 2, '00': 1}\n",
            "-0.939453125 {'01': 993, '00': 31}\n",
            "-0.990234375 {'01': 1019, '11': 5}\n",
            "-0.90234375 {'01': 974, '00': 21, '11': 29}\n",
            "-0.98046875 {'01': 1014, '11': 10}\n",
            "-0.994140625 {'01': 1021, '00': 3}\n",
            "-0.990234375 {'01': 1019, '11': 4, '00': 1}\n",
            "-0.98828125 {'01': 1018, '11': 6}\n",
            "-0.990234375 {'01': 1019, '11': 4, '00': 1}\n",
            "-0.994140625 {'01': 1021, '11': 2, '00': 1}\n",
            "-0.99609375 {'01': 1022, '11': 2}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-0.99609375 {'01': 1022, '00': 2}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-0.99609375 {'01': 1022, '00': 1, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-0.99609375 {'01': 1022, '11': 1, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-0.994140625 {'01': 1021, '00': 3}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-0.99609375 {'01': 1022, '11': 2}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.99609375 {'01': 1022, '11': 2}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              " message: Optimization terminated successfully.\n",
              " success: True\n",
              "  status: 1\n",
              "     fun: -0.998046875\n",
              "       x: [ 3.167e+00  6.940e-01  1.033e+00 -2.894e-02  8.933e-01\n",
              "            1.885e+00]\n",
              "    nfev: 128\n",
              "   maxcv: 0.0"
            ]
          },
          "execution_count": 20,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              " message: Optimization terminated successfully.\n",
              " success: True\n",
              "  status: 1\n",
              "     fun: -0.99609375\n",
              "       x: [ 3.098e+00 -5.402e-01  1.091e+00 -1.004e-02  3.615e-01\n",
              "            6.913e-01]\n",
              "    nfev: 115\n",
              "   maxcv: 0.0"
            ]
          },
          "execution_count": 30,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# minimize the cost function with scipy's minimize\n",
        "min_result = minimize(cost, [0, 0, 0, 0, 0, 0], method=\"COBYLA\", tol=1e-8)\n",
        "min_result"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d3e2de8d",
      "metadata": {},
      "source": [
        "Obtivemos um valor próprio extremamente próximo do mínimo que nos foi fornecido pela numpy.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "d34a8544",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'01': 1024}"
            ]
          },
          "execution_count": 21,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# check counts of bitstrings with the optimal parameters\n",
        "result = sampler.run([(qc, min_result.x)]).result()\n",
        "result[0].data.c.get_counts()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bc51e7bf-e582-49ba-93f8-035624d56ccf",
      "metadata": {},
      "source": [
        "<span id=\"3-quantum-optimization-with-qiskit-patterns\" />\n",
        "\n",
        "## 3. Otimização quântica com padrões Qiskit\n",
        "\n",
        "Neste tutorial, aprenderemos sobre os padrões Qiskit e a otimização quântica aproximada. Um padrão Qiskit é um conjunto intuitivo e repetível de etapas para implementar um fluxo de trabalho de computação quântica:\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "636ed1de-fc34-4cdd-9398-6fbd7c7fc9c6",
      "metadata": {},
      "source": [
        "![\"Função Qiskit\"](https://quantum.cloud.ibm.com/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/qiskit-function.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8d3218ca-ce9e-40b4-a041-1b1d09bac8f5",
      "metadata": {},
      "source": [
        "Aplicaremos esses padrões ao contexto da **otimização combinatória** e mostraremos como resolver o problema **do corte máximo** utilizando o **Algoritmo de Otimização Aproximada Quântica (QAOA)**, um método iterativo híbrido (quântico-clássico).\n",
        "\n",
        "Observe que essa parte do QAOA é baseada na \"Parte 1: QAOA em pequena escala\" do tutorial [do algoritmo de otimização aproximada quântica](/docs/tutorials/quantum-approximate-optimization-algorithm). Consulte o tutorial para saber como aumentar a escala.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1e943b1a-218a-468c-bb63-4269896ebebe",
      "metadata": {},
      "source": [
        "<span id=\"31-small-scale-qiskit-pattern-for-optimization\" />\n",
        "\n",
        "### 3.1 Padrão Qiskit (em pequena escala) para otimização\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "68fd0b4f-baa4-45dc-9f4c-d9cdff01a651",
      "metadata": {},
      "source": [
        "Esta seção utilizará um problema de corte máximo em pequena escala para ilustrar as etapas necessárias para resolver um problema de otimização usando um computador quântico.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "74b92ba5-c48a-405c-9c4b-04e985a7afbc",
      "metadata": {},
      "source": [
        "O problema do corte máximo é um problema de otimização difícil de resolver (mais especificamente, é um problema NP-difícil) com diversas aplicações em agrupamento, ciência das redes e física estatística. Este tutorial considera um grafo composto por nós conectados por arestas e tem como objetivo dividir os nós em dois conjuntos por meio do \"corte\" de arestas, de modo a maximizar o número de arestas cortadas.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "887eb6ea-f58e-482f-8965-953a08fceecf",
      "metadata": {},
      "source": [
        "![\"Maxcut\"](https://quantum.cloud.ibm.com/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/maxcut.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "893a25f2",
      "metadata": {},
      "source": [
        "Para contextualizar antes de traduzir esse problema em um algoritmo quântico, é possível compreender melhor como o problema do corte máximo se torna um problema clássico de otimização combinatória ao considerar, em primeiro lugar, a minimização de uma função $f(x)$\n",
        "\n",
        "$$\n",
        "\\min_{x\\in \\{0, 1\\}^n}f(x),\n",
        "$$\n",
        "\n",
        "em que a entrada $x$ é um vetor cujos componentes correspondem a cada nó de um gráfico.  Em seguida, restrinja cada um desses componentes para que sejam $0$ ou $1$ (que representam a inclusão ou não inclusão no corte). Este caso de exemplo em pequena escala usa um gráfico com $n=5$ nós.\n",
        "\n",
        "Você poderia escrever uma função de um par de nós $i,j$ que indica se a borda correspondente $(i,j)$ está no corte. Por exemplo, a função $x_i + x_j - 2 x_i x_j$ é 1 somente se um dos dois $x_i$ ou $x_j$ for 1 (o que significa que a borda está no corte) e zero caso contrário. O problema de maximizar as bordas no corte pode ser formulado como\n",
        "\n",
        "$$\n",
        "\\max_{x\\in \\{0, 1\\}^n} \\sum_{(i,j)} x_i + x_j - 2 x_i x_j,\n",
        "$$\n",
        "\n",
        "que pode ser reescrito como uma minimização da forma\n",
        "\n",
        "$$\n",
        "\\min_{x\\in \\{0, 1\\}^n} \\sum_{(i,j)}  2 x_i x_j - x_i - x_j.\n",
        "$$\n",
        "\n",
        "O mínimo de $f(x)$ nesse caso é quando o número de bordas percorridas pelo corte é máximo. Como você pode ver, ainda não há nada relacionado à computação quântica. Você precisa reformular esse problema em algo que um computador quântico possa entender.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e2105a90-027f-44d7-97d1-c2c99373d488",
      "metadata": {},
      "source": [
        "Inicialize seu problema criando um gráfico com $n=5$ nós.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "d3c0dfa7",
      "metadata": {},
      "outputs": [],
      "source": [
        "import matplotlib\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "import rustworkx as rx\n",
        "from rustworkx.visualization import mpl_draw"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "99e763fe",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/99e763fe-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "n = 5\n",
        "\n",
        "graph = rx.PyGraph()\n",
        "graph.add_nodes_from(range(1, n + 1))\n",
        "edge_list = [\n",
        "    (0, 1, 1.0),\n",
        "    (0, 2, 1.0),\n",
        "    (1, 2, 1.0),\n",
        "    (1, 3, 1.0),\n",
        "    (2, 4, 1.0),\n",
        "    (3, 4, 1.0),\n",
        "]\n",
        "graph.add_edges_from(edge_list)\n",
        "pos = rx.spring_layout(graph, seed=2)\n",
        "mpl_draw(graph, node_size=600, pos=pos, with_labels=True, labels=str)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a06e4386-d7bd-4914-9baa-36a5cc60e3ab",
      "metadata": {},
      "source": [
        "<span id=\"32-step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "### 3.2 Passo 1. Mapeie entradas clássicas para um problema quântico\n",
        "\n",
        "A primeira etapa do padrão é mapear o problema clássico (gráfico) em **circuitos** e **operadores** quânticos. Para isso, há três etapas principais a serem seguidas:\n",
        "\n",
        "1. Utilize uma série de reformulações matemáticas para representar esse problema usando a notação de problemas Quadratic Unconstrained Binary Optimization (QUBO).\n",
        "2. Reescreva o problema de otimização como um Hamiltoniano para o qual o estado fundamental corresponde à solução que minimiza a função de custo.\n",
        "3. Crie um circuito quântico que preparará o estado fundamental desse Hamiltoniano por meio de um processo semelhante ao recozimento quântico.\n",
        "\n",
        "**Observação:** Na metodologia QAOA, você deseja ter um operador (**Hamiltoniano** ) que represente a **função de custo** do nosso algoritmo híbrido, bem como um circuito parametrizado (**Ansatz** ) que represente os estados quânticos com soluções candidatas para o problema. Você pode fazer uma amostragem desses estados candidatos e, em seguida, avaliá-los usando a função de custo.\n",
        "\n",
        "<span id=\"graph-→-optimization-problem\" />\n",
        "\n",
        "#### Gráfico → problema de otimização\n",
        "\n",
        "A primeira etapa do mapeamento é uma mudança de notação. A seguir, expressamos o problema na notação QUBO:\n",
        "\n",
        "$$\n",
        "\\min_{x\\in \\{0, 1\\}^n}x^T Q x,\n",
        "$$\n",
        "\n",
        "onde $Q$ é uma matriz $n\\times n$ de números reais, $n$ corresponde ao número de nós em seu gráfico, $x$ é o vetor de variáveis binárias apresentado acima e $x^T$ indica a transposição do vetor $x$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c00a3493-abab-46d8-86a9-5b853979a575",
      "metadata": {},
      "source": [
        "```\n",
        "Problem name: maxcut\n",
        "\n",
        "Minimize\n",
        "  2*x_1*x_2 + 2*x_1*x_3 + 2*x_2*x_3 + 2*x_2*x_4 + 2*x_3*x_5 + 2*x_4*x_5 - 2*x_1\n",
        "  - 3*x_2 - 3*x_3 - 2*x_4 - 2*x_5\n",
        "\n",
        "Subject to\n",
        "  No constraints\n",
        "\n",
        "  Binary variables (5)\n",
        "    x_1 x_2 x_3 x_4 x_5\n",
        "```\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a5b9e551-38a1-4543-b9f1-caaefb0ef3a9",
      "metadata": {},
      "source": [
        "<span id=\"optimization-problem-→-hamiltonian\" />\n",
        "\n",
        "#### Problema de otimização → Hamiltoniano\n",
        "\n",
        "Em seguida, você pode reformular o problema QUBO como um **Hamiltoniano** (aqui, uma matriz que representa a energia de um sistema):\n",
        "\n",
        "$$\n",
        "H_C=\\sum_{ij}Q_{ij}Z_iZ_j + \\sum_i b_iZ_i.\n",
        "$$\n",
        "\n",
        "**Etapas de reformulação do problema QAOA para o Hamiltoniano**\n",
        "\n",
        "Para demonstrar como o problema QAOA pode ser reescrito dessa forma, primeiro substitua as variáveis binárias $x_i$ por um novo conjunto de variáveis $z_i\\in\\{-1, 1\\}$ por meio de\n",
        "\n",
        "$$\n",
        "x_i = \\frac{1-z_i}{2}.\n",
        "$$\n",
        "\n",
        "Aqui você pode ver que se $x_i$ é $0$, então $z_i$ deve ser $1$. Quando os $x_i$ 's são substituídos pelos $z_i$ 's no problema de otimização ( $x^TQx$ ), é possível obter uma formulação equivalente.\n",
        "\n",
        "$$\n",
        "x^TQx=\\sum_{ij}Q_{ij}x_ix_j \\\\ =\\frac{1}{4}\\sum_{ij}Q_{ij}(1-z_i)(1-z_j) \\\\=\\frac{1}{4}\\sum_{ij}Q_{ij}z_iz_j-\\frac{1}{4}\\sum_{ij}(Q_{ij}+Q_{ji})z_i + \\frac{n^2}{4}.\n",
        "$$\n",
        "\n",
        "Agora, se definirmos $b_i=-\\sum_{j}(Q_{ij}+Q_{ji})$, removermos o pré-fator e o termo $n^2$ constante, chegaremos às duas formulações equivalentes do mesmo problema de otimização.\n",
        "\n",
        "$$\n",
        "min_{x\\in\\{0,1\\}^n} x^TQx\\Longleftrightarrow \\min_{z\\in\\{-1,1\\}^n}z^TQz + b^Tz\n",
        "$$\n",
        "\n",
        "Aqui, $b$ depende de $Q$. Observe que, para obter $z^TQz + b^Tz$, eliminamos o fator de 1/4 e um deslocamento constante de $n^2$, que não desempenham um papel na otimização.\n",
        "\n",
        "Agora, para obter uma formulação quântica do problema, promova as variáveis $z_i$ para uma matriz Pauli $Z$, como uma matriz $2\\times 2$ da forma\n",
        "\n",
        "$$\n",
        "Z_i = \\begin{pmatrix}1 & 0 \\\\ 0 & -1\\end{pmatrix}.\n",
        "$$\n",
        "\n",
        "Quando você substitui essas matrizes no problema de otimização acima, obtém o seguinte Hamiltoniano\n",
        "\n",
        "$$\n",
        "H_C=\\sum_{ij}Q_{ij}Z_iZ_j + \\sum_i b_iZ_i.\n",
        "$$\n",
        "\n",
        "*Lembre-se também de que as matrizes $Z$ estão incorporadas no espaço computacional do computador quântico, ou seja, um espaço de Hilbert de tamanho $2^n\\times 2^n$. Portanto, você deve entender termos como $Z_iZ_j$ como o produto tensorial $Z_i\\otimes Z_j$ incorporado no espaço de Hilbert $2^n\\times 2^n$. Por exemplo, em um problema com cinco variáveis de decisão, o termo $Z_1Z_3$ é entendido como $I\\otimes Z_3\\otimes I\\otimes Z_1\\otimes I$, onde $I$ é a matriz identidade $2\\times 2$.*\n",
        "\n",
        "Esse Hamiltoniano é chamado de <b>função de custo Hamiltoniana</b>. Ele tem a propriedade de que seu estado fundamental corresponde à solução que <b>minimiza a função de custo $f(x)$</b>. Portanto, para resolver o problema de otimização, agora é necessário preparar o estado fundamental de $H_C$ (ou um estado com alta sobreposição com ele) no computador quântico. Então, a amostragem desse estado produzirá, com alta probabilidade, a solução para $\\min~f(x)$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "47256f53-8e02-494e-864a-a6f45b10442a",
      "metadata": {},
      "outputs": [],
      "source": [
        "def build_max_cut_operator(graph: rx.PyGraph) -> tuple[SparsePauliOp, float]:\n",
        "    sp_list = []\n",
        "    constant = 0\n",
        "    for s, t in graph.edge_list():\n",
        "        w = graph.get_edge_data(s, t)\n",
        "        sp_list.append((\"ZZ\", [s, t], w / 2))\n",
        "        constant -= 1 / 2\n",
        "    return SparsePauliOp.from_sparse_list(\n",
        "        sp_list, num_qubits=graph.num_nodes()\n",
        "    ), constant"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "01a2d8eb-b63b-40bc-93c5-0b547c06b194",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Cost Function Hamiltonian: SparsePauliOp(['IIIZZ', 'IIZIZ', 'IIZZI', 'IZIZI', 'ZIZII', 'ZZIII'],\n",
            "              coeffs=[0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j])\n",
            "Constant: -3.0\n"
          ]
        }
      ],
      "source": [
        "cost_hamiltonian, constant = build_max_cut_operator(graph)\n",
        "print(\"Cost Function Hamiltonian:\", cost_hamiltonian)\n",
        "print(\"Constant:\", constant)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "33f71b0d-4a2a-4082-8c1a-ce9d2b769048",
      "metadata": {},
      "source": [
        "<span id=\"hamiltonian-→-quantum-circuit\" />\n",
        "\n",
        "#### Circuito quântico hamiltoniano\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "00431c46-30c2-40f9-99df-40baf8da98f6",
      "metadata": {},
      "source": [
        "O Hamiltoniano $H_C$ contém a definição quântica de seu problema. Agora você pode criar um circuito quântico que ajudará a *obter* boas soluções do computador quântico. O QAOA é inspirado no recozimento quântico e aplica camadas alternadas de operadores no circuito quântico.\n",
        "\n",
        "A ideia geral é começar no estado fundamental de um sistema conhecido, $H^{\\otimes n}|0\\rangle$ acima, e depois direcionar o sistema para o estado fundamental do operador de custo no qual você está interessado. Isso é feito aplicando os operadores $\\exp\\{-i\\gamma_k H_C\\}$ e $\\exp\\{-i\\beta_k H_m\\}$ com os ângulos $\\gamma_1,...,\\gamma_p$ e $\\beta_1,...,\\beta_p~$.\n",
        "\n",
        "O circuito quântico que você gera é **parametrizado** por $\\gamma_i$ e $\\beta_i$, de modo que você pode experimentar diferentes valores de $\\gamma_i$ e $\\beta_i$ e fazer uma amostra do estado resultante.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "ca09d6bf-e421-4ada-9515-1f69687bb511",
      "metadata": {},
      "source": [
        "![\"Diagrama do circuito QAOA\"](https://quantum.cloud.ibm.com/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/circuit-diagram.svg)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0d12bc53-7805-43e4-b4cb-754fd234b519",
      "metadata": {},
      "source": [
        "Neste caso, tentaremos um exemplo com 1 camada QAOA que contém dois parâmetros: $\\gamma_1$ e $\\beta_1$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "1f6215c0",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.circuit.library import QAOAAnsatz"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "7bd8c6d4-f40f-4a11-a440-0b26d9021b53",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/7bd8c6d4-f40f-4a11-a440-0b26d9021b53-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 27,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circuit = QAOAAnsatz(cost_operator=cost_hamiltonian, reps=1)\n",
        "circuit.measure_all()\n",
        "circuit.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "id": "148d2d62",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/148d2d62-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 28,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circuit.decompose(reps=3).draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "id": "315c495a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "ParameterView([ParameterVectorElement(β[0]), ParameterVectorElement(γ[0])])"
            ]
          },
          "execution_count": 29,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circuit.parameters"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "82f70daa-ff68-447a-8064-8b7df7a646cf",
      "metadata": {},
      "source": [
        "<span id=\"33-step-2-optimize-circuits-for-quantum-hardware-execution\" />\n",
        "\n",
        "### 3.3 Passo 2. Otimizar circuitos para execução em hardware quântico\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c08be444-e3ed-4178-a10b-414069b1b411",
      "metadata": {},
      "source": [
        "O circuito acima contém uma série de abstrações úteis para pensar em algoritmos quânticos, mas não é possível executá-los no hardware. Para poder ser executado em uma QPU, o circuito precisa ser submetido a uma série de operações que compõem a etapa **de transpilação** ou **otimização do circuito** do padrão.\n",
        "\n",
        "A biblioteca Qiskit oferece uma série de **passagens de transpilação** que atendem a uma ampla gama de transformações de circuitos. Você precisa se certificar de que seu circuito seja **otimizado** para sua finalidade.\n",
        "\n",
        "A transpilação pode envolver várias etapas, como:\n",
        "\n",
        "* **Mapeamento inicial** dos qubits no circuito (como variáveis de decisão) para qubits físicos no dispositivo.\n",
        "* **Desenrolamento** das instruções no circuito quântico para as instruções nativas de hardware que o backend entende.\n",
        "* **Roteamento** de quaisquer qubits no circuito que interagem com qubits físicos adjacentes uns aos outros.\n",
        "* **Supressão de erros** com a adição de portas de um único qubit para suprimir o ruído com desacoplamento dinâmico.\n",
        "\n",
        "Mais informações sobre a transpilação estão disponíveis em nossa [documentação](/docs/guides/transpile).\n",
        "\n",
        "O código a seguir transforma e otimiza o circuito abstrato em um formato pronto para ser executado em um dos dispositivos acessíveis pela nuvem usando o **serviço Qiskit IBM® Runtime**.\n",
        "\n",
        "Observe que você pode testar seus programas localmente pelo \"modo de teste local\" antes de enviá-los para computadores quânticos reais.\n",
        "Mais informações sobre o modo de teste local estão disponíveis na [documentação.](/docs/guides/local-testing-mode)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "95cd3eed-0348-4373-b664-16a65d42f1e7",
      "metadata": {},
      "outputs": [
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "  service = QiskitRuntimeService(channel=\"ibm_quantum_platform\")\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "<IBMBackend('ibm_strasbourg')>\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/95cd3eed-0348-4373-b664-16a65d42f1e7-2.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 31,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "# Use a quantum device\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(min_num_qubits=127)\n",
        "# backend = service.backend(\"ibm_kingston\")\n",
        "\n",
        "# You can test your programs locally with a fake backend (local testing mode)\n",
        "# backend = FakeBrisbane()\n",
        "\n",
        "print(backend)\n",
        "\n",
        "# Create pass manager for transpilation\n",
        "pm = generate_preset_pass_manager(optimization_level=3, backend=backend)\n",
        "\n",
        "candidate_circuit = pm.run(circuit)\n",
        "candidate_circuit.draw(\"mpl\", fold=False, idle_wires=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4e75cad7-f599-4937-b5fe-f4d01f53423c",
      "metadata": {},
      "source": [
        "<span id=\"34-step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### 3.4 Passo 3. Execute usando Qiskit primitives\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9b99ce67-f121-4244-b62a-536be38fea86",
      "metadata": {},
      "source": [
        "No fluxo de trabalho do QAOA, os parâmetros ideais do QAOA são encontrados em um loop de otimização iterativo, que executa uma série de avaliações de circuitos e usa um otimizador clássico para encontrar os parâmetros ideais de $\\beta_k$ e $\\gamma_k$. Esse loop de execução é executado por meio das seguintes etapas:\n",
        "\n",
        "1. Definir os parâmetros iniciais\n",
        "2. Instanciar um novo site `Session` que contenha o loop de otimização e a primitiva usada para fazer a amostragem do circuito\n",
        "3. Quando for encontrado um conjunto ideal de parâmetros, execute o circuito uma última vez para obter uma distribuição final que será usada na etapa de pós-processamento.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "00b2b0f1-9bad-4ad3-b93e-5cbf40395dbf",
      "metadata": {},
      "source": [
        "<span id=\"define-circuit-with-initial-parameters\" />\n",
        "\n",
        "#### Defina o circuito com os parâmetros iniciais\n",
        "\n",
        "Começamos com parâmetros escolhidos arbitrariamente.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "id": "afa5747f-44dc-4e41-a875-7b6f896f13e2",
      "metadata": {},
      "outputs": [],
      "source": [
        "initial_gamma = np.pi\n",
        "initial_beta = np.pi / 2\n",
        "init_params = [initial_gamma, initial_beta]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b867f1b0-7196-4d34-9b28-e3fb1de8221c",
      "metadata": {},
      "source": [
        "<span id=\"define-backend-and-execution-primitive\" />\n",
        "\n",
        "#### Defina backend e primitiva de execução\n",
        "\n",
        "Use as **primitivas `Qiskit Runtime`** para interagir com os backends `IBM®`. As duas primitivas são Sampler e Estimator, e a escolha da primitiva depende do tipo de medição que você deseja realizar no computador quântico. Para minimizar a função de custo $H_C$, utilize o Estimador, uma vez que a medida da função de custo é simplesmente a esperança de $\\langle H_C \\rangle$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "36c789f2",
      "metadata": {},
      "source": [
        "<span id=\"run\" />\n",
        "\n",
        "#### Executar o\n",
        "\n",
        "As primitivas oferecem uma variedade de [modos de execução](/docs/guides/execution-modes) para agendar cargas de trabalho em dispositivos quânticos, e um fluxo de trabalho QAOA é executado iterativamente em uma sessão.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "122b7dc8-8d8a-45f2-813e-6199905d765b",
      "metadata": {},
      "source": [
        "![\"modo de execução\"](https://quantum.cloud.ibm.com/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/execution-mode.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fff58deb",
      "metadata": {},
      "source": [
        "Você pode inserir a função de custo baseada no amostrador na rotina de minimização do SciPy para encontrar os parâmetros ideais.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 33,
      "id": "ff947109-cddc-4d3c-9119-2c729df73115",
      "metadata": {},
      "outputs": [],
      "source": [
        "def cost_func_estimator(params, ansatz, hamiltonian, estimator):\n",
        "    # transform the observable defined on virtual qubits to\n",
        "    # an observable defined on all physical qubits\n",
        "    isa_hamiltonian = hamiltonian.apply_layout(ansatz.layout)\n",
        "\n",
        "    pub = (ansatz, isa_hamiltonian, params)\n",
        "    job = estimator.run([pub])\n",
        "\n",
        "    results = job.result()[0]\n",
        "    cost = results.data.evs\n",
        "\n",
        "    objective_func_vals.append(cost)\n",
        "\n",
        "    return cost"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 34,
      "id": "f5f46775",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            " message: Optimization terminated successfully.\n",
            " success: True\n",
            "  status: 1\n",
            "     fun: -0.6557925874481715\n",
            "       x: [ 2.873e+00  9.414e-01]\n",
            "    nfev: 21\n",
            "   maxcv: 0.0\n"
          ]
        }
      ],
      "source": [
        "from qiskit_ibm_runtime import Session, EstimatorV2\n",
        "from scipy.optimize import minimize\n",
        "\n",
        "objective_func_vals = []  # Global variable\n",
        "with Session(backend=backend) as session:\n",
        "    # If using qiskit-ibm-runtime<0.24.0, change `mode=` to `session=`\n",
        "    estimator = EstimatorV2(mode=session)\n",
        "    estimator.options.default_shots = 1000\n",
        "\n",
        "    # Set simple error suppression/mitigation options\n",
        "    estimator.options.dynamical_decoupling.enable = True\n",
        "    estimator.options.dynamical_decoupling.sequence_type = \"XY4\"\n",
        "    estimator.options.twirling.enable_gates = True\n",
        "    estimator.options.twirling.num_randomizations = \"auto\"\n",
        "\n",
        "    result = minimize(\n",
        "        cost_func_estimator,\n",
        "        init_params,\n",
        "        args=(candidate_circuit, cost_hamiltonian, estimator),\n",
        "        method=\"COBYLA\",\n",
        "        tol=1e-2,\n",
        "    )\n",
        "    print(result)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ad878b62",
      "metadata": {},
      "source": [
        "O otimizador foi capaz de reduzir o custo e encontrar parâmetros melhores para o circuito.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 35,
      "id": "f923dd5d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/f923dd5d-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.figure(figsize=(12, 6))\n",
        "plt.plot(objective_func_vals)\n",
        "plt.xlabel(\"Iteration\")\n",
        "plt.ylabel(\"Cost\")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e2ea6359",
      "metadata": {},
      "source": [
        "Depois de encontrar os parâmetros ideais para o circuito, você pode atribuir esses parâmetros e fazer uma amostragem da distribuição final obtida com os parâmetros otimizados. É aqui que a primitiva *Sampler* deve ser usada, pois é a distribuição de probabilidade das medições de bitstring que correspondem ao corte ideal do gráfico.\n",
        "\n",
        "**Observação:** Isso significa preparar um estado quântico $\\psi$ no computador e, em seguida, medi-lo. Uma medição colapsará o estado em um único estado de base computacional - por exemplo, `010101110000...` - que corresponde a uma solução candidata $x$ para o nosso problema de otimização inicial ( $\\max f(x)$ ou $\\min f(x)$, dependendo da tarefa).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 36,
      "id": "f8dddf5a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/f8dddf5a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 36,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "optimized_circuit = candidate_circuit.assign_parameters(result.x)\n",
        "optimized_circuit.draw(\"mpl\", fold=False, idle_wires=False)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 37,
      "id": "fd9669cf",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{12: 0.0652, 31: 0.0089, 4: 0.0085, 13: 0.0731, 26: 0.0256, 28: 0.0246, 17: 0.0405, 25: 0.0591, 20: 0.031, 15: 0.0221, 8: 0.017, 21: 0.0371, 14: 0.0461, 16: 0.0229, 19: 0.0723, 23: 0.0199, 22: 0.0478, 18: 0.0708, 24: 0.0165, 6: 0.0525, 7: 0.0155, 5: 0.0245, 3: 0.0231, 29: 0.0121, 30: 0.0062, 10: 0.0363, 1: 0.0097, 9: 0.042, 27: 0.0094, 11: 0.0349, 0: 0.0129, 2: 0.0119}\n"
          ]
        }
      ],
      "source": [
        "from qiskit_ibm_runtime import SamplerV2\n",
        "\n",
        "# If using qiskit-ibm-runtime<0.24.0, change `mode=` to `backend=`\n",
        "sampler = SamplerV2(mode=backend)\n",
        "\n",
        "# Set simple error suppression/mitigation options\n",
        "sampler.options.dynamical_decoupling.enable = True\n",
        "sampler.options.dynamical_decoupling.sequence_type = \"XY4\"\n",
        "sampler.options.twirling.enable_gates = True\n",
        "sampler.options.twirling.num_randomizations = \"auto\"\n",
        "\n",
        "pub = (optimized_circuit,)\n",
        "job = sampler.run([pub], shots=int(1e4))\n",
        "counts_int = job.result()[0].data.meas.get_int_counts()\n",
        "counts_bin = job.result()[0].data.meas.get_counts()\n",
        "shots = sum(counts_int.values())\n",
        "final_distribution_int = {key: val / shots for key, val in counts_int.items()}\n",
        "final_distribution_bin = {key: val / shots for key, val in counts_bin.items()}\n",
        "print(final_distribution_int)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2c89613f",
      "metadata": {},
      "source": [
        "<span id=\"35-step-4-post-process-return-result-in-classical-format\" />\n",
        "\n",
        "### 3.5 Passo 4. Pós-processamento, retornar resultado no formato clássico\n",
        "\n",
        "A etapa de pós-processamento interpreta a saída da amostragem para retornar uma solução para seu problema original. Nesse caso, você está interessado no bitstring com a maior probabilidade, pois isso determina o corte ideal. As simetrias no problema permitem quatro soluções possíveis, e o processo de amostragem retornará uma delas com uma probabilidade um pouco maior, mas você pode ver na distribuição plotada abaixo que quatro das cadeias de bits são distintamente mais prováveis do que as demais.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 38,
      "id": "d4f7fc70-883f-4b6b-8e92-2fc4afbbea46",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Result bitstring: [1, 0, 1, 1, 0]\n"
          ]
        }
      ],
      "source": [
        "# auxiliary functions to sample most likely bitstring\n",
        "def to_bitstring(integer, num_bits):\n",
        "    result = np.binary_repr(integer, width=num_bits)\n",
        "    return [int(digit) for digit in result]\n",
        "\n",
        "\n",
        "keys = list(final_distribution_int.keys())\n",
        "values = list(final_distribution_int.values())\n",
        "most_likely = keys[np.argmax(np.abs(values))]\n",
        "most_likely_bitstring = to_bitstring(most_likely, len(graph))\n",
        "most_likely_bitstring.reverse()\n",
        "\n",
        "print(\"Result bitstring:\", most_likely_bitstring)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 39,
      "id": "32a3020e-c1ea-4aff-988c-d7910a690fa8",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/32a3020e-c1ea-4aff-988c-d7910a690fa8-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "\n",
        "matplotlib.rcParams.update({\"font.size\": 10})\n",
        "final_bits = final_distribution_bin\n",
        "values = np.abs(list(final_bits.values()))\n",
        "top_4_values = sorted(values, reverse=True)[:4]\n",
        "positions = []\n",
        "for value in top_4_values:\n",
        "    positions.append(np.where(values == value)[0])\n",
        "fig = plt.figure(figsize=(11, 6))\n",
        "ax = fig.add_subplot(1, 1, 1)\n",
        "plt.xticks(rotation=45)\n",
        "plt.title(\"Result Distribution\")\n",
        "plt.xlabel(\"Bitstrings (reversed)\")\n",
        "plt.ylabel(\"Probability\")\n",
        "ax.bar(list(final_bits.keys()), list(final_bits.values()), color=\"tab:grey\")\n",
        "for p in positions:\n",
        "    ax.get_children()[p[0].item()].set_color(\"tab:purple\")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6cfcb278",
      "metadata": {},
      "source": [
        "<span id=\"visualize-best-cut\" />\n",
        "\n",
        "#### Visualize o melhor corte\n",
        "\n",
        "A partir da cadeia de bits ideal, você pode visualizar esse corte no gráfico original.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 40,
      "id": "22a48124-e6b4-4144-bee1-f01fa4c7ccbb",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/22a48124-e6b4-4144-bee1-f01fa4c7ccbb-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "colors = [\"tab:grey\" if i == 0 else \"tab:purple\" for i in most_likely_bitstring]\n",
        "mpl_draw(graph, node_size=600, pos=pos, with_labels=True, labels=str, node_color=colors)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4c4803ab",
      "metadata": {},
      "source": [
        "E calcule o valor do corte. A solução não é ideal devido ao ruído (o valor de corte da solução ideal é 5).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 41,
      "id": "7208ee7d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The value of the cut is: 5\n"
          ]
        }
      ],
      "source": [
        "from typing import Sequence\n",
        "\n",
        "\n",
        "def evaluate_sample(x: Sequence[int], graph: rx.PyGraph) -> float:\n",
        "    assert len(x) == len(\n",
        "        list(graph.nodes())\n",
        "    ), \"The length of x must coincide with the number of nodes in the graph.\"\n",
        "    return sum(\n",
        "        x[u] * (1 - x[v]) + x[v] * (1 - x[u]) for u, v in list(graph.edge_list())\n",
        "    )\n",
        "\n",
        "\n",
        "cut_value = evaluate_sample(most_likely_bitstring, graph)\n",
        "print(\"The value of the cut is:\", cut_value)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1450be7e",
      "metadata": {},
      "source": [
        "Isso conclui o tutorial de QAOA em pequena escala.\n",
        "Você aprenderá como adaptar o QAOA em uma escala de utilidade pública na \"Parte 2: aumente a escala!\" do tutorial [do algoritmo de otimização aproximada Quantum](/docs/tutorials/quantum-approximate-optimization-algorithm).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 42,
      "id": "2a4f85ab",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'2.0.2'"
            ]
          },
          "execution_count": 42,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Check Qiskit version\n",
        "import qiskit\n",
        "\n",
        "qiskit.__version__"
      ]
    },
    {
      "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"
    },
    "widgets": {
      "application/vnd.jupyter.widget-state+json": {
        "state": {},
        "version_major": 2,
        "version_minor": 0
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}