Training von Quantenkernen
Geschätzter Aufwand: unter einer Minute auf einem Eagle-r3-Prozessor (HINWEIS: Dies ist nur eine Schätzung. Deine tatsächliche Laufzeit kann abweichen.)
Hintergrund
Dieses Tutorial zeigt, wie du ein Qiskit pattern erstellst, um Einträge einer Quantenkernmatrix für binäre Klassifikation zu berechnen. Weitere Informationen zu Qiskit patterns und dazu, wie Qiskit Serverless genutzt werden kann, um sie für eine verwaltete Ausführung in der Cloud bereitzustellen, findest du auf unserer Dokumentationsseite zur IBM Quantum® Platform.
Voraussetzungen
Stelle vor dem Start dieses Tutorials sicher, dass folgende Pakete installiert sind:
- Qiskit SDK v1.0 oder höher, mit Unterstützung für Visualisierung
- Qiskit Runtime v0.22 oder höher (
pip install qiskit-ibm-runtime)
Einrichtung
# 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
Schritt 1: Klassische Eingaben auf ein Quantenproblem abbilden
- Eingabe: Trainingsdatensatz.
- Ausgabe: Abstrakter Circuit zur Berechnung eines Kernmatrixeintrags.
Erstelle den Quantum Circuit, der zur Auswertung eines Eintrags in der Kernmatrix verwendet wird. Wir nutzen die Eingabedaten, um die Rotationswinkel der parametrisierten Gates des Circuits zu bestimmen. Wir verwenden die Datenpunkte x1=14 und x2=19.
Hinweis: Der in diesem Tutorial verwendete Datensatz kann hier heruntergeladen werden.
# 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")
Schritt 2: Problem für die Ausführung auf Quantenhardware optimieren
- Eingabe: Abstrakter Circuit, nicht für ein bestimmtes Backend optimiert
- Ausgabe: Ziel-Circuit und Observable, optimiert für den ausgewählten QPU
Verwende die Funktion generate_preset_pass_manager aus Qiskit, um eine Optimierungsroutine für unseren Circuit bezüglich des QPU festzulegen, auf dem wir das Experiment ausführen möchten. Wir setzen optimization_level=3, was bedeutet, dass wir den vordefinierten Pass-Manager mit dem höchsten Optimierungsgrad verwenden.
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")
Schritt 3: Ausführung mit Qiskit-Primitiven
- Eingabe: Ziel-Circuit
- Ausgabe: Quasi-Wahrscheinlichkeitsverteilung
Verwende das Sampler-Primitive aus Qiskit Runtime, um eine Quasi-Wahrscheinlichkeitsverteilung der Zustände zu rekonstruieren, die beim Sampling des Circuits entstehen. Für die Erstellung einer Kernmatrix interessiert uns besonders die Wahrscheinlichkeit, den Zustand |0> zu messen.
Für diese Demo führen wir die Berechnung auf einem QPU mit den Primitiven von qiskit-ibm-runtime aus. Um stattdessen die zustandsvektor-basierten Primitive von qiskit zu verwenden, ersetze den Codeblock mit den Qiskit IBM® Runtime-Primitiven durch den auskommentierten Block.
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)
Schritt 4: Nachverarbeitung und Rückgabe des Ergebnisses im gewünschten klassischen Format
- Eingabe: Wahrscheinlichkeitsverteilung
- Ausgabe: Ein einzelnes Kernmatrixelement
Berechne die Wahrscheinlichkeit, |0> auf dem Overlap-Circuit zu messen, und befülle die Kernmatrix an der Position, die den durch diesen Circuit repräsentierten Datenpunkten entspricht (Zeile 15, Spalte 20). In dieser Visualisierung steht ein dunkleres Rot für Fidelitätswerte nahe 1,0. Um die gesamte Kernmatrix zu füllen, müssen wir für jeden Eintrag ein Quantenexperiment durchführen.
# 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
Das Qiskit-Pattern in der Cloud bereitstellen
Verschiebe dazu den obigen Quellcode in eine Datei ./source/generate_kernel_entry.py, verpacke ihn in ein Skript, das Eingaben entgegennimmt und die endgültige Lösung zurückgibt, und lade es anschließend mit der Klasse QiskitFunction aus Qiskit Serverless auf einen Remote-Cluster hoch. Hinweise zur Angabe externer Abhängigkeiten, zur Übergabe von Eingabeargumenten und mehr findest du in den Qiskit Serverless-Anleitungen.
Die Eingabe für das Pattern ist ein Paar von Datenpunkten, x1 und x2. Die Ausgabe ist die Fidelität zwischen den beiden Punkten. Dieser Wert wird verwendet, um den Kernmatrixeintrag für diese beiden Datenpunkte zu befüllen.
serverless = QiskitServerless()
kernel_entry_pattern = QiskitFunction(
title="generate-kernel-entry",
entrypoint="generate_kernel_entry.py",
working_dir="./source/",
)
serverless.upload(kernel_entry_pattern)
Das Qiskit-Pattern als verwalteten Dienst ausführen
Sobald wir das Pattern in die Cloud hochgeladen haben, können wir es einfach über den IBMServerlessProvider-Client ausführen. Der Einfachheit halber verwenden wir in der Cloud-Umgebung einen exakten Quantensimulator, sodass die berechnete Fidelität exakt ist.
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]}")
Tutorial-Umfrage
Nimm dir bitte einen Moment Zeit für diese kurze Umfrage, um Feedback zu diesem Tutorial zu geben. Deine Rückmeldungen helfen uns dabei, unsere Inhalte und die Nutzererfahrung zu verbessern.