Zum Hauptinhalt springen

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[Files: 0  Bytes: 0  [0 B/s] Re]87[https://raw.githubusercontent.]87Saving 'dataset_graph7.csv.1'
87dataset_graph7.csv.1 100% [=============================>] 20.25K --.-KB/s87HTTP response 200 [https://raw.githubusercontent.com/qiskit-community/prototype-quantum-kernel-training/main/data/dataset_graph7.csv]
87dataset_graph7.csv.1 100% [=============================>] 20.25K --.-KB/s87[Files: 1 Bytes: 20.25K [93.33]8

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")

Output of the previous code cell

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")

Output of the previous code cell

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)

Output of the previous code cell

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

kernel_matrix.png

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.

Link zur Umfrage