{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "4ba97b35-a453-42fb-a59a-e9f0c129283f",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Entraînement du noyau quantique\"\n",
        "description: \"Construire un modèle Qiskit pour évaluer les entrées dans une matrice de noyau quantique utilisée pour la classification binaire.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore mapsto forall */}\n",
        "\n",
        "<span id=\"quantum-kernel-training\" />\n",
        "\n",
        "# Entraînement du noyau quantique\n",
        "\n",
        "*Estimation du temps d'exécution : moins d'une minute sur un processeur Heron r3 (REMARQUE : il s'agit uniquement d'une estimation.) (Votre temps d'exécution peut varier.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bd3bec67",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Résultats d'apprentissage\n",
        "\n",
        "À l'issue de ce tutoriel, vous devriez être en mesure de comprendre les points suivants :\n",
        "\n",
        "* Les méthodes du noyau et leurs applications\n",
        "* Les noyaux quantiques et leur capacité à fournir des espaces de caractéristiques améliorés\n",
        "* Construction de circuits à noyau quantique\n",
        "* Comment entraîner un noyau quantique à l'aide d'un [modèle Qiskit](/docs/guides/intro-to-patterns) : cartographier, optimiser, exécuter et post-traiter\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ba36136c",
      "metadata": {},
      "source": [
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Prérequis\n",
        "\n",
        "Il est recommandé de vous familiariser avec les noyaux quantiques, de comprendre leur importance et de découvrir comment ils sont utilisés dans la pratique.\n",
        "\n",
        "* [Noyaux quantiques covariants pour les données présentant une structure de groupe](https://arxiv.org/abs/2105.03406) (article)\n",
        "* [Introduction aux noyaux quantiques et aux machines à vecteurs de support](https://www.youtube.com/watch?v=GVhCOTzAkCM) (vidéo)\n",
        "* [Les noyaux quantiques en pratique](https://www.youtube.com/watch?v=LmQcSxgINis) (vidéo)\n",
        "\n",
        "Il est également utile d'avoir des connaissances de base en théorie des groupes.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c2d486a5",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Arrière-plan\n",
        "\n",
        "Les méthodes basées sur les noyaux sont couramment utilisées dans les applications d'apprentissage automatique.\n",
        "Dans ce contexte, le terme « noyau » désigne la matrice du noyau ou les éléments qui la composent.\n",
        "En général, un noyau est une mesure de similarité entre des données codées dans un *espace de caractéristiques* de haute dimension; il peut être utilisé, par exemple, dans des tâches de classification avec des machines à vecteurs de support.\n",
        "\n",
        "Les méthodes de noyau quantique sont celles qui utilisent des ordinateurs quantiques pour estimer un noyau.\n",
        "On sait que les ordinateurs quantiques peuvent coder des données dans des espaces de caractéristiques améliorés par la mécanique quantique, remplaçant ainsi efficacement leurs équivalents classiques.\n",
        "Pour $\\vec{x} \\in \\mathbb{R}$ et $\\Psi(\\vec{x}) \\in \\mathbb{R}^{d'}$, généralement avec $d' >d$, $\\Psi(\\vec{x})$ est une carte de caractéristiques, $\\vec{x} \\mapsto \\Psi(\\vec{x})$.\n",
        "L'objectif de $\\Psi(\\vec{x})$ est de séparer les catégories de données à l'aide d'un hyperplan.\n",
        "En prenant comme arguments les vecteurs de l'espace de représentation des caractéristiques, la fonction noyau $K(\\vec{x}, \\vec{y}) = \\langle{\\Psi(\\vec{x}) | \\Psi(\\vec{y}) \\rangle{}}$ renvoie leur produit scalaire : $K: \\mathbb{R}^d \\rightarrow$ $\\mathbb{R}^d$.\n",
        "Classiquement, les représentations de caractéristiques qui présentent un intérêt sont celles pour lesquelles la fonction noyau peut être facilement évaluée;\n",
        "c'est-à-dire lorsque le produit scalaire dans l'espace de représentation des caractéristiques peut s'écrire en fonction des vecteurs de données d'origine et qu'il n'est pas nécessaire de construire $\\Psi(\\vec{x})$ et $\\Psi(\\vec{y})$.\n",
        "Dans le cas des noyaux quantiques, la mise en correspondance des caractéristiques est effectuée par un circuit quantique, et le noyau est estimé à partir des probabilités de mesure échantillonnées à partir du circuit.\n",
        "\n",
        "Ce tutoriel explique comment créer un modèle Qiskit permettant d'évaluer les entrées d'une matrice de noyau quantique utilisée pour la classification binaire.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ad7bc6f9",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Exigences\n",
        "\n",
        "Avant de commencer ce tutoriel, assurez-vous que les éléments suivants sont installés :\n",
        "\n",
        "* Qiskit SDK v2.3.1 ou version ultérieure, avec prise en charge [de la visualisation](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.44.0 ou version ultérieure (`pip install qiskit-ibm-runtime`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dfb22142-2377-40bf-801c-b2116f048a9a",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuration\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",
        "## Exemple de simulateur à petite échelle\n",
        "\n",
        "Dans cette section, nous passons en revue les quatre étapes du modèle Qiskit sur une instance à sept qubits du problème de l'étiquetage des classes de cosets avec erreur, et nous évaluons une seule entrée de la matrice du noyau à l'aide de la `StatevectorSampler` primitive de Qiskit. Un simulateur de vecteur d'état est précis (à l'exception du bruit de shot) et nous permet de visualiser la méthode de bout en bout sans mobiliser de temps de calcul sur le QPU. Nous reproduisons ensuite cette même opération sur du matériel réel dans la section consacrée aux exemples matériels.\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",
        "### Étape 1 : Mettre en correspondance les entrées classiques avec un problème quantique\n",
        "\n",
        "* Entrée : Ensemble de données d'apprentissage.\n",
        "* Sortie : Circuit abstrait pour le calcul d'une entrée de la matrice du noyau.\n",
        "\n",
        "Le problème de classification binaire que nous cherchons à résoudre ici est appelé « [*étiquetage de classes d'équivalence avec erreur*](https://arxiv.org/abs/2105.03406) » L'ensemble de données d'apprentissage d'entrée présente une structure de groupe, composée de deux classes de cosets formées par un groupe et un sous-groupe.\n",
        "On considère que ce groupe est l' $G = SU(2)^{\\otimes n}$ e des qubits, qui est le groupe unitaire particulier des\n",
        "matrices de $2 \\times 2$ et qui trouve de nombreuses applications dans la nature; par exemple, dans le Modèle standard de la physique des particules.\n",
        "Nous considérons le sous-groupe (stabilisateur du graphe) $S_\\text{graph} < G$, avec $S_\\text{graph} = \\langle \\{ X_i \\otimes _{k:(k,i) \\in \\mathcal{E}} Z_k\\} _{i \\in \\mathcal{V}} \\} \\rangle$ pour un graphe dont les arêtes sont $\\mathcal{E}$ et les sommets $\\mathcal{V}$.\n",
        "Notons que les stabilisateurs fixent un état de stabilisateur tel que $D_s | \\psi \\rangle = | \\psi \\rangle,~ \\forall s \\in S_\\text{graph}$.\n",
        "Enfin, nous définissons deux classes de gauche $C_\\pm = c_\\pm S_\\text{graph}$ en tirant au hasard deux $c_\\pm \\in G$.\n",
        "\n",
        "Pour plus d'informations sur l'ensemble de données et son mode de génération, consultez [ce cahier de notes](https://github.com/qiskit-community/prototype-quantum-kernel-training/blob/main/docs/background/qkernels_and_data_w_group_structure.ipynb) issu du [Quantum Kernel Training Toolkit](https://github.com/qiskit-community/prototype-quantum-kernel-training/tree/main).\n",
        "\n",
        "Nous créons le circuit quantique servant à évaluer un élément de la matrice noyau.\n",
        "Les données d'entrée servent à déterminer les angles de rotation des portes paramétrées du circuit.\n",
        "Par souci de simplicité, nous utiliserons les échantillons `x1=14` de données et `x2=19`.\n",
        "\n",
        "***Note : Le jeu de données utilisé dans ce tutoriel peut être téléchargé [ici.](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",
        "### Étape 2 : Optimiser le problème pour l'exécution sur du matériel quantique\n",
        "\n",
        "* Entrée : circuit abstrait, non optimisé pour un backend particulier.\n",
        "* Résultat : circuit cible, optimisé pour le QPU sélectionné.\n",
        "\n",
        "Pour le chemin du simulateur de vecteur d'état utilisé dans cette section, aucune optimisation spécifique au backend n'est nécessaire : le circuit abstrait peut être échantillonné directement. Nous mettons cette étape en pratique dans l'exemple matériel ci-dessous, où le circuit est transcompilé pour un QPU réel à l'aide de `generate_preset_pass_manager` avec `optimization_level=3`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "step3-small-md",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Étape 3 : Exécutez à l'aide d' Qiskit primitives\n",
        "\n",
        "* Entrée : Circuit abstrait.\n",
        "* Résultat : distribution quasi-probabiliste.\n",
        "\n",
        "Utilisez la `StatevectorSampler` primitive de Qiskit pour reconstruire une distribution de quasi-probabilité des états obtenus à partir de l'échantillonnage du circuit. Pour ce qui est de la génération d'une matrice de noyau, nous nous intéressons tout particulièrement à la probabilité de mesurer l'état |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",
        "### Étape 4 : Post-traitement et restitution du résultat dans le format classique souhaité\n",
        "\n",
        "* Entrée : distribution de probabilité.\n",
        "* Résultat : un seul élément de la matrice du noyau.\n",
        "\n",
        "Calculez la probabilité de mesurer une valeur de $|0 \\rangle$ e sur le circuit de chevauchement, puis remplissez la matrice du noyau à l'emplacement correspondant aux échantillons représentés par ce circuit de chevauchement particulier (ligne 15, colonne 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",
        "## Exemple de matériel\n",
        "\n",
        "Une matrice de noyau quantique comporte un nombre d'entrées égal à $\\mathcal{O}(N^2)$ pour $N$ échantillons d'apprentissage, et chaque entrée nécessite l'exécution d'un circuit de superposition dont la profondeur en portes à deux qubits augmente avec la taille de la carte de caractéristiques. Par conséquent, l'adaptation de ce tutoriel à un problème de plus grande envergure entraîne deux coûts cumulés : le temps de calcul du QPU par matrice de noyau augmente de manière quadratique avec l' $N$, et la profondeur de `unitary_overlap` (qui compose la carte de caractéristiques avec son adjoint) réduit la fidélité compte tenu de la taille du système et de la connectivité du matériel actuel. Afin de limiter la durée de la démonstration et de permettre une comparaison claire, nous exécutons donc la **même** instance à sept qubits que celle de l'exemple à petite échelle sur un QPU réel, puis nous comparons la fidélité d'une seule entrée de la matrice du noyau à la valeur calculée précédemment par le simulateur.\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": [
        "Pour remplir l'intégralité de la matrice du noyau, il faudrait réaliser une expérience quantique pour chacune de ses $N(N+1)/2$ entrées uniques. La figure ci-dessous présente la matrice obtenue pour cet ensemble de données; les nuances de rouge plus foncées indiquent des fidélités plus proches de 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",
        "## Etapes suivantes\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recommandations\">\n",
        "  Si ce travail vous a paru intéressant, les documents suivants pourraient vous intéresser :\n",
        "\n",
        "  * [Quantum Kernel Training Toolkit](https://github.com/qiskit-community/prototype-quantum-kernel-training/tree/main) - le référentiel de prototypes sur lequel s'appuie ce tutoriel\n",
        "  * [Formation sur Quantum Kernel pour les applications d' Machine Learning s](https://qiskit-community.github.io/qiskit-machine-learning/tutorials/08_quantum_kernel_trainer.html) – un tutoriel sur l' Machine Learning de Qiskit expliquant comment entraîner les paramètres entraînables\n",
        "  * [Introduction à l' Machine Learning quantique](/learning/courses/quantum-machine-learning/introduction) : un cours sur l' quantique\n",
        "  * [Machine Learning s sur la physique quantique sur IBM Research](https://research.ibm.com/topics/quantum-machine-learning) – un aperçu des recherches sur la physique quantique sur IBM\n",
        "  * [Noyaux quantiques covariants pour les données présentant une structure de groupe](https://arxiv.org/abs/2105.03406) – l'article sur lequel s'appuie ce tutoriel\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
}