Saltar al contenido principal

Entrenamiento de kernel cuántico

Estimación de uso: menos de un minuto en un procesador Eagle r3 (NOTA: Esto es solo una estimación. Su tiempo de ejecución puede variar.)

Contexto

Este tutorial muestra cómo construir un Qiskit pattern para evaluar entradas en una matriz de kernel cuántico utilizada para clasificación binaria. Para obtener más información sobre los Qiskit patterns y cómo se puede utilizar Qiskit Serverless para desplegarlos en la nube para una ejecución gestionada, visite nuestra página de documentación en IBM Quantum® Platform.

Requisitos

Antes de comenzar este tutorial, asegúrese de tener instalado lo siguiente:

  • Qiskit SDK v1.0 o posterior, con soporte de visualización
  • Qiskit Runtime v0.22 o posterior (pip install qiskit-ibm-runtime)

Configuración

# 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

Paso 1: Mapear las entradas clásicas a un problema cuántico

  • Entrada: Conjunto de datos de entrenamiento.
  • Salida: Circuito abstracto para calcular una entrada de la matriz de kernel.

Cree el circuito cuántico utilizado para evaluar una entrada en la matriz de kernel. Utilizamos los datos de entrada para determinar los ángulos de rotación de las compuertas parametrizadas del circuito. Usaremos las muestras de datos x1=14 y x2=19.

Nota: El conjunto de datos utilizado en este tutorial se puede descargar aquí.

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

Salida de la celda de código anterior

Paso 2: Optimizar el problema para la ejecución en hardware cuántico

  • Entrada: Circuito abstracto, no optimizado para un backend en particular
  • Salida: Circuito y observable objetivo, optimizados para la QPU seleccionada

Utilice la función generate_preset_pass_manager de Qiskit para especificar una rutina de optimización para nuestro circuito con respecto a la QPU en la que planeamos ejecutar el experimento. Establecemos optimization_level=3, lo que significa que utilizaremos el gestor de pases predefinido que proporciona el nivel más alto de optimización.

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

Salida de la celda de código anterior

Paso 3: Ejecutar utilizando primitivas de Qiskit

  • Entrada: Circuito objetivo
  • Salida: Distribución de cuasi-probabilidad

Utilice la primitiva Sampler de Qiskit Runtime para reconstruir una distribución de cuasi-probabilidad de los estados obtenidos al muestrear el circuito. Para la tarea de generar una matriz de kernel, estamos particularmente interesados en la probabilidad de medir el estado |0>.

Para esta demostración, ejecutaremos en una QPU con las primitivas de qiskit-ibm-runtime. Para ejecutar con las primitivas basadas en Statevector de qiskit, reemplace el bloque de código que utiliza las primitivas de Qiskit IBM® Runtime con el bloque comentado.

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)

Salida de la celda de código anterior

Paso 4: Post-procesar y devolver el resultado en el formato clásico deseado

  • Entrada: Distribución de probabilidad
  • Salida: Un solo elemento de la matriz de kernel

Calcule la probabilidad de medir |0> en el circuito de solapamiento y complete la matriz de kernel en la posición correspondiente a las muestras representadas por este circuito de solapamiento en particular (fila 15, columna 20). En esta visualización, el rojo más oscuro indica fidelidades más cercanas a 1.0. Para completar toda la matriz de kernel, necesitamos ejecutar un experimento cuántico para cada entrada.

# 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

Desplegar el patrón de Qiskit en la nube

Para hacer esto, mueve el código fuente anterior a un archivo, ./source/generate_kernel_entry.py, encapsula el código en un script que reciba entradas y devuelva la solución final, y finalmente cárgalo a un clúster remoto usando la clase QiskitFunction de Qiskit Serverless. Para obtener orientación sobre cómo especificar dependencias externas, pasar argumentos de entrada y más, consulta las guías de Qiskit Serverless.

La entrada al patrón es un par de muestras de datos, x1 y x2. La salida es la fidelidad entre las dos muestras. Este valor se utilizará para completar la entrada de la matriz de kernel correspondiente a estas dos muestras.

serverless = QiskitServerless()

kernel_entry_pattern = QiskitFunction(
title="generate-kernel-entry",
entrypoint="generate_kernel_entry.py",
working_dir="./source/",
)

serverless.upload(kernel_entry_pattern)

Ejecutar el patrón de Qiskit como un servicio gestionado

Una vez que hemos cargado el patrón en la nube, podemos ejecutarlo fácilmente utilizando el cliente IBMServerlessProvider. Por simplicidad, utilizaremos un simulador cuántico exacto en el entorno de la nube, por lo que la fidelidad que calculemos será exacta.

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

Encuesta del tutorial

Por favor, realice esta breve encuesta para proporcionar comentarios sobre este tutorial. Sus opiniones nos ayudarán a mejorar nuestro contenido y la experiencia de usuario.

Enlace a la encuesta

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.