{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "4ba97b35-a453-42fb-a59a-e9f0c129283f",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Formazione sul kernel quantistico\"\n",
        "description: \"Costruisci un modello Qiskit per valutare le voci in una matrice kernel quantistica utilizzata per la classificazione binaria.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore mapsto forall */}\n",
        "\n",
        "<span id=\"quantum-kernel-training\" />\n",
        "\n",
        "# Formazione sul kernel quantistico\n",
        "\n",
        "*Stima dei tempi di esecuzione: meno di un minuto su un processore Heron r3 (NOTA: si tratta solo di una stima). (La durata potrebbe variare.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bd3bec67",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Risultati di apprendimento\n",
        "\n",
        "Una volta completato questo tutorial, avrai acquisito le seguenti conoscenze:\n",
        "\n",
        "* I metodi del kernel e i loro utilizzi\n",
        "* I kernel quantistici e come possono fornire spazi di caratteristiche potenziati\n",
        "* Costruzione di circuiti a kernel quantistico\n",
        "* Come addestrare un kernel quantistico utilizzando un [modello Qiskit](/docs/guides/intro-to-patterns) : mappatura, ottimizzazione, esecuzione e post-elaborazione\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ba36136c",
      "metadata": {},
      "source": [
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Prerequisiti\n",
        "\n",
        "Si consiglia di acquisire familiarità con i kernel quantistici, capire perché sono importanti e come vengono utilizzati nella pratica.\n",
        "\n",
        "* [Kernel quantistici covarianti per dati con struttura di gruppo](https://arxiv.org/abs/2105.03406) (articolo)\n",
        "* [Introduzione ai kernel quantistici e alle macchine a vettori di supporto](https://www.youtube.com/watch?v=GVhCOTzAkCM) (video)\n",
        "* [I kernel quantistici nella pratica](https://www.youtube.com/watch?v=LmQcSxgINis) (video)\n",
        "\n",
        "È inoltre utile avere una conoscenza di base della teoria dei gruppi.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c2d486a5",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Sfondo\n",
        "\n",
        "I metodi basati sul kernel sono molto diffusi nelle applicazioni di apprendimento automatico.\n",
        "In questo contesto, il termine \"kernel\" si riferisce alla matrice del kernel o alle singole voci in essa contenute.\n",
        "In generale, un kernel è una misura di similarità tra dati codificati in uno *spazio di caratteristiche* ad alta dimensione e può essere utilizzato, ad esempio, in compiti di classificazione con le macchine a vettori di supporto.\n",
        "\n",
        "I metodi del kernel quantistico sono quelli che utilizzano i computer quantistici per stimare un kernel.\n",
        "È noto che i computer quantistici sono in grado di codificare i dati in spazi di caratteristiche potenziati dal quantum, sostituendo di fatto i loro analoghi classici.\n",
        "Per $\\vec{x} \\in \\mathbb{R}$ e $\\Psi(\\vec{x}) \\in \\mathbb{R}^{d'}$, solitamente con $d' >d$, $\\Psi(\\vec{x})$ è una mappa delle caratteristiche, $\\vec{x} \\mapsto \\Psi(\\vec{x})$.\n",
        "L'obiettivo di $\\Psi(\\vec{x})$ è quello di separare le categorie di dati tramite un iperpiano.\n",
        "Prendendo come argomenti i vettori nello spazio delle mappe di caratteristiche, la funzione kernel $K(\\vec{x}, \\vec{y}) = \\langle{\\Psi(\\vec{x}) | \\Psi(\\vec{y}) \\rangle{}}$ restituisce il loro prodotto interno: $K: \\mathbb{R}^d \\rightarrow$ $\\mathbb{R}^d$.\n",
        "Tradizionalmente, le mappe di caratteristiche di interesse sono quelle in cui la funzione kernel può essere facilmente calcolata;\n",
        "ovvero, quando il prodotto interno nello spazio delle mappe di caratteristiche può essere espresso in termini dei vettori di dati originali e non è necessario costruire $\\Psi(\\vec{x})$ e $\\Psi(\\vec{y})$.\n",
        "Nel caso dei kernel quantistici, la mappatura delle caratteristiche viene eseguita da un circuito quantistico e il kernel viene stimato utilizzando le probabilità di misura ricavate dal circuito.\n",
        "\n",
        "Questo tutorial illustra come creare un modello Qiskit per valutare gli elementi di una matrice del kernel quantistico utilizzata per la classificazione binaria.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ad7bc6f9",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Requisiti\n",
        "\n",
        "Prima di iniziare questa esercitazione, assicuratevi di aver installato quanto segue:\n",
        "\n",
        "* Qiskit SDK v2.3.1 o versioni successive, con supporto [alla visualizzazione](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.44.0 o versioni successive (`pip install qiskit-ibm-runtime`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dfb22142-2377-40bf-801c-b2116f048a9a",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configura\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",
        "## Esempio di simulatore su piccola scala\n",
        "\n",
        "In questa sezione, illustriamo le quattro fasi del modello Qiskit su un'istanza a sette qubit del problema dell'etichettatura dei coset con errore e calcoliamo il valore di una singola voce della matrice del kernel utilizzando la `StatevectorSampler` primitiva di Qiskit. Un simulatore di vettori di stato è esatto (fatto salvo il rumore di shot) e ci mostra il metodo dall'inizio alla fine senza consumare tempo della QPU. Ripetiamo poi la stessa procedura su hardware reale nella sezione dedicata agli esempi hardware.\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",
        "### Fase 1: mappare gli input classici su un problema quantistico\n",
        "\n",
        "* Input: Set di dati di allenamento.\n",
        "* Uscita: Circuito astratto per il calcolo di una voce della matrice kernel.\n",
        "\n",
        "Il problema di classificazione binaria che intendiamo risolvere in questa sede è noto come \" [*assegnazione di etichette ai coset con errore*](https://arxiv.org/abs/2105.03406) \" Il set di dati di addestramento contiene una struttura di gruppo, costituita da due classi di equivalenza formate da un gruppo e da un sottogruppo.\n",
        "Si considera che il gruppo sia l' $G = SU(2)^{\\otimes n}$ e per i qubit, ovvero lo speciale gruppo unitario delle\n",
        "matrici di $2 \\times 2$, che trova ampia applicazione in natura; ad esempio, nel Modello Standard della fisica delle particelle.\n",
        "Consideriamo il sottogruppo (stabilizzatore del grafo) $S_\\text{graph} < G$ con $S_\\text{graph} = \\langle \\{ X_i \\otimes _{k:(k,i) \\in \\mathcal{E}} Z_k\\} _{i \\in \\mathcal{V}} \\} \\rangle$ per un grafo con archi $\\mathcal{E}$ e vertici $\\mathcal{V}$.\n",
        "Si noti che gli stabilizzatori fissano uno stato di stabilizzatore tale che $D_s | \\psi \\rangle = | \\psi \\rangle,~ \\forall s \\in S_\\text{graph}$.\n",
        "Infine, definiamo due classi di coset a sinistra $C_\\pm = c_\\pm S_\\text{graph}$ estraendo a caso due $c_\\pm \\in G$.\n",
        "\n",
        "Per ulteriori dettagli sul set di dati e su come viene generato, consulta [questo notebook](https://github.com/qiskit-community/prototype-quantum-kernel-training/blob/main/docs/background/qkernels_and_data_w_group_structure.ipynb) tratto dal [Quantum Kernel Training Toolkit](https://github.com/qiskit-community/prototype-quantum-kernel-training/tree/main).\n",
        "\n",
        "Creiamo il circuito quantistico utilizzato per calcolare un elemento della matrice kernel.\n",
        "I dati di input vengono utilizzati per determinare gli angoli di rotazione delle porte parametrizzate del circuito.\n",
        "Per semplicità, useremo i campioni `x1=14` di dati e `x2=19`.\n",
        "\n",
        "***Nota: il set di dati utilizzato in questa esercitazione può essere scaricato [qui](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",
        "### Fase 2: Ottimizzazione del problema per l'esecuzione su hardware quantistico\n",
        "\n",
        "* Dati di input: circuito astratto, non ottimizzato per un backend specifico.\n",
        "* Risultato: circuito di destinazione, ottimizzato per la QPU selezionata.\n",
        "\n",
        "Per il percorso del simulatore a vettori di stato utilizzato in questa sezione, non è necessaria alcuna ottimizzazione specifica del backend: il circuito astratto può essere campionato direttamente. Applichiamo questo passaggio nell'esempio hardware riportato di seguito, in cui il circuito viene transpilato per una QPU reale utilizzando `generate_preset_pass_manager` con `optimization_level=3`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "step3-small-md",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Passaggio 3: eseguire utilizzando Qiskit primitives\n",
        "\n",
        "* Input: Circuito astratto.\n",
        "* Risultato: distribuzione di quasi-probabilità.\n",
        "\n",
        "Utilizza la `StatevectorSampler` primitiva di Qiskit per ricostruire una distribuzione di quasi-probabilità degli stati ottenuta dal campionamento del circuito. Per quanto riguarda il compito di generare una matrice del kernel, ci interessa in particolare la probabilità di rilevare lo stato |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",
        "### Fase 4: Post-elaborazione e restituzione del risultato nel formato classico desiderato\n",
        "\n",
        "* Dati di input: distribuzione di probabilità.\n",
        "* Risultato: un singolo elemento della matrice del kernel.\n",
        "\n",
        "Calcola la probabilità di rilevare un valore di $|0 \\rangle$ e sul circuito di sovrapposizione e inserisci la matrice del kernel nella posizione corrispondente ai campioni rappresentati da questo specifico circuito di sovrapposizione (riga 15, colonna 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",
        "## Esempio di hardware\n",
        "\n",
        "Una matrice kernel quantistica presenta un $\\mathcal{O}(N^2)$ e voci per un $N$ i campioni di addestramento, e ciascuna voce richiede l'esecuzione di un circuito di sovrapposizione la cui profondità in termini di porte a due qubit cresce con le dimensioni della mappa delle caratteristiche. Di conseguenza, estendere questo tutorial a un problema più complesso comporta due costi cumulativi: il tempo di elaborazione della QPU per ogni matrice del kernel cresce in modo quadratico con l' $N$ e, mentre la profondità di `unitary_overlap` (che compone la mappa delle caratteristiche con la sua aggiunta) riduce la fedeltà a causa delle dimensioni del sistema e della connettività dell'hardware attuale. Per mantenere breve la dimostrazione e garantire un confronto chiaro, eseguiamo quindi la **stessa** istanza a sette qubit dell'esempio su piccola scala su una QPU reale e confrontiamo la fedeltà di una singola voce della matrice del kernel con il valore calcolato in precedenza dal simulatore.\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": [
        "Per compilare l'intera matrice del kernel, dovremmo condurre un esperimento quantistico per ciascuna delle sue voci uniche dell' $N(N+1)/2$. La figura sottostante mostra la matrice risultante per questo set di dati; il rosso più scuro indica fedeltà più vicine a 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",
        "## Passi successivi\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Suggerimenti\">\n",
        "  Se questo lavoro ti è sembrato interessante, potrebbero interessarti anche i seguenti contenuti:\n",
        "\n",
        "  * [Quantum Kernel Training Toolkit](https://github.com/qiskit-community/prototype-quantum-kernel-training/tree/main) - il repository del prototipo su cui si basa questo tutorial\n",
        "  * [Addestramento del kernel quantistico per applicazioni \" Machine Learning](https://qiskit-community.github.io/qiskit-machine-learning/tutorials/08_quantum_kernel_trainer.html) \" - un tutorial su Qiskit Machine Learning che illustra come addestrare il parametro addestrabile\n",
        "  * [Introduzione all' Machine Learning](/learning/courses/quantum-machine-learning/introduction) e quantistico: un corso sull'apprendimento automatico quantistico\n",
        "  * [Machine Learning e quantistica da IBM Research](https://research.ibm.com/topics/quantum-machine-learning) - una panoramica sulla ricerca in materia di QML su IBM\n",
        "  * [Kernel quantistici covarianti per dati con struttura di gruppo](https://arxiv.org/abs/2105.03406) - l'articolo su cui si basa questo tutorial\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
}