{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "4ba97b35-a453-42fb-a59a-e9f0c129283f",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"量子カーネル・トレーニング\"\n",
        "description: \"量子カーネル行列への入力評価を行うQiskitパターンを構築する（二値分類用）。\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore mapsto forall */}\n",
        "\n",
        "<span id=\"quantum-kernel-training\" />\n",
        "\n",
        "# 量子カーネル・トレーニング\n",
        "\n",
        "*所要時間の目安：Heron r3 プロセッサで1分未満（注：これはあくまで目安です。 （実行時間は異なる場合があります。）*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bd3bec67",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## 学習成果\n",
        "\n",
        "このチュートリアルを完了すると、以下の内容を理解できるようになります：\n",
        "\n",
        "* カーネル法とその用途\n",
        "* 量子カーネルと、それらがどのように強化された特徴空間を提供できるか\n",
        "* 量子カーネル回路の構築\n",
        "* [Qiskitパターンを](/docs/guides/intro-to-patterns)用いた量子カーネルのトレーニング方法：マッピング、最適化、実行、および後処理\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ba36136c",
      "metadata": {},
      "source": [
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## 前提条件\n",
        "\n",
        "量子カーネルとは何か、なぜ重要なのか、そして実際にどのように活用されているのかについて、あらかじめ理解しておくことをお勧めします。\n",
        "\n",
        "* [群構造を持つデータのための共変量子カーネル](https://arxiv.org/abs/2105.03406) （論文）\n",
        "* [量子カーネルとサポートベクターマシン入門](https://www.youtube.com/watch?v=GVhCOTzAkCM) （動画）\n",
        "* 『[Quantum Kernels in Practice](https://www.youtube.com/watch?v=LmQcSxgINis) 』（動画）\n",
        "\n",
        "また、群論の基礎的な知識を持っておくと役立ちます。\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c2d486a5",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## 背景\n",
        "\n",
        "カーネル法は、機械学習の応用分野において広く用いられている。\n",
        "この文脈において、「カーネル」とは、カーネル行列、またはその個々の要素を指す。\n",
        "一般的に、カーネルとは、高次元*の特徴空間*に符号化されたデータ間の類似度を測る尺度であり、例えばサポートベクターマシン（SVM）を用いた分類タスクなどで活用される。\n",
        "\n",
        "量子カーネル法とは、量子コンピュータを用いてカーネルを推定する手法のことである。\n",
        "量子コンピュータは、量子強化された特徴空間にデータを符号化することができ、これにより従来のアナログ方式を事実上置き換えることができることが知られている。\n",
        "$\\vec{x} \\in \\mathbb{R}$ および $\\Psi(\\vec{x}) \\in \\mathbb{R}^{d'}$ の場合、通常は $d' >d$ を使用し、 $\\Psi(\\vec{x})$ は特徴マップ、 $\\vec{x} \\mapsto \\Psi(\\vec{x})$ となります。\n",
        "$\\Psi(\\vec{x})$ の目的は、データカテゴリを超平面で分離することです。\n",
        "特徴マップ空間内のベクトルを引数として受け取るカーネル関数 $K(\\vec{x}, \\vec{y}) = \\langle{\\Psi(\\vec{x}) | \\Psi(\\vec{y}) \\rangle{}}$ は、それらの内積を返します： $K: \\mathbb{R}^d \\rightarrow$ $\\mathbb{R}^d$。\n",
        "従来、注目される特徴マップとは、カーネル関数を容易に評価できるものです。\n",
        "つまり、特徴マップ空間における内積が元のデータベクトルを用いて表すことができ、 $\\Psi(\\vec{x})$ や $\\Psi(\\vec{y})$ を構築する必要がない場合です。\n",
        "量子カーネルの場合、特徴量のマッピングは量子回路によって行われ、回路からサンプリングされた測定確率を用いてカーネルが推定される。\n",
        "\n",
        "このチュートリアルでは、二値分類に使用される量子カーネル行列への入力を評価するためのQiskitパターンを構築する方法について説明します。\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ad7bc6f9",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## 要件\n",
        "\n",
        "このチュートリアルを始める前に、以下のものがインストールされていることを確認してください：\n",
        "\n",
        "* Qiskit SDK v2.3.1 以降、 [可視化](/docs/api/qiskit/visualization)機能を搭載\n",
        "* Qiskit Runtime v0.44.0 以降 (`pip install qiskit-ibm-runtime`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dfb22142-2377-40bf-801c-b2116f048a9a",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## セットアップ\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "4405f2da-ce6e-4b97-960a-bb04730cfb6d",
      "metadata": {},
      "outputs": [],
      "source": [
        "# General Imports and helper functions\n",
        "import urllib.request\n",
        "\n",
        "import pandas as pd\n",
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "\n",
        "from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit\n",
        "from qiskit.circuit.library import unitary_overlap\n",
        "from qiskit.primitives import StatevectorSampler\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService, Sampler\n",
        "\n",
        "# Download the dataset (portable across platforms)\n",
        "urllib.request.urlretrieve(\n",
        "    \"https://raw.githubusercontent.com/qiskit-community/prototype-quantum-kernel-training/main/data/dataset_graph7.csv\",\n",
        "    \"dataset_graph7.csv\",\n",
        ")\n",
        "\n",
        "\n",
        "def visualize_counts(res_counts, num_qubits, num_shots):\n",
        "    \"\"\"Visualize the outputs from the Qiskit Sampler primitive.\"\"\"\n",
        "    zero_prob = res_counts.get(0, 0.0)\n",
        "    top_10 = dict(\n",
        "        sorted(res_counts.items(), key=lambda item: item[1], reverse=True)[\n",
        "            :10\n",
        "        ]\n",
        "    )\n",
        "    top_10.update({0: zero_prob})\n",
        "    by_key = dict(sorted(top_10.items(), key=lambda item: item[0]))\n",
        "    x_vals, y_vals = list(zip(*by_key.items()))\n",
        "    x_vals = [bin(x_val)[2:].zfill(num_qubits) for x_val in x_vals]\n",
        "    y_vals_prob = []\n",
        "    for t in range(len(y_vals)):\n",
        "        y_vals_prob.append(y_vals[t] / num_shots)\n",
        "    y_vals = y_vals_prob\n",
        "    plt.bar(x_vals, y_vals)\n",
        "    plt.xticks(rotation=75)\n",
        "    plt.title(\"Results of sampling\")\n",
        "    plt.xlabel(\"Measured bitstring\")\n",
        "    plt.ylabel(\"Probability\")\n",
        "    plt.show()\n",
        "\n",
        "\n",
        "def get_training_data():\n",
        "    \"\"\"Read the training data.\"\"\"\n",
        "    df = pd.read_csv(\"dataset_graph7.csv\", sep=\",\", header=None)\n",
        "    training_data = df.values[:20, :]\n",
        "    ind = np.argsort(training_data[:, -1])\n",
        "    X_train = training_data[ind][:, :-1]\n",
        "\n",
        "    return X_train"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "small-scale-intro",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## 小規模シミュレータの例\n",
        "\n",
        "このセクションでは、「ラベリング・コセット・ウィズ・エラー」問題の7キュービットインスタンスを用いて、Qiskitパターンの4つのステップを順を追って解説し、Qiskitのプリミティブ `StatevectorSampler` を使用してカーネル行列の1つの要素を評価します。 状態ベクトルシミュレータは（ショットノイズの範囲内では）正確であり、QPUの処理時間を消費することなく、エンドツーエンドの手法を示してくれます。 次に、ハードウェアの例を示すセクションで、同じ処理を実機上で繰り返します。\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6ddce42e-a365-451d-972d-24c1306ac47b",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "### ステップ1：古典的な入力を量子問題にマッピングする\n",
        "\n",
        "* 入力：訓練データセット．\n",
        "* 出力：カーネル行列のエントリを計算するための抽象回路。\n",
        "\n",
        "ここで解決を目指す二値分類問題は、「 [*誤差を伴う剰余類のラベル付け*](https://arxiv.org/abs/2105.03406) 」と呼ばれている 入力トレーニングデータセットには、群と部分群によって構成される2つの剰余類からなる群構造が含まれている。\n",
        "この群は、量子ビットについて $G = SU(2)^{\\otimes n}$ と定義される。これは、\n",
        "$2 \\times 2$ 行列からなる特別なユニタリ群であり、自然界において、例えば素粒子物理学の標準模型など、幅広い応用が見られる。\n",
        "辺が $\\mathcal{E}$、頂点が $\\mathcal{V}$ であるグラフについて、安定化子群 $S_\\text{graph} < G$ （ $S_\\text{graph} = \\langle \\{ X_i \\otimes _{k:(k,i) \\in \\mathcal{E}} Z_k\\} _{i \\in \\mathcal{V}} \\} \\rangle$ ）を考える。\n",
        "安定化子は、 $D_s | \\psi \\rangle = | \\psi \\rangle,~ \\forall s \\in S_\\text{graph}$ となるような安定化子状態を固定することに注意されたい。\n",
        "最後に、2つの $c_\\pm \\in G$ をランダムに選び、2つの左剰余類 $C_\\pm = c_\\pm S_\\text{graph}$ を定義する。\n",
        "\n",
        "データセットの詳細やその生成方法については、 [Quantum Kernel Training Toolkit](https://github.com/qiskit-community/prototype-quantum-kernel-training/tree/main) の[このノートブック](https://github.com/qiskit-community/prototype-quantum-kernel-training/blob/main/docs/background/qkernels_and_data_w_group_structure.ipynb)をご覧ください。\n",
        "\n",
        "カーネル行列の1つの要素を評価するために使用する量子回路を作成します。\n",
        "入力データは、回路のパラメータ化されたゲートの回転角度を決定するために使用されます。\n",
        "`x2=19`簡便のため、データサンプル `x1=14` と を使用する。\n",
        "\n",
        "***注：このチュートリアルで使用したデータセットは、 [こちらから](https://github.com/qiskit-community/prototype-quantum-kernel-training/blob/main/data/dataset_graph7.csv)ダウンロードできます。***\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "70d6faff-9a56-44bb-b26f-f573a8c90889",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/quantum-kernel-training/extracted-outputs/70d6faff-9a56-44bb-b26f-f573a8c90889-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Prepare training data\n",
        "X_train = get_training_data()\n",
        "\n",
        "# Empty kernel matrix\n",
        "num_samples = np.shape(X_train)[0]\n",
        "kernel_matrix = np.full((num_samples, num_samples), np.nan)\n",
        "\n",
        "# Prepare feature map for computing overlap\n",
        "num_features = np.shape(X_train)[1]\n",
        "num_qubits = int(num_features / 2)\n",
        "entangler_map = [[0, 2], [3, 4], [2, 5], [1, 4], [2, 3], [4, 6]]\n",
        "fm = QuantumCircuit(num_qubits)\n",
        "training_param = Parameter(\"θ\")\n",
        "feature_params = ParameterVector(\"x\", num_qubits * 2)\n",
        "fm.ry(training_param, fm.qubits)\n",
        "for cz in entangler_map:\n",
        "    fm.cz(cz[0], cz[1])\n",
        "for i in range(num_qubits):\n",
        "    fm.rz(-2 * feature_params[2 * i + 1], i)\n",
        "    fm.rx(-2 * feature_params[2 * i], i)\n",
        "\n",
        "# Assign tunable parameter to known optimal value and set the data params for\n",
        "# first two samples\n",
        "x1 = 14\n",
        "x2 = 19\n",
        "unitary1 = fm.assign_parameters(list(X_train[x1]) + [np.pi / 2])\n",
        "unitary2 = fm.assign_parameters(list(X_train[x2]) + [np.pi / 2])\n",
        "\n",
        "# Create the overlap circuit\n",
        "overlap_circ = unitary_overlap(unitary1, unitary2)\n",
        "overlap_circ.measure_all()\n",
        "overlap_circ.draw(\"mpl\", scale=0.6, style=\"iqp\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "step2-small-md",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "### ステップ2：量子ハードウェア実行に向けた問題の最適化\n",
        "\n",
        "* 入力：抽象的な回路。特定のバックエンド向けに最適化されていない。\n",
        "* 出力：選択されたQPU向けに最適化されたターゲット回路。\n",
        "\n",
        "このセクションで使用される状態ベクトルシミュレータパスについては、バックエンド固有の最適化は不要であり、抽象回路を直接サンプリングすることができます。 `optimization_level=3`この手順は、以下のハードウェアの例で実行します。この例では、実際のQPUに対して、\\`\\` `generate_preset_pass_manager` を使用して回路のトランスパイルを行います。\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "step3-small-md",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### ステップ3: `Qiskit primitives`を使用して実行する\n",
        "\n",
        "* 入力：抽象回路。\n",
        "* 出力：準確率分布。\n",
        "\n",
        "`StatevectorSampler` Qiskitのプリミティブを使用して、回路のサンプリングから得られる状態の準確率分布を再構築します。 カーネル行列を生成する課題において、我々は特に|0>状態が観測される確率に関心を持っている。\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "step3-small-code",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/quantum-kernel-training/extracted-outputs/step3-small-code-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "sampler = StatevectorSampler()\n",
        "\n",
        "# Execute and get counts\n",
        "num_shots = 10_000\n",
        "results = sampler.run([overlap_circ], shots=num_shots).result()\n",
        "counts = results[0].data.meas.get_int_counts()\n",
        "\n",
        "# Plot counts\n",
        "visualize_counts(counts, num_qubits, num_shots)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "step4-small-md",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "### ステップ4：後処理を行い、結果を希望の古典形式で返す\n",
        "\n",
        "* 入力：確率分布。\n",
        "* 出力：カーネル行列の要素1つ。\n",
        "\n",
        "オーバーラップ回路において $|0 \\rangle$ が測定される確率を計算し、この特定のオーバーラップ回路によって表されるサンプルに対応する位置（行15、列20）にカーネル行列を格納する。\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "step4-small-code",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Fidelity (simulator): 0.8261\n"
          ]
        }
      ],
      "source": [
        "kernel_matrix[x1, x2] = counts.get(0, 0.0) / num_shots\n",
        "print(f\"Fidelity (simulator): {kernel_matrix[x1, x2]}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "large-scale-intro",
      "metadata": {},
      "source": [
        "<span id=\"hardware-example\" />\n",
        "\n",
        "## ハードウェアの例\n",
        "\n",
        "量子カーネル行列には、 $N$ 個の学習サンプルに対応する $\\mathcal{O}(N^2)$ 個の要素があり、各要素を処理するには、特徴マップのサイズに応じて2量子ビットゲートの深さが増加するオーバーラップ回路を実行する必要がある。 その結果、このチュートリアルをより大規模な問題に適用する際には、2つの相乗的なコストが生じます。すなわち、カーネル行列あたりのQPU処理時間は $N$ に比例して2乗的に増加し、また（特徴マップとその随伴行列を合成する）深さが深くなるほど `unitary_overlap` 、現在のハードウェアのシステム規模や接続性において忠実度が低下します。 デモを簡潔にし、明確な比較を行うため、小規模な例で**用**いたのと同じ7キュービットのインスタンスを実際のQPU上で実行し、カーネル行列の単一要素のフィデリティを、前述のシミュレータで算出された値と比較します。\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "d2f4f6cf-067e-4d53-aa04-7ca9c803d3e1",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Using backend: ibm_pittsburgh\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/quantum-kernel-training/extracted-outputs/d2f4f6cf-067e-4d53-aa04-7ca9c803d3e1-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Fidelity (hardware): 0.7517\n"
          ]
        }
      ],
      "source": [
        "# ------------------------------ Step 1 ------------------------------\n",
        "# Prepare training data\n",
        "X_train = get_training_data()\n",
        "\n",
        "# Empty kernel matrix\n",
        "num_samples = np.shape(X_train)[0]\n",
        "kernel_matrix = np.full((num_samples, num_samples), np.nan)\n",
        "\n",
        "# Prepare feature map for computing overlap\n",
        "num_features = np.shape(X_train)[1]\n",
        "num_qubits = int(num_features / 2)\n",
        "entangler_map = [[0, 2], [3, 4], [2, 5], [1, 4], [2, 3], [4, 6]]\n",
        "fm = QuantumCircuit(num_qubits)\n",
        "training_param = Parameter(\"θ\")\n",
        "feature_params = ParameterVector(\"x\", num_qubits * 2)\n",
        "fm.ry(training_param, fm.qubits)\n",
        "for cz in entangler_map:\n",
        "    fm.cz(cz[0], cz[1])\n",
        "for i in range(num_qubits):\n",
        "    fm.rz(-2 * feature_params[2 * i + 1], i)\n",
        "    fm.rx(-2 * feature_params[2 * i], i)\n",
        "\n",
        "# Assign tunable parameter to known optimal value and\n",
        "# set the data params for first two samples\n",
        "x1 = 14\n",
        "x2 = 19\n",
        "unitary1 = fm.assign_parameters(list(X_train[x1]) + [np.pi / 2])\n",
        "unitary2 = fm.assign_parameters(list(X_train[x2]) + [np.pi / 2])\n",
        "\n",
        "# Create the overlap circuit\n",
        "overlap_circ = unitary_overlap(unitary1, unitary2)\n",
        "overlap_circ.measure_all()\n",
        "\n",
        "# ------------------------------ Step 2 ------------------------------\n",
        "service = QiskitRuntimeService()\n",
        "# backend = service.least_busy(\n",
        "#    operational=True, simulator=False, min_num_qubits=overlap_circ.num_qubits\n",
        "# )\n",
        "backend = service.backend(\"ibm_pittsburgh\")\n",
        "print(f\"Using backend: {backend.name}\")\n",
        "pm = generate_preset_pass_manager(optimization_level=3, backend=backend)\n",
        "overlap_ibm = pm.run(overlap_circ)\n",
        "\n",
        "# ------------------------------ Step 3 ------------------------------\n",
        "sampler = Sampler(mode=backend)\n",
        "sampler.options.environment.job_tags = [\"TUT_QKT\"]\n",
        "\n",
        "num_shots = 10_000\n",
        "results = sampler.run([overlap_ibm], shots=num_shots).result()\n",
        "counts = results[0].data.meas.get_int_counts()\n",
        "visualize_counts(counts, num_qubits, num_shots)\n",
        "\n",
        "# ------------------------------ Step 4 ------------------------------\n",
        "kernel_matrix[x1, x2] = counts.get(0, 0.0) / num_shots\n",
        "print(f\"Fidelity (hardware): {kernel_matrix[x1, x2]}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dec33657-9055-4fdc-b146-dba0e5882719",
      "metadata": {},
      "source": [
        "カーネル行列全体を埋めるには、その $N(N+1)/2$ 個の固有要素それぞれについて量子実験を行う必要がある。 下の図は、このデータセットから得られた行列を示しています。赤色が濃いほど、 1.0 に近い忠実度を示しています。\n",
        "\n",
        "![kernel\\_matrix.png](https://quantum.cloud.ibm.com/docs/images/tutorials/quantum-kernel-training/kernel_matrix.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6847b837",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## 次のステップ\n",
        "\n",
        "<Admonition type=\"tip\" title=\"推奨事項\">\n",
        "  この作品に興味を持たれた方は、以下の資料もご参照ください：\n",
        "\n",
        "  * [Quantum Kernel Training Toolkit](https://github.com/qiskit-community/prototype-quantum-kernel-training/tree/main) - 本チュートリアルが基にしているプロトタイプリポジトリ\n",
        "  * [Machine Learning アプリケーション向けQuantum Kernelトレーニング](https://qiskit-community.github.io/qiskit-machine-learning/tutorials/08_quantum_kernel_trainer.html) - トレーニング可能なパラメータの学習方法を解説するQiskitの Machine Learning チュートリアル\n",
        "  * 『[Quantum Machine Learning 入門](/learning/courses/quantum-machine-learning/introduction) 』―量子機械学習に関する講座\n",
        "  * [IBM Research による「Quantum Machine Learning](https://research.ibm.com/topics/quantum-machine-learning) 」— IBM におけるQML研究の概要\n",
        "  * [群構造を持つデータのための共変量子カーネル](https://arxiv.org/abs/2105.03406) ― 本チュートリアルが基づく論文\n",
        "</Admonition>\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"
    },
    "hours": 1,
    "qpuSeconds": 60
  },
  "nbformat": 4,
  "nbformat_minor": 5
}