{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"クイック・スタート\"\n",
        "description: \"最新バージョンのサンプルベース量子対角化（SQD）のクイックスタートガイド\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "intro",
      "metadata": {},
      "source": [
        "<span id=\"quickstart\" />\n",
        "\n",
        "# クイック・スタート\n",
        "\n",
        "{/* cspell:ignore mcscf, ncas, nelecas, hcore, binom */}\n",
        "\n",
        "このガイドでは、この `qiskit-addon-sqd` パッケージの最小限の動作例を紹介します。 我々は、化学ハミルトニアンの基底状態を近似する量子回路から取得したノイズの混じったサンプルに対し、サンプルベース量子対角化（SQD）を用いて精緻化を行う。 基底状態のエネルギーは、精製された試料によって形成される部分空間においてハミルトニアンを対角化することで近似される。\n",
        "\n",
        "量子ハードウェア上で実行されるエンドツーエンドのワークフローについて、さらに詳しく知りたい場合は、以下のチュートリアルをご覧ください：\n",
        "\n",
        "* [サンプルベースの量子対角化](/docs/tutorials/sample-based-quantum-diagonalization) ：化学ハミルトニアンのワークフロー\n",
        "* [サンプルベースのクリロフ量子対角化](/docs/tutorials/sample-based-krylov-quantum-diagonalization) ：フェルミオン格子モデルのワークフロー\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "prepare-header",
      "metadata": {},
      "source": [
        "<span id=\"1-prepare-the-inputs-for-sqd\" />\n",
        "\n",
        "## 1. SQD用の入力データを準備する\n",
        "\n",
        "まず、化学ハミルトニアンと、その基底状態を近似する量子回路を定義する。 この例で検討した系は、 STO-3G 基底において、2つのコア軌道を固定した平衡幾何構造のN₂である。 この系には `8` 個の空間軌道と `10` 個の電子があり、これは量子回路が 個の量子ビット上で `16` 定義されていることを意味する。ここで、量子ビット `0-7` はスピンアップの軌道を、量子ビット `8-15` はスピンダウンの軌道を表す。 `5``10`これは電子を持つ `10` 閉殻系であるため、物理的に妥当な各試料はハミング重みが であることがわかっている。ここで、ビット列の左右両半分のハミング重みは である。\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. ノイズを含むサンプルを生成する\n",
        "\n",
        "実際のワークフローでは、アンザッツ回路を設計し、ハードウェア向けに最適化した上で、QPU上でサンプリングを行います。 ここではその手順を省略し、代わりに一様分布に従うランダムなビット列を生成 `10,000` します。 これらのサンプルには実際の基底状態に関する情報は含まれていませんが、SQDプロトコルでは、これらのサンプルを反復的に精緻化し、対角化を行い、各反復におけるエネルギーを次のビット列の精緻化に反映させることで、真の基底状態を復元できることがわかります。 基底状態を含む部分空間は十分に小さいため、正確に計算することが可能であり、この方法によって最終的には正確な基底状態を復元することができる。 `5`具体的には、各ビット列（`8` bits）のそれぞれの半分がハミング重みを持つことがわかっているため、このシステムのヒルベルト部分空間全体のサイズは $\\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. SQDによる後処理\n",
        "\n",
        "SQDアルゴリズムの目的は、QPUから取得したノイズの多いサンプルを精緻化し、最終的にそれらの精緻化されたサンプルによって張られる部分空間においてハミルトニアンを対角化することです。 これは、次のように反復的に行われます：\n",
        "\n",
        "* スピンアップおよびスピンダウンの両サブシステムにおいて、粒子数が正しいサンプルのみを後選択して、サンプルを絞り込む\n",
        "* 精緻化されたサンプルによって張られる小さな部分空間について対角化を行い、エネルギーが最小となる部分空間に関する情報を収集する。\n",
        "* 各反復ごとに：\n",
        "  * 粒子数が不正確なサンプルのビットを確率的に反転させることで、ノイズの多いサンプルを精緻化する。 確率は、直近の対角化から得られた部分空間情報を用いて計算される。\n",
        "  * 精製されたサンプルによって張られる小さな部分空間において対角化を行う\n",
        "  * 最良（最小）のエネルギーをもたらす部分空間に関する情報を収集する\n",
        "\n",
        "出力結果から、アルゴリズムが 回の反復後に `6` 正確な基底状態エネルギーに収束すること、また、次元 $\\binom{8}{5}^2=3136$ のヒルベルト部分空間全体に対して対角化が行われていることがわかります。もちろん、部分空間全体に対して対角化を行うことは通常現実的ではないため、基底状態のサポートが疎な系ほど、SQDなどの部分空間対角化手法の恩恵を受けやすいと言えます。\n",
        "\n",
        "diagonalize\\_fermionic\\_hamiltonian 関数の詳細については、 [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
}