{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Iniciação rápida\"\n",
        "description: \"Guia rápido para a versão mais recente da diagonalização quântica baseada em amostras (SQD)\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "intro",
      "metadata": {},
      "source": [
        "<span id=\"quickstart\" />\n",
        "\n",
        "# Iniciação rápida\n",
        "\n",
        "{/* cspell:ignore mcscf, ncas, nelecas, hcore, binom */}\n",
        "\n",
        "`qiskit-addon-sqd` Este guia apresenta um exemplo mínimo funcional do pacote. Utilizamos a diagonalização quântica baseada em amostras (SQD) para refinar amostras ruidosas obtidas de um circuito quântico que aproxima o estado fundamental de um hamiltoniano químico. As energias do estado fundamental são estimadas por meio da diagonalização do hamiltoniano em subespaços formados pelas amostras refinadas.\n",
        "\n",
        "Para conhecer fluxos de trabalho mais detalhados, de ponta a ponta, executados em hardware quântico, confira os tutoriais:\n",
        "\n",
        "* [Diagonalização quântica baseada em amostras](/docs/tutorials/sample-based-quantum-diagonalization) : fluxo de trabalho do hamiltoniano químico\n",
        "* [Diagonalização quântica de Krylov baseada em amostras](/docs/tutorials/sample-based-krylov-quantum-diagonalization) : fluxo de trabalho do modelo de rede fermiónica\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "prepare-header",
      "metadata": {},
      "source": [
        "<span id=\"1-prepare-the-inputs-for-sqd\" />\n",
        "\n",
        "## 1. Preparar os dados de entrada para o SQD\n",
        "\n",
        "Primeiro, definimos o hamiltoniano químico e o circuito quântico que aproxima seu estado fundamental. O sistema estudado neste exemplo é o N₂ na geometria de equilíbrio, na base de coordenaçã STO-3G, com dois orbitais centrais congelados. Esse sistema possui `8` orbitais espaciais e `10` elétrons, o que significa que o circuito quântico é definido por `16` qubits, em que os qubits `0-7` representam orbitais de spin para cima e os qubits `8-15` representam orbitais de spin para baixo. `10`Como se trata de um sistema de camada fechada com `10` elétrons, sabemos que cada amostra fisicamente válida deve ter peso de Hamming, sendo que o peso de Hamming tanto da metade esquerda quanto da metade direita da sequência de bits é `5`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "load-molecule",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Spatial orbitals: 8\n",
            "Qubits: 16\n",
            "Electrons (alpha, beta): (5, 5)\n"
          ]
        }
      ],
      "source": [
        "from pyscf import ao2mo, gto, mcscf, scf\n",
        "\n",
        "# Construct the Hamiltonian, pull out integrals and other information about the system\n",
        "mol = gto.M(\n",
        "    atom=\"N 0 0 0; N 0 0 1.09768\", basis=\"sto-3g\", spin=0, charge=0, verbose=0\n",
        ")\n",
        "mf = scf.RHF(mol).run()\n",
        "cas = mcscf.CASCI(mf, ncas=8, nelecas=10)\n",
        "hcore, nuclear_repulsion_energy = cas.get_h1eff()\n",
        "eri = cas.get_h2eff()\n",
        "num_orbitals = cas.ncas\n",
        "eri = ao2mo.restore(1, eri, num_orbitals)\n",
        "n_alpha, n_beta = cas.nelecas\n",
        "nelec = (n_alpha, n_beta)\n",
        "\n",
        "print(\n",
        "    f\"Spatial orbitals: {num_orbitals}\\nQubits: {num_orbitals * 2}\\nElectrons (alpha, beta): {nelec}\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "samples-explanation",
      "metadata": {},
      "source": [
        "<span id=\"2-generate-noisy-samples\" />\n",
        "\n",
        "## 2. Gerar amostras com ruído\n",
        "\n",
        "Em um fluxo de trabalho real, você projetaria um circuito ansatz, o otimizaria para o hardware e o simularia na QPU. Aqui, ignoramos isso e, em vez disso, geramos `10,000` sequências de bits aleatórias uniformemente distribuídas. Embora nossas amostras não contenham informações sobre o estado fundamental real, veremos que o protocolo SQD ainda é capaz de recuperar o verdadeiro estado fundamental por meio do refinamento iterativo dessas amostras, da diagonalização e do uso das energias em cada iteração para orientar a próxima rodada de refinamento da sequência de bits. Podemos, eventualmente, recuperar o estado fundamental exato dessa maneira, pois o subespaço que contém o estado fundamental é pequeno o suficiente para ser calculado com exatidão. `5`Especificamente, como sabemos que cada metade de cada sequência de bits (`8` bits) deve ter peso de Hamming, sabemos que o subespaço de Hilbert completo do sistema tem tamanho $\\binom{8}{5}^2=3136$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "generate-samples",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Generated 10000 uniformly-random, 16-qubit samples.\n"
          ]
        }
      ],
      "source": [
        "import numpy as np\n",
        "from qiskit_addon_sqd.counts import generate_bit_array_uniform\n",
        "\n",
        "# Generate some uniformly-random bitstrings to simulate QPU samples\n",
        "rng = np.random.default_rng(24)\n",
        "bit_array = generate_bit_array_uniform(\n",
        "    10_000, num_orbitals * 2, rand_seed=rng\n",
        ")\n",
        "print(\n",
        "    f\"Generated {bit_array.num_shots} uniformly-random, {bit_array.num_bits}-qubit samples.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "postprocess-header",
      "metadata": {},
      "source": [
        "<span id=\"3-post-process-with-sqd\" />\n",
        "\n",
        "## 3. Pós-processamento com SQD\n",
        "\n",
        "O objetivo do algoritmo SQD é refinar amostras ruidosas obtidas de uma QPU e, por fim, diagonalizar o hamiltoniano em um subespaço gerado por essas amostras refinadas. Isso é feito de forma iterativa, da seguinte maneira:\n",
        "\n",
        "* Refinar as amostras selecionando posteriormente apenas aquelas que apresentam o número correto de partículas tanto para o subsistema de aceleração quanto para o de desaceleração\n",
        "* Diagonalize em pequenos subespaços gerados pelas amostras refinadas e colete informações sobre os subespaços que apresentam as melhores (mais baixas) energias.\n",
        "* Para cada iteração:\n",
        "  * Refine as amostras com ruído invertendo, de forma probabilística, os bits nas amostras com número incorreto de partículas. As probabilidades são calculadas utilizando as informações do subespaço obtidas na diagonalização mais recente.\n",
        "  * Diagonalizar em pequenos subespaços gerados pelas amostras refinadas\n",
        "  * Coletar informações sobre os subespaços que apresentam as melhores (mais baixas) energias\n",
        "\n",
        "Podemos observar na saída que o algoritmo converge para a energia exata do estado fundamental após `6` iterações e que a diagonalização é realizada sobre todo o subespaço de Hilbert de dimensão $\\binom{8}{5}^2=3136$. É claro que a diagonalização sobre todo o subespaço geralmente não é viável e, portanto, os sistemas com estados fundamentais de suporte esparso são os que mais provavelmente se beneficiarão de técnicas de diagonalização de subespaços, como o SQD.\n",
        "\n",
        "Para obter mais informações sobre a função diagonalize\\_fermionic\\_hamiltonian, consulte a [documentação da API](/docs/api/qiskit-addon-sqd/fermion#diagonalize_fermionic_hamiltonian).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "run-sqd",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Exact (FCI) reference energy: -107.652521 Ha\n",
            "\n",
            "Iteration 1\n",
            "  Subsample 0\n",
            "    Energy: -106.908481\n",
            "    Subspace dimension: 784\n",
            "    Error vs exact: 0.744040 Ha\n",
            "Iteration 2\n",
            "  Subsample 0\n",
            "    Energy: -106.934921\n",
            "    Subspace dimension: 1764\n",
            "    Error vs exact: 0.717600 Ha\n",
            "Iteration 3\n",
            "  Subsample 0\n",
            "    Energy: -106.953430\n",
            "    Subspace dimension: 2209\n",
            "    Error vs exact: 0.699091 Ha\n",
            "Iteration 4\n",
            "  Subsample 0\n",
            "    Energy: -107.651946\n",
            "    Subspace dimension: 2916\n",
            "    Error vs exact: 0.000575 Ha\n",
            "Iteration 5\n",
            "  Subsample 0\n",
            "    Energy: -107.652003\n",
            "    Subspace dimension: 3025\n",
            "    Error vs exact: 0.000518 Ha\n",
            "Iteration 6\n",
            "  Subsample 0\n",
            "    Energy: -107.652521\n",
            "    Subspace dimension: 3136\n",
            "    Error vs exact: 0.000000 Ha\n",
            "Iteration 7\n",
            "  Subsample 0\n",
            "    Energy: -107.652521\n",
            "    Subspace dimension: 3136\n",
            "    Error vs exact: 0.000000 Ha\n"
          ]
        }
      ],
      "source": [
        "from functools import partial\n",
        "\n",
        "from pyscf import fci\n",
        "from qiskit_addon_sqd.fermion import (\n",
        "    SCIResult,\n",
        "    diagonalize_fermionic_hamiltonian,\n",
        "    solve_sci_batch,\n",
        ")\n",
        "\n",
        "exact_energy, _ = fci.direct_spin1.kernel(\n",
        "    hcore, eri, int(num_orbitals), nelec\n",
        ")\n",
        "exact_energy += nuclear_repulsion_energy\n",
        "print(f\"Exact (FCI) reference energy: {exact_energy:.6f} Ha\\n\")\n",
        "\n",
        "sci_solver = partial(solve_sci_batch, spin_sq=0.0, max_cycle=200)\n",
        "\n",
        "result_history = []\n",
        "\n",
        "\n",
        "def callback(results: list[SCIResult]):\n",
        "    result_history.append(results)\n",
        "    iteration = len(result_history)\n",
        "    print(f\"Iteration {iteration}\")\n",
        "    for i, result in enumerate(results):\n",
        "        error = result.energy + nuclear_repulsion_energy - exact_energy\n",
        "        print(f\"  Subsample {i}\")\n",
        "        print(f\"    Energy: {result.energy + nuclear_repulsion_energy:.6f}\")\n",
        "        print(\n",
        "            f\"    Subspace dimension: {np.prod(result.sci_state.amplitudes.shape)}\"\n",
        "        )\n",
        "        print(f\"    Error vs exact: {error:.6f} Ha\")\n",
        "\n",
        "\n",
        "samples_per_batch = 20\n",
        "result = diagonalize_fermionic_hamiltonian(\n",
        "    hcore,\n",
        "    eri,\n",
        "    bit_array,\n",
        "    samples_per_batch=samples_per_batch,\n",
        "    norb=num_orbitals,\n",
        "    nelec=nelec,\n",
        "    occupancies_tol=1e-7,\n",
        "    max_iterations=30,\n",
        "    sci_solver=sci_solver,\n",
        "    symmetrize_spin=True,\n",
        "    callback=callback,\n",
        "    seed=np.random.default_rng(32),\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}