Treinamento de kernel quântico
Estimativa de uso: menos de um minuto em um processador Eagle r3 (NOTA: Esta é apenas uma estimativa. Seu tempo de execução pode variar.)
Contexto
Este tutorial mostra como construir um padrão Qiskit para avaliar entradas em uma matriz de kernel quântico usada para classificação binária. Para mais informações sobre padrões Qiskit e como o Qiskit Serverless pode ser usado para implantá-los na nuvem para execução gerenciada, visite nossa página de documentação na IBM Quantum® Platform.
Requisitos
Antes de iniciar este tutorial, certifique-se de ter o seguinte instalado:
- Qiskit SDK v1.0 ou posterior, com suporte para visualização
- Qiskit Runtime v0.22 ou posterior (
pip install qiskit-ibm-runtime)
Configuração
# 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
Etapa 1: Mapear entradas clássicas para um problema quântico
- Entrada: Conjunto de dados de treinamento.
- Saída: Circuito abstrato para calcular uma entrada da matriz de kernel.
Crie o circuito quântico usado para avaliar uma entrada na matriz de kernel. Usamos os dados de entrada para determinar os ângulos de rotação para as portas parametrizadas do circuito. Usaremos as amostras de dados x1=14 e x2=19.
Nota: O conjunto de dados usado neste tutorial pode ser baixado aqui.
# 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")
Etapa 2: Otimizar o problema para execução em hardware quântico
- Entrada: Circuito abstrato, não otimizado para um backend específico
- Saída: Circuito alvo e observável, otimizados para a QPU selecionada
Use a função generate_preset_pass_manager do Qiskit para especificar uma rotina de otimização para nosso circuito em relação à QPU na qual planejamos executar o experimento. Definimos optimization_level=3, o que significa que usaremos o gerenciador de passes predefinido que fornece o mais alto nível de otimização.
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")
Etapa 3: Executar usando primitivas Qiskit
- Entrada: Circuito alvo
- Saída: Distribuição de quasi-probabilidade
Use a primitiva Sampler do Qiskit Runtime para reconstruir uma distribuição de quasi-probabilidade dos estados produzidos pela amostragem do circuito. Para a tarefa de gerar uma matriz de kernel, estamos particularmente interessados na probabilidade de medir o estado |0>.
Para esta demonstração, executaremos em uma QPU com primitivas qiskit-ibm-runtime. Para executar em primitivas baseadas em statevector do qiskit, substitua o bloco de código que usa primitivas do Qiskit IBM® Runtime pelo bloco 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)
Etapa 4: Pós-processar e retornar o resultado no formato clássico desejado
- Entrada: Distribuição de probabilidade
- Saída: Um único elemento da matriz de kernel
Calcule a probabilidade de medir |0> no circuito de sobreposição e preencha a matriz de kernel na posição correspondente às amostras representadas por este circuito de sobreposição específico (linha 15, coluna 20). Nesta visualização, vermelho mais escuro indica fidelidades mais próximas de 1.0. Para preencher toda a matriz de kernel, precisamos executar um experimento quâ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
Implantar o padrão Qiskit na nuvem
Para fazer isso, mova o código-fonte acima para um arquivo, ./source/generate_kernel_entry.py, envolva o código em um script que recebe entradas e retorna a solução final e, por fim, faça upload para um cluster remoto usando a classe QiskitFunction do Qiskit Serverless. Para orientações sobre especificação de dependências externas, passagem de argumentos de entrada e muito mais, consulte os guias do Qiskit Serverless.
A entrada para o Padrão é um par de amostras de dados, x1 e x2. A saída é a fidelidade entre as duas amostras. Este valor será usado para preencher a entrada da matriz de kernel correspondente a essas duas amostras.
serverless = QiskitServerless()
kernel_entry_pattern = QiskitFunction(
title="generate-kernel-entry",
entrypoint="generate_kernel_entry.py",
working_dir="./source/",
)
serverless.upload(kernel_entry_pattern)
Executar o padrão Qiskit como um serviço gerenciado
Depois de fazer upload do padrão para a nuvem, podemos executá-lo facilmente usando o cliente IBMServerlessProvider. Para simplificar, usaremos um simulador quântico exato no ambiente de nuvem, então a fidelidade que calculamos será exata.
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]}")
Pesquisa do tutorial
Por favor, responda a esta breve pesquisa para fornecer feedback sobre este tutorial. Suas percepções nos ajudarão a melhorar nossas ofertas de conteúdo e experiência do usuário.