Trening kwantowego jądra (Quantum kernel training)
Szacowany czas użycia: poniżej jednej minuty na procesorze Eagle r3 (UWAGA: to tylko szacunek. Twój czas wykonania może się różnić.)
Tło
Ten samouczek pokazuje, jak zbudować wzorzec Qiskit pattern do obliczania wpisów macierzy kwantowego jądra używanej w klasyfikacji binarnej. Aby dowiedzieć się więcej o wzorcach Qiskit patterns i jak Qiskit Serverless może służyć do wdrażania ich w chmurze jako zarządzanego środowiska wykonawczego, odwiedź naszą stronę dokumentacji dotyczącą IBM Quantum® Platform.
Wymagania
Przed rozpoczęciem tego samouczka upewnij się, że masz zainstalowane następujące elementy:
- Qiskit SDK v1.0 lub nowszy, z obsługą wizualizacji
- Qiskit Runtime v0.22 lub nowszy (
pip install qiskit-ibm-runtime)
Konfiguracja
# 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
Krok 1: Odwzorowanie klasycznych danych wejściowych na problem kwantowy
- Dane wejściowe: Zbiór danych treningowych.
- Dane wyjściowe: Abstrakcyjny Circuit do obliczania jednego wpisu macierzy jądra.
Utwórz Circuit kwantowy służący do obliczenia jednego wpisu macierzy jądra. Używamy danych wejściowych do określenia kątów obrotu dla sparametryzowanych bramek Circuit. Użyjemy próbek danych x1=14 i x2=19.
Uwaga: Zbiór danych użyty w tym samouczku można pobrać tutaj.
# 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")
Krok 2: Optymalizacja problemu pod kątem sprzętu kwantowego
- Dane wejściowe: Abstrakcyjny Circuit, nieoptymalizowany pod kątem konkretnego Backend
- Dane wyjściowe: Docelowy Circuit i obserwabla, zoptymalizowane pod wybrany QPU
Użyj funkcji generate_preset_pass_manager z Qiskit, aby określić procedurę optymalizacji Circuit w odniesieniu do QPU, na którym planujemy przeprowadzić eksperyment. Ustawiamy optimization_level=3, co oznacza, że użyjemy wstępnie skonfigurowanego menedżera przejść zapewniającego najwyższy poziom optymalizacji.
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")
Krok 3: Wykonanie przy użyciu prymitywów Qiskit
- Dane wejściowe: Docelowy Circuit
- Dane wyjściowe: Rozkład quasi-prawdopodobieństwa
Użyj prymitywu Sampler z Qiskit Runtime, aby zrekonstruować rozkład quasi-prawdopodobieństwa stanów uzyskanych przez próbkowanie Circuit. W zadaniu generowania macierzy jądra szczególnie interesuje nas prawdopodobieństwo zmierzenia stanu |0>.
W tym demo uruchomimy obliczenia na QPU za pomocą prymitywów qiskit-ibm-runtime. Aby uruchomić je na prymitywach opartych na wektorze stanu qiskit, zastąp blok kodu używający prymitywów Qiskit IBM® Runtime zakomentowanym blokiem.
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)
Krok 4: Post-processing i zwrócenie wyniku w żądanym formacie klasycznym
- Dane wejściowe: Rozkład prawdopodobieństwa
- Dane wyjściowe: Pojedynczy element macierzy jądra
Oblicz prawdopodobieństwo zmierzenia |0> na Circuit nakładkowym i wypełnij macierz jądra na pozycji odpowiadającej próbkom reprezentowanym przez ten konkretny Circuit (wiersz 15, kolumna 20). Na tej wizualizacji ciemniejszy czerwony oznacza wierności bliższe 1,0. Aby wypełnić całą macierz jądra, musimy przeprowadzić eksperyment kwantowy dla każdego wpisu.
# 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
Wdrożenie wzorca Qiskit w chmurze
Aby to zrobić, przenieś powyższy kod źródłowy do pliku ./source/generate_kernel_entry.py, opakuj kod w skrypt przyjmujący dane wejściowe i zwracający końcowe rozwiązanie, a następnie prześlij go do zdalnego klastra przy użyciu klasy QiskitFunction z Qiskit Serverless. Wskazówki dotyczące określania zewnętrznych zależności, przekazywania argumentów wejściowych i innych kwestii znajdziesz w przewodnikach Qiskit Serverless.
Dane wejściowe wzorca to para próbek danych, x1 i x2. Dane wyjściowe to wierność między tymi dwiema próbkami. Ta wartość zostanie użyta do wypełnienia wpisu macierzy jądra odpowiadającego tym dwóm próbkom.
serverless = QiskitServerless()
kernel_entry_pattern = QiskitFunction(
title="generate-kernel-entry",
entrypoint="generate_kernel_entry.py",
working_dir="./source/",
)
serverless.upload(kernel_entry_pattern)
Uruchomienie wzorca Qiskit jako usługi zarządzanej
Po przesłaniu wzorca do chmury możemy łatwo go uruchomić za pomocą klienta IBMServerlessProvider. Dla uproszczenia użyjemy w środowisku chmurowym dokładnego symulatora kwantowego, dzięki czemu obliczona wierność będzie dokładna.
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]}")
Ankieta dotycząca samouczka
Wypełnij tę krótką ankietę, aby podzielić się opinią na temat tego samouczka. Twoje spostrzeżenia pomogą nam ulepszyć naszą ofertę treści oraz doświadczenie użytkownika.
Note: This survey is provided by IBM Quantum and relates to the original English content. To give feedback on doQumentation's website, translations, or code execution, please open a GitHub issue.