Entraînement du noyau quantique
Estimation d'utilisation : moins d'une minute sur un processeur Eagle r3 (REMARQUE : Il s'agit uniquement d'une estimation. Votre temps d'exécution peut varier.)
Contexte
Ce tutoriel montre comment construire un Qiskit pattern pour évaluer les entrées d'une matrice de noyau quantique utilisée pour la classification binaire. Pour plus d'informations sur les Qiskit patterns et comment Qiskit Serverless peut être utilisé pour les déployer dans le cloud pour une exécution gérée, consultez notre page de documentation sur IBM Quantum® Platform.
Prérequis
Avant de commencer ce tutoriel, assurez-vous d'avoir installé les éléments suivants :
- Qiskit SDK v1.0 ou ultérieur, avec le support de visualisation
- Qiskit Runtime v0.22 ou ultérieur (
pip install qiskit-ibm-runtime)
Configuration
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy pandas qiskit qiskit-ibm-catalog qiskit-ibm-runtime
!wget https://raw.githubusercontent.com/qiskit-community/prototype-quantum-kernel-training/main/data/dataset_graph7.csv
# General Imports and helper functions
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 UnitaryOverlap
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler
# from qiskit_serverless import IBMServerlessClient, QiskitFunction
from qiskit_ibm_catalog import QiskitServerless, QiskitFunction
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_train
7[1A[1G[27G[Files: 0 Bytes: 0 [0 B/s] Re]87[2A[1G[27G[https://raw.githubusercontent.]87[1S[3A[1G[0JSaving 'dataset_graph7.csv.1'
87[2A[1Gdataset_graph7.csv.1 100% [=============================>] 20.25K --.-KB/s87[1S[3A[1G[0JHTTP response 200 [https://raw.githubusercontent.com/qiskit-community/prototype-quantum-kernel-training/main/data/dataset_graph7.csv]
87[2A[1Gdataset_graph7.csv.1 100% [=============================>] 20.25K --.-KB/s87[1A[1G[27G[Files: 1 Bytes: 20.25K [93.33]8[m[m[m[m
Étape 1 : Transposer les entrées classiques en un problème quantique
- Entrée : Jeu de données d'entraînement.
- Sortie : Circuit abstrait pour le calcul d'une entrée de la matrice de noyau.
Créez le circuit quantique utilisé pour évaluer une entrée dans la matrice de noyau. Nous utilisons les données d'entrée pour déterminer les angles de rotation des portes paramétrées du circuit. Nous utiliserons les échantillons de données x1=14 et x2=19.
Remarque : Le jeu de données utilisé dans ce tutoriel peut être téléchargé ici.
# 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 = UnitaryOverlap(unitary1, unitary2)
overlap_circ.measure_all()
overlap_circ.draw("mpl", scale=0.6, style="iqp")
Étape 2 : Optimiser le problème pour l'exécution sur du matériel quantique
- Entrée : Circuit abstrait, non optimisé pour un backend particulier
- Sortie : Circuit cible et observable, optimisés pour le QPU sélectionné
Utilisez la fonction generate_preset_pass_manager de Qiskit pour spécifier une routine d'optimisation de notre circuit par rapport au QPU sur lequel nous prévoyons d'exécuter l'expérience. Nous définissons optimization_level=3, ce qui signifie que nous utiliserons le gestionnaire de passes prédéfini offrant le plus haut niveau d'optimisation.
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=overlap_circ.num_qubits
)
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
overlap_ibm = pm.run(overlap_circ)
overlap_ibm.draw("mpl", scale=0.6, idle_wires=False, fold=-1, style="iqp")
Étape 3 : Exécuter à l'aide des primitives Qiskit
- Entrée : Circuit cible
- Sortie : Distribution de quasi-probabilités
Utilisez la primitive Sampler de Qiskit Runtime pour reconstruire une distribution de quasi-probabilités des états obtenus par échantillonnage du circuit. Pour la tâche de génération d'une matrice de noyau, nous nous intéressons particulièrement à la probabilité de mesurer l'état |0>.
Pour cette démonstration, nous exécuterons sur un QPU avec les primitives qiskit-ibm-runtime. Pour exécuter sur les primitives basées sur le vecteur d'état de qiskit, remplacez le bloc de code utilisant les primitives Qiskit IBM® Runtime par le bloc commenté.
num_shots = 10_000
## Evaluate the problem using statevector-based primitives from Qiskit
# from qiskit.primitives import StatevectorSampler
# sampler = StatevectorSampler()
# results = sampler.run([overlap_circ]).result()
# counts = results[0].data.meas.get_int_counts()
# Evaluate the problem using a QPU via Qiskit IBM Runtime
sampler = Sampler(mode=backend)
results = sampler.run([overlap_ibm]).result()
counts = results[0].data.meas.get_int_counts()
visualize_counts(counts, num_qubits, num_shots)
Étape 4 : Post-traiter et renvoyer le résultat dans le format classique souhaité
- Entrée : Distribution de probabilités
- Sortie : Un seul élément de la matrice de noyau
Calculez la probabilité de mesurer |0> sur le circuit de recouvrement, et remplissez la matrice de noyau à la position correspondant aux échantillons représentés par ce circuit de recouvrement particulier (ligne 15, colonne 20). Dans cette visualisation, un rouge plus foncé indique des fidélités plus proches de 1.0. Pour remplir l'intégralité de la matrice de noyau, nous devons exécuter une expérience quantique pour chaque entrée.
# Calculate the fidelity, or the probability to measure 0
kernel_matrix[x1, x2] = counts.get(0, 0.0) / num_shots
print(f"Fidelity: {kernel_matrix[x1, x2]}")
Fidelity: 0.1279
Déployer le patron Qiskit dans le cloud
Pour ce faire, déplacez le code source ci-dessus dans un fichier, ./source/generate_kernel_entry.py, encapsulez le code dans un script qui prend des entrées et renvoie la solution finale, puis téléversez-le vers un cluster distant à l'aide de la classe QiskitFunction de Qiskit Serverless. Pour des conseils sur la spécification des dépendances externes, le passage d'arguments d'entrée et plus encore, consultez les guides Qiskit Serverless.
L'entrée du patron est une paire d'échantillons de données, x1 et x2. La sortie est la fidélité entre les deux échantillons. Cette valeur sera utilisée pour remplir l'entrée de la matrice de noyau correspondant à ces deux échantillons.
serverless = QiskitServerless()
kernel_entry_pattern = QiskitFunction(
title="generate-kernel-entry",
entrypoint="generate_kernel_entry.py",
working_dir="./source/",
)
serverless.upload(kernel_entry_pattern)
Exécuter le patron Qiskit en tant que service géré
Une fois que nous avons téléversé le patron dans le cloud, nous pouvons facilement l'exécuter à l'aide du client IBMServerlessProvider. Par souci de simplicité, nous utiliserons un simulateur quantique exact dans l'environnement cloud, de sorte que la fidélité que nous calculons sera exacte.
generate_kernel_entry = serverless.load("generate-kernel-entry")
job = generate_kernel_entry.run(
sample1=list(X_train[x1]), sample2=list(X_train[x2])
)
kernel_matrix[x1, x2] = job.result()["fidelity"]
print(f"fidelity: {kernel_matrix[x1, x2]}")
Enquête sur le tutoriel
Veuillez répondre à cette courte enquête pour nous faire part de vos commentaires sur ce tutoriel. Vos retours nous aideront à améliorer nos contenus et l'expérience utilisateur.