{
  "cells": [
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "2a246d14-ed2b-4573-bf97-939c3628b3bb",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Loops de otimização\"\n",
        "description: \"Esta lição orienta o uso de loops de otimização clássicos para alimentar parâmetros em cálculos quânticos.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore nabla */}\n",
        "\n",
        "<span id=\"optimization-loops\" />\n",
        "\n",
        "# Loops de otimização\n",
        "\n",
        "Nesta lição, aprenderemos a usar um *otimizador* para explorar iterativamente os estados quânticos parametrizados de nosso ansatz:\n",
        "\n",
        "* Inicialização de um loop de otimização\n",
        "* Entenda as compensações ao usar otimizadores locais e globais\n",
        "* Explore os platôs estéreis e como evitá-los\n",
        "\n",
        "Em um nível elevado, os otimizadores são fundamentais para explorar nosso espaço de pesquisa. O otimizador usa avaliações da função de custo para selecionar o próximo conjunto de parâmetros em um loop variacional e repete o processo até atingir um estado estável. Nesse estágio, um conjunto ideal de valores de parâmetros $\\vec\\theta^*$ é retornado.\n",
        "\n",
        "![Um diagrama de alguns fatores importantes na otimização, incluindo platôs estéreis, otimizadores com gradiente versus otimizadores sem gradiente e bootstrapping.](https://quantum.cloud.ibm.com/learning/images/courses/variational-algorithm-design/optimization-loops/optimization-workflow.svg)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "110421e7-0d2a-4653-a195-1925c809ca61",
      "metadata": {},
      "source": [
        "<span id=\"local-and-global-optimizers\" />\n",
        "\n",
        "## Otimizadores locais e globais\n",
        "\n",
        "Primeiro, definiremos nosso problema antes de explorar cada classe de otimizador. Começaremos com um circuito que contém oito parâmetros de variação:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "15cf7810-1d25-4c54-aff0-91d3a0c51cec",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/variational-algorithm-design/optimization-loops/extracted-outputs/15cf7810-1d25-4c54-aff0-91d3a0c51cec-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit.circuit.library import TwoLocal\n",
        "import numpy as np\n",
        "\n",
        "theta_list = (2 * np.pi * np.random.rand(1, 8)).tolist()\n",
        "observable = SparsePauliOp.from_list([(\"XX\", 1), (\"YY\", -3)])\n",
        "\n",
        "reference_circuit = QuantumCircuit(2)\n",
        "reference_circuit.x(0)\n",
        "\n",
        "variational_form = TwoLocal(\n",
        "    2,\n",
        "    rotation_blocks=[\"rz\", \"ry\"],\n",
        "    entanglement_blocks=\"cx\",\n",
        "    entanglement=\"linear\",\n",
        "    reps=1,\n",
        ")\n",
        "ansatz = reference_circuit.compose(variational_form)\n",
        "\n",
        "ansatz.decompose().draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "950569c0-4e8f-49b5-b118-7cd227bd5ce1",
      "metadata": {},
      "outputs": [],
      "source": [
        "def cost_func_vqe(params, ansatz, hamiltonian, estimator):\n",
        "    \"\"\"Return estimate of energy from estimator\n",
        "\n",
        "    Parameters:\n",
        "        params (ndarray): Array of ansatz parameters\n",
        "        ansatz (QuantumCircuit): Parameterized ansatz circuit\n",
        "        hamiltonian (SparsePauliOp): Operator representation of Hamiltonian\n",
        "        estimator (Estimator): Estimator primitive instance\n",
        "\n",
        "    Returns:\n",
        "        float: Energy estimate\n",
        "    \"\"\"\n",
        "    pub = (ansatz, hamiltonian, params)\n",
        "    cost = estimator.run([pub]).result()[0].data.evs\n",
        "    return cost"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "f536ee50-373f-4873-b258-66c5c07c65c5",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.primitives import StatevectorEstimator\n",
        "\n",
        "estimator = StatevectorEstimator()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c9dcec4d-5b84-4731-8b5a-cb8a0f7572d5",
      "metadata": {},
      "source": [
        "<span id=\"local-optimizers\" />\n",
        "\n",
        "### Otimizadores locais\n",
        "\n",
        "Os otimizadores locais buscam um ponto que minimize a função de custo a partir de um ou mais pontos iniciais $C(\\vec{\\theta_0})$ e se movem para pontos diferentes com base no que observam na região que estão avaliando no momento em iterações sucessivas. Isso implica que a convergência desses algoritmos geralmente é rápida, mas pode depender muito do ponto inicial. Os otimizadores locais não conseguem ver além da região em que estão avaliando e podem ser especialmente vulneráveis a mínimos locais, informando a convergência quando encontram um e ignorando outros estados com avaliações mais favoráveis.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "447df742-13bd-4d4a-a364-41d34380cbc9",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              " message: Optimization terminated successfully\n",
              " success: True\n",
              "  status: 0\n",
              "     fun: -3.9999999964520634\n",
              "       x: [ 1.000e+00  1.000e+00 -1.571e+00 -4.556e-05 -1.207e+00\n",
              "           -1.935e+00  4.079e-01 -4.079e-01]\n",
              "     nit: 12\n",
              "     jac: [ 0.000e+00  0.000e+00 -7.957e-04  2.543e-04  1.381e-03\n",
              "            1.381e-03  5.430e-04  5.431e-04]\n",
              "    nfev: 112\n",
              "    njev: 12"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# SciPy minimizer routine\n",
        "from scipy.optimize import minimize\n",
        "\n",
        "x0 = np.ones(8)\n",
        "\n",
        "result = minimize(\n",
        "    cost_func_vqe, x0, args=(ansatz, observable, estimator), method=\"SLSQP\"\n",
        ")\n",
        "\n",
        "result"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "25fee3f4-9d3e-45ce-a104-dfde5e132b2b",
      "metadata": {},
      "source": [
        "<span id=\"global-optimizers\" />\n",
        "\n",
        "### Otimizadores globais\n",
        "\n",
        "Os otimizadores globais buscam o ponto que minimiza a função de custo em várias regiões de seu domínio (ou seja, não local), avaliando-a iterativamente (ou seja, na iteração $i$ ) em um conjunto de vetores de parâmetros $\\Theta_i := \\\\{ {\\vec\\theta_{i,j} | j \\in \\mathcal{J}_\\text{opt}^i} \\\\}$ determinado pelo otimizador. Isso os torna menos suscetíveis a mínimos locais e, de certa forma, independentes da inicialização, mas também significativamente mais lentos para convergir para uma solução proposta.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8d794038-8e75-4649-a978-a9e832ebed60",
      "metadata": {},
      "source": [
        "<span id=\"bootstrapping-optimization\" />\n",
        "\n",
        "### Otimização de bootstrapping\n",
        "\n",
        "*O bootstrapping*, ou seja, a definição do valor inicial dos parâmetros $\\vec\\theta$ com base em uma otimização anterior, pode ajudar nosso otimizador a convergir para uma solução mais rapidamente. Referimo-nos a isso como o ponto inicial $\\vec\\theta_0$, e $|\\psi(\\vec\\theta_0)\\rangle = U_V(\\vec\\theta_0)|\\rho\\rangle$ como o estado inicial. Esse estado inicial difere do nosso estado de referência $|\\rho\\rangle$, pois o primeiro se concentra nos parâmetros iniciais definidos durante nosso loop de otimização, enquanto o último se concentra no uso de soluções de \"referência\" conhecidas. Elas podem coincidir se $U_V(\\vec\\theta_0) \\equiv I$ (ou seja, a operação de identidade).\n",
        "\n",
        "Quando os otimizadores locais convergem para mínimos locais não ideais, podemos tentar inicializar a otimização globalmente e refinar a convergência localmente. Embora isso exija a configuração de duas cargas de trabalho variacionais, permite que o otimizador encontre uma solução mais otimizada do que o otimizador local sozinho.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "07ba982e-9280-4e15-9d8c-4b6460b1deea",
      "metadata": {
        "gloss": {
          "hyperparameter": {
            "text": "A hyperparameter is a parameter that we use to control our algorithm. The ‘hyper’ distinguishes it from the parameters (θ) that our algorithm is trying to find.",
            "title": "Hyperparameter"
          },
          "local-minimum": {
            "text": "A local minimum is the lowest point of the function, for a small range of values of theta. In contrast, a global minimum is <i>the</i> lowest point, anywhere in our function (that is, for any value of theta). <a href='https://en.wikipedia.org/wiki/Maxima_and_minima'>Read more</a>.",
            "title": "Local minimum"
          }
        }
      },
      "source": [
        "<span id=\"gradient-based-and-gradient-free-optimizers\" />\n",
        "\n",
        "## Otimizadores baseados em gradiente e sem gradiente\n",
        "\n",
        "<span id=\"gradient-based\" />\n",
        "\n",
        "### Baseado em gradiente\n",
        "\n",
        "Para nossa função de custo $C(\\vec\\theta)$, se tivermos acesso ao gradiente da função $\\vec{\\nabla} C(\\vec\\theta)$ a partir de um ponto inicial, a maneira mais simples de minimizar a função é atualizar os parâmetros na direção da descida mais íngreme da função. Ou seja, atualizamos os parâmetros como $\\vec\\theta_{n+1} = \\vec\\theta_n - \\eta \\vec{\\nabla} C(\\vec\\theta)$, em que $\\eta$ é a taxa de aprendizado - um <DefinitionTooltip definition=\"Um hiperparâmetro é um parâmetro que usamos para controlar nosso algoritmo. O termo hyper o distingue dos parâmetros (θ) que nosso algoritmo está tentando encontrar.\">hiperparâmetro</DefinitionTooltip> pequeno e positivo que controla o tamanho da atualização. Continuamos fazendo isso até convergirmos para um <DefinitionTooltip definition=\"Um mínimo local é o ponto mais baixo da função, para um pequeno intervalo de valores de theta. Por outro lado, um mínimo global é o ponto mais baixo, em qualquer lugar em nossa função (ou seja, para qualquer valor de θ).\">mínimo local</DefinitionTooltip> da função de custo, $C({\\vec\\theta^*})$.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "0897260d-f3f8-4caa-8a79-f258e41f8e21",
      "metadata": {},
      "source": [
        "Podemos usar essa função de custo e um otimizador para calcular os parâmetros ideais\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "aad81101-3c19-4946-8453-3db9df3369c0",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "  message: Optimization terminated successfully.\n",
              "  success: True\n",
              "   status: 0\n",
              "      fun: -3.9999999999997025\n",
              "        x: [ 1.000e+00  1.000e+00  1.571e+00  3.220e-07  2.009e-01\n",
              "            -2.009e-01  6.342e-01 -6.342e-01]\n",
              "      nit: 14\n",
              "      jac: [-1.192e-07 -2.980e-08  8.345e-07  1.103e-06  5.960e-08\n",
              "             0.000e+00 -5.960e-08  2.980e-08]\n",
              " hess_inv: [[ 1.000e+00  1.872e-10 ...  5.077e-05  3.847e-05]\n",
              "            [ 1.872e-10  1.000e+00 ... -5.208e-05 -4.060e-05]\n",
              "            ...\n",
              "            [ 5.077e-05 -5.208e-05 ...  7.243e-01 -2.604e-01]\n",
              "            [ 3.847e-05 -4.060e-05 ... -2.604e-01  8.179e-01]]\n",
              "     nfev: 144\n",
              "     njev: 16"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# SciPy minimizer routine\n",
        "from scipy.optimize import minimize\n",
        "\n",
        "x0 = np.ones(8)\n",
        "\n",
        "result = minimize(\n",
        "    cost_func_vqe, x0, args=(ansatz, observable, estimator), method=\"BFGS\"\n",
        ")\n",
        "\n",
        "result"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "0f7d55f8-e3a2-4223-b456-17b39a81f524",
      "metadata": {},
      "source": [
        "As principais desvantagens desse tipo de otimização são a velocidade de convergência, que pode ser muito lenta, e o fato de não haver garantia de alcançar a solução ideal.\n",
        "\n",
        "![no gráfico de f(theta) em relação a theta, vários pontos mostram diferentes estados de um algoritmo de descida de gradiente que encontra o mínimo de uma curva.](https://quantum.cloud.ibm.com/learning/images/courses/variational-algorithm-design/optimization-loops/optimization-gradient-descent.svg)\n",
        "\n",
        "<span id=\"gradient-free\" />\n",
        "\n",
        "### Sem gradiente\n",
        "\n",
        "Os algoritmos de otimização sem gradiente não exigem informações sobre o gradiente e podem ser úteis em situações em que o cálculo do gradiente é difícil, caro ou muito ruidoso. Eles também tendem a ser mais robustos na busca de ótimos globais, enquanto os métodos baseados em gradiente tendem a convergir para ótimos locais. Vamos explorar alguns casos em que um otimizador sem gradiente pode ajudar a evitar platôs estéreis. No entanto, os métodos sem gradiente exigem mais recursos computacionais, especialmente para problemas com espaços de pesquisa de alta dimensão.\n",
        "\n",
        "Aqui está um exemplo que usa o otimizador [`COBYLA`](https://docs.scipy.org/doc/scipy/reference/optimize.minimize-cobyla.html#optimize-minimize-cobyla) em vez do otimizador:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "c749695b-3124-47d8-8a23-10c9d5965a7f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              " message: Optimization terminated successfully.\n",
              " success: True\n",
              "  status: 1\n",
              "     fun: -3.999999973369678\n",
              "       x: [ 1.631e+00  1.492e+00  1.571e+00  3.142e+00  1.375e+00\n",
              "           -1.767e+00  1.484e+00  1.658e+00]\n",
              "    nfev: 137\n",
              "   maxcv: 0.0"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# SciPy minimizer routine\n",
        "from scipy.optimize import minimize\n",
        "\n",
        "x0 = np.ones(8)\n",
        "\n",
        "result = minimize(\n",
        "    cost_func_vqe, x0, args=(ansatz, observable, estimator), method=\"COBYLA\"\n",
        ")\n",
        "\n",
        "result"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "396d0617-9f20-48cb-b57c-30dfa54293ff",
      "metadata": {
        "gloss": {
          "barren-plateaus": {
            "text": "When gradients of parametrized quantum circuits become exponentially small with respect to the number of qubits, making optimization difficult and potentially impossible.",
            "title": "Barren Plateaus"
          }
        }
      },
      "source": [
        "<span id=\"barren-plateaus\" />\n",
        "\n",
        "## Planaltos áridos\n",
        "\n",
        "De fato, o cenário de custos pode ser bastante complicado, como mostram as colinas e os vales no exemplo abaixo. O método de otimização nos conduz pelo cenário de custos, buscando o mínimo, conforme mostrado pelos pontos e linhas pretos. Podemos ver que duas das três pesquisas terminam em um mínimo local do cenário, em vez de um mínimo global.\n",
        "\n",
        "![Um coletor curvo complicado com muitos picos e depressões.](https://quantum.cloud.ibm.com/learning/images/courses/variational-algorithm-design/optimization-loops/optimization-loss-landscape.svg)\n",
        "\n",
        "Independentemente do tipo de método de otimização usado, se o cenário de custos for relativamente plano, pode ser um desafio para o método determinar a direção apropriada para a pesquisa. Esse cenário é conhecido como <DefinitionTooltip definition=\"Quando os gradientes dos circuitos quânticos parametrizados se tornam exponencialmente pequenos em relação ao número de qubits, dificultando a otimização e tornando-a potencialmente impossível.\">platô estéril,</DefinitionTooltip>, em que o cenário de custos se torna progressivamente mais plano (e, portanto, mais desafiador para determinar a direção para o mínimo). Para uma ampla gama de circuitos quânticos parametrizados, a probabilidade de que o gradiente ao longo de qualquer direção razoável seja diferente de zero para alguma precisão fixa diminui exponencialmente à medida que o número de qubits aumenta.\n",
        "\n",
        "![Um diagrama de um platô geográfico comparado à inclinação de uma montanha, para explicar por que um gradiente nos ajuda a encontrar um mínimo e um platô dificulta nossos esforços.](https://quantum.cloud.ibm.com/learning/images/courses/variational-algorithm-design/optimization-loops/optimization-barren-plateaus.svg)\n",
        "\n",
        "Embora essa área ainda esteja em pesquisa ativa, temos algumas recomendações para melhorar o desempenho da otimização:\n",
        "\n",
        "* **O bootstrapping** pode ajudar o loop de otimização a evitar ficar preso em um espaço de parâmetros em que o gradiente é pequeno.\n",
        "* **Experimentação com ansatz eficiente de hardware** : como estamos usando um sistema quântico ruidoso como um oráculo de caixa preta, a qualidade dessas avaliações pode afetar o desempenho do otimizador. O uso de ansatz eficiente em termos de hardware, como [`EfficientSU2`](/docs/api/qiskit/qiskit.circuit.library.EfficientSU2)pode evitar a produção de gradientes exponencialmente pequenos.\n",
        "* **Experimentação com supressão e atenuação de erros** : as primitivas Qiskit Runtime fornecem uma interface simples para experimentar vários valores para `optimization_level` e `resilience_setting`, respectivamente. Isso pode reduzir o impacto do ruído e tornar o processo de otimização mais eficiente.\n",
        "* **Experimentando otimizadores sem gradiente** : Diferentemente dos algoritmos de otimização baseados em gradiente, otimizadores como o `COBYLA` não dependem de informações de gradiente para otimizar os parâmetros e, portanto, têm menos probabilidade de serem afetados pelo platô estéril.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "182110bc-f952-4460-8048-d1df961ba860",
      "metadata": {},
      "source": [
        "<span id=\"summary\" />\n",
        "\n",
        "## Resumo\n",
        "\n",
        "Com esta lição, você aprendeu a definir seu loop de otimização:\n",
        "\n",
        "* Inicialização de um loop de otimização\n",
        "* Entenda as compensações ao usar otimizadores locais e globais\n",
        "* Explore os platôs estéreis e como evitá-los\n",
        "\n",
        "Nossa carga de trabalho variacional de alto nível está completa:\n",
        "\n",
        "![Um circuito quântico agora com uma unidade para preparar o estado de referência e uma segunda unidade para variar o estado usando parâmetros variacionais.](https://quantum.cloud.ibm.com/learning/images/courses/variational-algorithm-design/optimization-loops/optimization-circuit.svg)\n",
        "\n",
        "Em seguida, exploraremos algoritmos variacionais específicos com essa estrutura em mente.\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": 2
}