Formazione sul kernel quantistico
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.)
Risultati di apprendimento
Una volta completato questo tutorial, avrai acquisito le seguenti conoscenze:
- I metodi del kernel e i loro utilizzi
- I kernel quantistici e come possono fornire spazi di caratteristiche potenziati
- Costruzione di circuiti a kernel quantistico
- Come addestrare un kernel quantistico utilizzando un modello Qiskit : mappatura, ottimizzazione, esecuzione e post-elaborazione
Prerequisiti
Si consiglia di acquisire familiarità con i kernel quantistici, capire perché sono importanti e come vengono utilizzati nella pratica.
- Kernel quantistici covarianti per dati con struttura di gruppo (articolo)
- Introduzione ai kernel quantistici e alle macchine a vettori di supporto (video)
- I kernel quantistici nella pratica (video)
È inoltre utile avere una conoscenza di base della teoria dei gruppi.
Sfondo
I metodi basati sul kernel sono molto diffusi nelle applicazioni di apprendimento automatico. In questo contesto, il termine "kernel" si riferisce alla matrice del kernel o alle singole voci in essa contenute. 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.
I metodi del kernel quantistico sono quelli che utilizzano i computer quantistici per stimare un kernel. È 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. Per e , solitamente con , è una mappa delle caratteristiche, . L'obiettivo di è quello di separare le categorie di dati tramite un iperpiano. Prendendo come argomenti i vettori nello spazio delle mappe di caratteristiche, la funzione kernel restituisce il loro prodotto interno: . Tradizionalmente, le mappe di caratteristiche di interesse sono quelle in cui la funzione kernel può essere facilmente calcolata; 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 e . 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.
Questo tutorial illustra come creare un modello Qiskit per valutare gli elementi di una matrice del kernel quantistico utilizzata per la classificazione binaria.
Requisiti
Prima di iniziare questa esercitazione, assicuratevi di aver installato quanto segue:
- Qiskit SDK v2.3.1 o versioni successive, con supporto alla visualizzazione
- Qiskit Runtime v0.44.0 o versioni successive (
pip install qiskit-ibm-runtime)
Configura
# General Imports and helper functions
import urllib.request
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit
from qiskit.circuit.library import unitary_overlap
from qiskit.primitives import StatevectorSampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler
# Download the dataset (portable across platforms)
urllib.request.urlretrieve(
"https://raw.githubusercontent.com/qiskit-community/prototype-quantum-kernel-training/main/data/dataset_graph7.csv",
"dataset_graph7.csv",
)
def visualize_counts(res_counts, num_qubits, num_shots):
"""Visualize the outputs from the Qiskit Sampler primitive."""
zero_prob = res_counts.get(0, 0.0)
top_10 = dict(
sorted(res_counts.items(), key=lambda item: item[1], reverse=True)[
:10
]
)
top_10.update({0: zero_prob})
by_key = dict(sorted(top_10.items(), key=lambda item: item[0]))
x_vals, y_vals = list(zip(*by_key.items()))
x_vals = [bin(x_val)[2:].zfill(num_qubits) for x_val in x_vals]
y_vals_prob = []
for t in range(len(y_vals)):
y_vals_prob.append(y_vals[t] / num_shots)
y_vals = y_vals_prob
plt.bar(x_vals, y_vals)
plt.xticks(rotation=75)
plt.title("Results of sampling")
plt.xlabel("Measured bitstring")
plt.ylabel("Probability")
plt.show()
def get_training_data():
"""Read the training data."""
df = pd.read_csv("dataset_graph7.csv", sep=",", header=None)
training_data = df.values[:20, :]
ind = np.argsort(training_data[:, -1])
X_train = training_data[ind][:, :-1]
return X_trainEsempio di simulatore su piccola scala
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.
Fase 1: mappare gli input classici su un problema quantistico
- Input: Set di dati di allenamento.
- Uscita: Circuito astratto per il calcolo di una voce della matrice kernel.
Il problema di classificazione binaria che intendiamo risolvere in questa sede è noto come " assegnazione di etichette ai coset con errore " 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. Si considera che il gruppo sia l' e per i qubit, ovvero lo speciale gruppo unitario delle matrici di , che trova ampia applicazione in natura; ad esempio, nel Modello Standard della fisica delle particelle. Consideriamo il sottogruppo (stabilizzatore del grafo) con per un grafo con archi e vertici . Si noti che gli stabilizzatori fissano uno stato di stabilizzatore tale che . Infine, definiamo due classi di coset a sinistra estraendo a caso due .
Per ulteriori dettagli sul set di dati e su come viene generato, consulta questo notebook tratto dal Quantum Kernel Training Toolkit.
Creiamo il circuito quantistico utilizzato per calcolare un elemento della matrice kernel.
I dati di input vengono utilizzati per determinare gli angoli di rotazione delle porte parametrizzate del circuito.
Per semplicità, useremo i campioni x1=14 di dati e x2=19.
Nota: il set di dati utilizzato in questa esercitazione può essere scaricato qui.
# Prepare training data
X_train = get_training_data()
# Empty kernel matrix
num_samples = np.shape(X_train)[0]
kernel_matrix = np.full((num_samples, num_samples), np.nan)
# Prepare feature map for computing overlap
num_features = np.shape(X_train)[1]
num_qubits = int(num_features / 2)
entangler_map = [[0, 2], [3, 4], [2, 5], [1, 4], [2, 3], [4, 6]]
fm = QuantumCircuit(num_qubits)
training_param = Parameter("θ")
feature_params = ParameterVector("x", num_qubits * 2)
fm.ry(training_param, fm.qubits)
for cz in entangler_map:
fm.cz(cz[0], cz[1])
for i in range(num_qubits):
fm.rz(-2 * feature_params[2 * i + 1], i)
fm.rx(-2 * feature_params[2 * i], i)
# Assign tunable parameter to known optimal value and set the data params for
# first two samples
x1 = 14
x2 = 19
unitary1 = fm.assign_parameters(list(X_train[x1]) + [np.pi / 2])
unitary2 = fm.assign_parameters(list(X_train[x2]) + [np.pi / 2])
# Create the overlap circuit
overlap_circ = unitary_overlap(unitary1, unitary2)
overlap_circ.measure_all()
overlap_circ.draw("mpl", scale=0.6, style="iqp")Output:
Fase 2: Ottimizzazione del problema per l'esecuzione su hardware quantistico
- Dati di input: circuito astratto, non ottimizzato per un backend specifico.
- Risultato: circuito di destinazione, ottimizzato per la QPU selezionata.
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.
Passaggio 3: eseguire utilizzando Qiskit primitives
- Input: Circuito astratto.
- Risultato: distribuzione di quasi-probabilità.
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>.
sampler = StatevectorSampler()
# Execute and get counts
num_shots = 10_000
results = sampler.run([overlap_circ], shots=num_shots).result()
counts = results[0].data.meas.get_int_counts()
# Plot counts
visualize_counts(counts, num_qubits, num_shots)Output:
Fase 4: Post-elaborazione e restituzione del risultato nel formato classico desiderato
- Dati di input: distribuzione di probabilità.
- Risultato: un singolo elemento della matrice del kernel.
Calcola la probabilità di rilevare un valore di 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).
kernel_matrix[x1, x2] = counts.get(0, 0.0) / num_shots
print(f"Fidelity (simulator): {kernel_matrix[x1, x2]}")Output:
Fidelity (simulator): 0.8261
Esempio di hardware
Una matrice kernel quantistica presenta un e voci per un 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' 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.
# ------------------------------ Step 1 ------------------------------
# Prepare training data
X_train = get_training_data()
# Empty kernel matrix
num_samples = np.shape(X_train)[0]
kernel_matrix = np.full((num_samples, num_samples), np.nan)
# Prepare feature map for computing overlap
num_features = np.shape(X_train)[1]
num_qubits = int(num_features / 2)
entangler_map = [[0, 2], [3, 4], [2, 5], [1, 4], [2, 3], [4, 6]]
fm = QuantumCircuit(num_qubits)
training_param = Parameter("θ")
feature_params = ParameterVector("x", num_qubits * 2)
fm.ry(training_param, fm.qubits)
for cz in entangler_map:
fm.cz(cz[0], cz[1])
for i in range(num_qubits):
fm.rz(-2 * feature_params[2 * i + 1], i)
fm.rx(-2 * feature_params[2 * i], i)
# Assign tunable parameter to known optimal value and
# set the data params for first two samples
x1 = 14
x2 = 19
unitary1 = fm.assign_parameters(list(X_train[x1]) + [np.pi / 2])
unitary2 = fm.assign_parameters(list(X_train[x2]) + [np.pi / 2])
# Create the overlap circuit
overlap_circ = unitary_overlap(unitary1, unitary2)
overlap_circ.measure_all()
# ------------------------------ Step 2 ------------------------------
service = QiskitRuntimeService()
# backend = service.least_busy(
# operational=True, simulator=False, min_num_qubits=overlap_circ.num_qubits
# )
backend = service.backend("ibm_pittsburgh")
print(f"Using backend: {backend.name}")
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
overlap_ibm = pm.run(overlap_circ)
# ------------------------------ Step 3 ------------------------------
sampler = Sampler(mode=backend)
sampler.options.environment.job_tags = ["TUT_QKT"]
num_shots = 10_000
results = sampler.run([overlap_ibm], shots=num_shots).result()
counts = results[0].data.meas.get_int_counts()
visualize_counts(counts, num_qubits, num_shots)
# ------------------------------ Step 4 ------------------------------
kernel_matrix[x1, x2] = counts.get(0, 0.0) / num_shots
print(f"Fidelity (hardware): {kernel_matrix[x1, x2]}")Output:
Using backend: ibm_pittsburgh
Fidelity (hardware): 0.7517
Per compilare l'intera matrice del kernel, dovremmo condurre un esperimento quantistico per ciascuna delle sue voci uniche dell' . La figura sottostante mostra la matrice risultante per questo set di dati; il rosso più scuro indica fedeltà più vicine a 1.0.
Passi successivi
Se questo lavoro ti è sembrato interessante, potrebbero interessarti anche i seguenti contenuti:
- Quantum Kernel Training Toolkit - il repository del prototipo su cui si basa questo tutorial
- Addestramento del kernel quantistico per applicazioni " Machine Learning " - un tutorial su Qiskit Machine Learning che illustra come addestrare il parametro addestrabile
- Introduzione all' Machine Learning e quantistico: un corso sull'apprendimento automatico quantistico
- Machine Learning e quantistica da IBM Research - una panoramica sulla ricerca in materia di QML su IBM
- Kernel quantistici covarianti per dati con struttura di gruppo - l'articolo su cui si basa questo tutorial