Langkau ke kandungan utama

Kernel Kuantum

Pengenalan kepada kernel kuantum

"Kaedah kernel kuantum" merujuk kepada mana-mana kaedah yang menggunakan komputer kuantum untuk menganggar kernel. Dalam konteks ini, "kernel" akan merujuk kepada matriks kernel atau entri individu di dalamnya. Ingat bahawa pemetaan ciri Φ(x)\Phi(\vec{x}) adalah pemetaan dari xRd\vec{x}\in \mathbb{R}^d ke Φ(x)Rd,\Phi(\vec{x})\in \mathbb{R}^{d'}, di mana biasanya d>dd'>d dan di mana matlamat pemetaan ini adalah untuk menjadikan kategori data boleh dipisahkan oleh hiperplan. Fungsi kernel mengambil vektor dalam ruang yang telah dipetakan ciri sebagai hujah dan mengembalikan hasil darab dalaman mereka, iaitu K:Rd×RdRK:\mathbb{R}^d\times\mathbb{R}^d\rightarrow \mathbb{R} dengan K(x,y)=Φ(x)Φ(y)K(x,y) = \langle \Phi(x)|\Phi(y)\rangle. Secara klasik, kita berminat dengan pemetaan ciri di mana fungsi kernel mudah untuk dinilai. Ini sering bermakna mencari fungsi kernel di mana hasil darab dalaman dalam ruang yang telah dipetakan ciri boleh ditulis dalam sebutan vektor data asal, tanpa perlu membina Φ(x)\Phi(x) dan Φ(y)\Phi(y) sama sekali. Dalam kaedah kernel kuantum, pemetaan ciri dilakukan oleh litar kuantum, dan kernel dianggar menggunakan pengukuran pada litar tersebut dan kebarangkalian pengukuran relatif.

Dalam pelajaran ini kita akan mengkaji kedalaman litar pengekodan pra-kod yang menggunakan pengelitan yang ketara dan membandingkannya dengan kedalaman litar yang kita kod sendiri. Ini bukan untuk menyokong satu kaedah berbanding kaedah lain. Anda mungkin mendapati bahawa litar pra-kod terlalu dalam, dan bahawa pengelitan dalam litar yang dibina sendiri tidak mencukupi untuk berguna. Sekali lagi, ini hanya ditunjukkan untuk membolehkan anda meneroka.

Sebelum melalui anggaran matriks kernel secara terperinci, mari kita gariskan aliran kerja menggunakan bahasa corak Qiskit.

Langkah 1: Petakan input klasik kepada masalah kuantum

  • Input: Set data latihan
  • Output: Litar abstrak untuk mengira entri matriks kernel

Diberi set data, titik permulaan ialah mengekodkan data ke dalam litar kuantum. Dengan kata lain, kita perlu memetakan data kita ke dalam ruang Hilbert keadaan komputer kuantum kita. Kita melakukan ini dengan membina litar bergantung data. Terdapat banyak cara melakukan ini, dan pelajaran sebelumnya menggariskan beberapa pilihan. Anda boleh membina litar sendiri untuk mengekodkan data anda, atau anda boleh menggunakan peta ciri pra-siap seperti zz_feature_map. Dalam pelajaran ini, kita akan melakukan kedua-duanya.

Perhatikan bahawa untuk mengira satu elemen matriks kernel, kita ingin mengekodkan dua titik berbeza, supaya kita dapat menganggar hasil darab dalaman mereka. Aliran kerja kernel kuantum penuh tentu melibatkan banyak hasil darab dalaman sedemikian antara vektor data yang dipetakan, serta kaedah pembelajaran mesin klasik. Tetapi langkah teras yang diulang adalah anggaran satu elemen matriks kernel tunggal. Untuk ini kita memilih litar kuantum bergantung data dan memetakan dua vektor data ke dalam ruang ciri.

Classical_Review_background_kernel_circuit

Untuk tugas menghasilkan matriks kernel, kita amat berminat dengan kebarangkalian mengukur keadaan 0N|0\rangle^{\otimes N}, di mana semua NN qubit berada dalam keadaan 0|0\rangle. Untuk melihat ini, pertimbangkan bahawa litar yang bertanggungjawab untuk pengekodan dan pemetaan satu vektor data xi\vec{x}_i boleh ditulis sebagai Φ(xi)\Phi(\vec{x}_i), dan yang bertanggungjawab untuk mengekodkan dan memetakan xj\vec{x}_j ialah Φ(xj)\Phi(\vec{x}_j), dan namakan keadaan yang dipetakan

ψ(xi)=Φ(xi)0N|\psi(\vec{x}_i)\rangle = \Phi(\vec{x}_i)|0\rangle^{\otimes N} ψ(xj)=Φ(xj)0N.|\psi(\vec{x}_j)\rangle = \Phi(\vec{x}_j)|0\rangle^{\otimes N}.

Keadaan-keadaan ini adalah pemetaan data ke dimensi yang lebih tinggi, jadi entri kernel yang kita inginkan ialah hasil darab dalaman

ψ(xj)ψ(xi)=0NΦ(xj)Φ(xi)0N.\langle\psi(\vec{x}_j)|\psi(\vec{x}_i)\rangle = \langle 0 |^{\otimes N}\Phi^\dagger(\vec{x}_j)\Phi(\vec{x}_i)|0\rangle^{\otimes N}.

Jika kita beroperasi ke atas keadaan awal lalai 0N|0\rangle^{\otimes N} dengan kedua-dua litar Φ(xj)\Phi^\dagger(\vec{x}_j) dan Φ(xi)\Phi(\vec{x}_i), kebarangkalian kemudian mengukur keadaan 0N|0\rangle^{\otimes N} ialah

P0=0NΦ(xj)Φ(xi)0N2.P_0 = |\langle0|^{\otimes N}\Phi^\dagger(\vec{x}_j)\Phi(\vec{x}_i)|0\rangle^{\otimes N}|^2.

Ini adalah tepat nilai yang kita inginkan (sehingga 2||^2). Lapisan pengukuran litar kita akan mengembalikan kebarangkalian pengukuran (atau "kuasi-kebarangkalian" yang dipanggil, jika kaedah pengurangan ralat tertentu digunakan). Kebarangkalian yang diminati ialah keadaan sifar, 0N|0\rangle^{\otimes N}.

Langkah 2: Optimumkan masalah untuk pelaksanaan kuantum

  • Input: Litar abstrak, tidak dioptimumkan untuk Backend tertentu
  • Output: Litar sasaran dan boleh cerap, dioptimumkan untuk QPU yang dipilih

Dalam langkah ini, kita akan menggunakan fungsi generate_preset_pass_manager dari Qiskit untuk menentukan rutin pengoptimuman bagi litar kita berkenaan dengan komputer kuantum sebenar yang kita rancang untuk menjalankan eksperimen. Kita tetapkan optimization_level=3, bermakna kita akan menggunakan pengurus laluan pratetap yang memberikan tahap pengoptimuman tertinggi. Dalam konteks ini, "pengoptimuman" merujuk kepada mengoptimumkan pelaksanaan litar pada komputer kuantum sebenar. Ini termasuk pertimbangan seperti memilih qubit fizikal yang sepadan dengan qubit dalam litar kuantum abstrak yang akan meminimumkan kedalaman get, atau memilih qubit fizikal dengan kadar ralat terendah yang tersedia. Ini tidak berkaitan secara langsung dengan pengoptimuman masalah pembelajaran mesin (seperti dalam pengoptimum klasik seperti COBYLA).

Bergantung kepada cara anda melaksanakan langkah 2, anda mungkin perlu mengoptimumkan litar lebih dari sekali, kerana setiap pasangan titik yang terlibat dalam elemen matriks menghasilkan litar berbeza yang perlu diukur.

Langkah 3: Laksanakan menggunakan Qiskit Runtime Primitives

  • Input: Litar sasaran
  • Output: Taburan kebarangkalian

Gunakan primitif Sampler dari Qiskit Runtime untuk membina semula taburan kebarangkalian keadaan yang dihasilkan dari pensampelan litar. Perhatikan bahawa anda mungkin melihat ini dirujuk sebagai "taburan kuasi-kebarangkalian", satu istilah yang terpakai apabila hingar menjadi isu dan apabila langkah tambahan diperkenalkan, seperti dalam pengurangan ralat. Dalam kes sedemikian, jumlah semua kebarangkalian mungkin tidak tepat sama dengan 1; oleh itu "kuasi-kebarangkalian".

Langkah 4: Pasca-proses, kembalikan keputusan dalam format klasik

  • Input: Taburan kebarangkalian
  • Output: Satu elemen matriks kernel tunggal, atau matriks kernel jika berulang

Kirakan kebarangkalian mengukur 0N|0\rangle^{\otimes N} pada litar kuantum, dan isikan matriks kernel pada kedudukan yang sepadan dengan dua vektor data yang digunakan. Untuk melengkapkan keseluruhan matriks kernel, kita perlu menjalankan eksperimen kuantum untuk setiap entri. Setelah kita mempunyai matriks kernel, kita boleh menggunakannya dalam banyak algoritma pembelajaran mesin klasik yang menerima kernel pra-dikira. Contohnya: qml_svc = SVC(kernel="precomputed"). Kita kemudian boleh menggunakan aliran kerja klasik untuk menggunakan model kita pada data ujian kita, dan mendapatkan skor ketepatan. Bergantung kepada kepuasan kita dengan skor ketepatan, kita mungkin perlu menyemak semula aspek pengiraan kita, seperti peta ciri kita.

Garis besar pelajaran

Dalam pelajaran ini kita akan menjalankan langkah-langkah ini dengan beberapa cara untuk memanfaatkan sepenuhnya masa anda pada komputer kuantum sebenar. Kita akan menggunakan kaedah kernel kuantum untuk

  • Satu entri matriks kernel tunggal untuk data dengan ciri yang agak sedikit, menggunakan Backend sebenar, supaya kita boleh dengan mudah mengikuti apa yang berlaku pada setiap langkah.
  • Keseluruhan set data dengan ciri yang agak sedikit, menggunakan Backend simulasi, supaya kita dapat melihat bagaimana aliran kerja kuantum berhubung dengan kaedah pembelajaran mesin klasik
  • Satu entri matriks kernel tunggal untuk data dengan banyak ciri, menggunakan komputer kuantum IBM® sebenar. Kita tidak akan menganggar keseluruhan matriks kernel untuk set data besar, untuk menghormati masa pada komputer kuantum IBM®.
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy pandas qiskit qiskit-ibm-runtime scikit-learn
# If you have not already, install scikit learn
#!pip install scikit-learn

Entri matriks kernel tunggal

Langkah 1: Petakan input klasik kepada masalah kuantum

Pertama-tama mari kita pertimbangkan set data dengan hanya beberapa ciri, katakan 10. Set data boleh sebesar yang anda suka, kerana kita mengira elemen matriks kernel satu per satu. Kita memerlukan sekurang-kurangnya dua titik, jadi kita akan bermula dengan itu (dalam contoh seterusnya, kita akan mengimport set data penuh). Mari import beberapa pakej yang diperlukan:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# Two mock data points, including category labels, as in training
small_data = [
[-0.194, 0.114, -0.006, 0.301, -0.359, -0.088, -0.156, 0.342, -0.016, 0.143, 1],
[-0.1, 0.002, 0.244, 0.127, -0.064, -0.086, 0.072, 0.043, -0.053, 0.02, -1],
]

# Data points with labels removed, for inner product
train_data = [small_data[0][:-1], small_data[1][:-1]]

Kita boleh cuba menggunakan z_feature_map.

# from qiskit.circuit.library import zz_feature_map
# fm = zz_feature_map(feature_dimension=np.shape(train_data)[1], entanglement='linear', reps=1)

from qiskit.circuit.library import z_feature_map

fm = z_feature_map(feature_dimension=np.shape(train_data)[1])

unitary1 = fm.assign_parameters(train_data[0])
unitary2 = fm.assign_parameters(train_data[1])

Dua unitari di atas tepat sepadan dengan U1U_1 dan U2U_2 yang diterangkan dalam pengenalan. Kita boleh menggabungkannya menggunakan unitary_overlap. Seperti biasa, kita ingin memantau kedalaman litar kita.

from qiskit.circuit.library import unitary_overlap

overlap_circ = unitary_overlap(unitary1, unitary2)
overlap_circ.measure_all()

print("circuit depth = ", overlap_circ.decompose().depth())
overlap_circ.decompose().draw("mpl", scale=0.6, style="iqp")
circuit depth =  9

Output of the previous code cell

Langkah 2: Optimumkan masalah untuk pelaksanaan kuantum

Kita mulakan dengan memilih Backend yang paling kurang sibuk, kemudian mengoptimumkan litar kita untuk dijalankan pada Backend tersebut.

# Import needed packages
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService

# Get the least busy backend
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=fm.num_qubits
)
print(backend)
<IBMBackend('ibm_brisbane')>
# Apply level 3 optimization to our overlap circuit
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
overlap_ibm = pm.run(overlap_circ)

Untuk litar yang rumit, langkah ini akan meningkatkan kedalaman litar secara ketara apabila dipetakan ke get asli untuk komputer kuantum sebenar, dan maklumat mungkin perlu dipindahkan dari qubit ke qubit. Dalam kes mudah ini, kedalaman hampir tidak terjejas langsung.

print("circuit depth = ", overlap_ibm.decompose().depth())
overlap_ibm.decompose().depth(lambda instr: len(instr.qubits) > 1)
circuit depth =  10
1

Langkah 3: Laksanakan menggunakan Qiskit Runtime Primitives

Sintaks untuk menjalankan pada simulator diperlihatkan dalam komen di bawah. Untuk set data ini, dengan bilangan ciri yang kecil, menjalankan pada simulator masih merupakan pilihan. Untuk pengiraan utiliti berskala, simulasi biasanya tidak boleh dilakukan. Simulator hanya patut digunakan untuk menyahpepijat kod berskala kecil.

# Run this for a simulator
# from qiskit.primitives import StatevectorSampler

# from qiskit_ibm_runtime import Options, Session, Sampler

# num_shots = 10000

# Evaluate the problem using state vector-based primitives from Qiskit
# sampler = StatevectorSampler()
# results = sampler.run([overlap_circ], shots=num_shots).result()
# .get_counts() returns counts associated with a state labeled by bit results such as |001101...01>.
# counts_bit = results[0].data.meas.get_counts()
# .get_int_counts returns the same counts, but labeled by integer equivalent of the above bit string.
# counts = results[0].data.meas.get_int_counts()
# Benchmarked on an Eagle processor, 7-11-24, took 4 sec.

# Import our runtime primitive
from qiskit_ibm_runtime import Session, SamplerV2 as Sampler

num_shots = 10000

# Use sampler and get the counts

sampler = Sampler(mode=backend)
results = sampler.run([overlap_ibm], shots=num_shots).result()
# .get_counts() returns counts associated with a state labeled by bit results such as |001101...01>.
counts_bit = results[0].data.meas.get_counts()
# .get_int_counts returns the same counts, but labeled by integer equivalent of the above bit string.
counts = results[0].data.meas.get_int_counts()

Langkah 4: Pasca-proses, kembalikan keputusan dalam format klasik

Seperti yang diterangkan dalam pengenalan, pengukuran yang paling berguna di sini ialah kebarangkalian mengukur keadaan sifar 00000|00000\rangle.

counts.get(0, 0.0) / num_shots
0.6525

Inilah hasil yang kita inginkan: anggaran hasil darab dalaman (sehingga mod kuasa dua) vektor yang sepadan dengan dua titik data. Jika kita ingin melihat taburan penuh kebarangkalian pengukuran (atau kuasi-kebarangkalian), kita boleh berbuat demikian menggunakan fungsi plot_distribution seperti yang ditunjukkan di bawah. Seseorang dapat melihat bahawa untuk bilangan qubit yang besar, gambar seperti ini dengan cepat menjadi tidak mudah difahami.

from qiskit.visualization import plot_distribution

plot_distribution(counts_bit)

Output of the previous code cell

Sebagai alternatif, seseorang mungkin mentakrifkan visualisasi seperti di bawah untuk melihat hanya 10 pengukuran yang paling mungkin. Ini mungkin penting untuk penyelesaian masalah atau cuba mendapatkan lebih banyak intuisi tentang data. Tetapi kebarangkalian pengukuran keadaan sifar adalah elemen matriks kernel kita.

def visualize_counts(probs, num_qubits):
"""Visualize the outputs from the Qiskit Sampler primitive."""
zero_prob = probs.get(0, 0.0)
top_10 = dict(sorted(probs.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]))
xvals, yvals = list(zip(*by_key.items()))
xvals = [bin(xval)[2:].zfill(num_qubits) for xval in xvals]
plt.bar(xvals, yvals)
plt.xticks(rotation=75)
plt.title("Results of sampling")
plt.xlabel("Measured bitstring")
plt.ylabel("Counts")
plt.show()

visualize_counts(counts, overlap_circ.num_qubits)

Output of the previous code cell

Dari maklumat tentang hanya satu hasil darab dalaman antara dua titik data dalam ruang ciri berdimensi tinggi, yang boleh kita katakan ialah pertindanan mereka agak besar berbanding pertindanan maksimum (yang akan menjadi 1.0). Ini boleh menjadi petunjuk bahawa dua titik data ini entah bagaimana serupa sifatnya dan akan dikategorikan dalam kelas yang sama. Atau ia boleh menjadi petunjuk bahawa peta ciri kita tidak berkesan dalam memetakan ke dalam ruang di mana data serupa mempunyai pertindanan yang kuat dan data berbeza mempunyai pertindanan yang kecil. Untuk mengetahui yang mana satu benar, kita mesti menggunakan peta ciri kita ke seluruh set data dan melihat sama ada matriks kernel yang terhasil boleh dimanipulasi untuk memisahkan kelas dengan ketepatan yang tinggi.

Patut diperhatikan bahawa kita menggunakan z_feature_map yang menghasilkan kedalaman dua-qubit yang telah ditranspilasi rendah (kedalaman 1, sebenarnya). Jika litar anda menjadi terlalu dalam, ia pasti akan menghasilkan banyak hingar, dan ini akan menjadikan kebarangkalian mengukur keadaan sifar sangat rendah, walaupun peta ciri anda sangat sepadan dengan data anda. Sebagai contoh, pengulangan proses di atas menggunakan zz_feature_map dan , entanglement='linear', reps=1 menghasilkan dist.get(0,0.0) = 0.0015 menggunakan titik data yang sama. Ini disebabkan kedalaman litar dan kedalaman dua-qubit yang jauh lebih besar dari zz_feature_map. Gambar di bawah menunjukkan taburan kebarangkalian untuk pengiraan tersebut.

Bad results from a zz feature map.

Adalah berbaloi untuk bermain-main dengan beberapa titik data dari kategori yang sama untuk melihat betapa rendah kedalaman anda perlu supaya mendapat keputusan yang baik. Berikut adalah nasihat kasar yang pasti mempunyai pengecualian. Secara umumnya, kedalaman dua-qubit yang telah ditranspilasi sebanyak 10 atau kurang tidak sepatutnya menjadi masalah. Kedalaman dua-qubit yang telah ditranspilasi sebanyak 50-60 adalah mutakhir dan akan memerlukan pengurangan ralat canggih antara alat lain. Di antaranya, keputusan anda mungkin berbeza-beza bergantung kepada kesamaan data, ekspresi peta ciri, lebar litar, dan faktor lain. Biasanya langkah pasca-pemprosesan juga akan merangkumi proses pembelajaran mesin klasik. Dalam bahagian seterusnya kita akan memperluaskan proses ini ke keseluruhan set data, dan menunjukkan aliran kerja pembelajaran mesin klasik.

Uji kefahaman anda

Baca soalan-soalan di bawah, fikirkan jawapan anda, kemudian klik segitiga untuk mendedahkan penyelesaian.

Dalam litar kuantum 10-qubit, secara umum, berapa banyak keadaan berbeza yang mungkin diukur?

Jawapan:

2102^{10} atau 1024.

Andaikan seseorang yang baru berkenalan dengan pengkomputeran kuantum cuba menggunakan litar kuantum yang mempunyai kedalaman dua-qubit yang sangat tinggi, dan mereka tidak menggunakan pengurangan ralat. Andaikan selanjutnya bahawa ini menghasilkan kadar ralat 10% pada setiap qubit. Jika elemen matriks kernel sebenar (bebas ralat) yang sepadan dengan litar ini sangat besar, katakan 1.0, berapakah kebarangkalian mengukur semua 10 qubit berada dalam keadaan dengan setiap qubit |0>?

Jawapan:

Kebarangkalian setiap qubit ditemui dengan betul dalam keadaan |0> ialah 0.90. Kebarangkalian semua 10 qubit ditemui dalam keadaan yang betul ialah 0.90100.90^{10} atau kira-kira 35%.

Terangkan dengan kata-kata anda sendiri mengapa amat penting untuk memantau kedalaman litar. Ini benar secara umum, tetapi terangkan dalam konteks anggaran kernel kuantum.

Jawapan:

Dalam aliran kerja QKE ini, anggaran kita berdasarkan pengukuran keadaan sifar, bermakna keadaan di mana setiap qubit ditemui dalam keadaan 0|0\rangle. Litar yang sangat dalam akan memperkenalkan kadar ralat yang tinggi. Apabila kadar ralat itu digabungkan merentasi banyak qubit, ini akan mengurangkan kebarangkalian mengukur keadaan sifar secara ketara.

Matriks kernel penuh

Dalam bahagian ini, kita akan memperluaskan proses di atas kepada pengelasan binari set data penuh. Ini akan memperkenalkan dua komponen penting: (1) kita kini boleh melaksanakan pembelajaran mesin klasik dalam pasca-pemprosesan, dan (2) kita boleh mendapatkan skor ketepatan untuk latihan kita.

Langkah 1: Petakan input klasik kepada masalah kuantum

Sekarang kita akan mengimport set data sedia ada untuk pengelasan kita. Set data ini terdiri daripada 128 baris (titik data) dan 14 ciri pada setiap titik. Terdapat elemen ke-15 yang menunjukkan kategori binari setiap titik (±1\pm 1). Set data diimport di bawah, atau anda boleh mengakses set data dan melihat strukturnya di sini.

Kita akan menggunakan 90 titik data pertama untuk latihan, dan 30 titik seterusnya untuk ujian.

!wget https://raw.githubusercontent.com/qiskit-community/prototype-quantum-kernel-training/main/data/dataset_graph7.csv

df = pd.read_csv("dataset_graph7.csv", sep=",", header=None)

# Prepare training data

train_size = 90
X_train = df.values[0:train_size, :-1]
train_labels = df.values[0:train_size, -1]

# Prepare testing data
test_size = 30
X_test = df.values[train_size : train_size + test_size, :-1]
test_labels = df.values[train_size : train_size + test_size, -1]
--2024-07-11 23:05:22--  https://raw.githubusercontent.com/qiskit-community/prototype-quantum-kernel-training/main/data/dataset_graph7.csv
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.111.133, 185.199.109.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 49405 (48K) [text/plain]
Saving to: ‘dataset_graph7.csv.15’

dataset_graph7.csv. 100%[===================>] 48.25K --.-KB/s in 0.02s

2024-07-11 23:05:23 (2.11 MB/s) - ‘dataset_graph7.csv.15’ saved [49405/49405]

Kita akan bersedia untuk menyimpan pelbagai output dengan membina matriks kernel dan matriks ujian berdimensi yang sesuai.

# Empty kernel matrix
num_samples = np.shape(X_train)[0]
kernel_matrix = np.full((num_samples, num_samples), np.nan)
test_matrix = np.full((test_size, num_samples), np.nan)

Sekarang kita cipta peta ciri untuk pengekodan dan pemetaan data klasik kita dalam litar kuantum. Kita bebas untuk membina peta ciri sendiri atau menggunakan yang pra-dibuat. Jangan ragu untuk mengubah suai peta ciri di bawah, atau beralih kembali ke ZFeatureMap. Tetapi sentiasa perhatikan kedalaman litar. Ingat bahawa dalam contoh 6-qubit sebelumnya, kedalaman litar yang ditranspilasi tidak dapat diterima apabila menggunakan zz_feature_map. Memandangkan skala dan kerumitan litar meningkat, kedalaman boleh meningkat dengan pesat ke titik di mana hingar mengatasi keputusan kita. Bila sahaja anda mengetahui sesuatu tentang struktur data anda yang mungkin memaklumkan struktur peta ciri yang paling berguna, adalah disyorkan untuk mencipta peta ciri tersuai sendiri yang memanfaatkan pengetahuan tersebut.

from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit

# Prepare feature map for computing overlap
num_features = np.shape(X_train)[1]
num_qubits = int(num_features / 2)

# To use a custom feature map use the lines below.
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)

Langkah 2 dan 3: Optimumkan masalah dan laksanakan menggunakan primitif

Kita akan membina litar pertindanan, dan jika kita menjalankan pada komputer kuantum sebenar dalam contoh ini, kita akan mengoptimumkannya untuk pelaksanaan seperti sebelumnya. Tetapi dalam kes ini, kita ingin melangkah ke semua titik data dan mengira matriks kernel penuh. Untuk setiap pasangan vektor data xi\vec{x}_i dan xj\vec{x}_j, kita mencipta litar pertindanan yang berbeza. Oleh itu kita mesti mengoptimumkan litar kita untuk setiap pasangan titik data. Jadi langkah 2 dan 3 akan dilakukan bersama dalam pelbagai lelaran.

Sel kod di bawah melakukan proses yang sama persis seperti sebelumnya untuk satu pasangan titik data tunggal. Kali ini ia hanya dilaksanakan di dalam dua gelung for, dan terdapat baris tambahan di penghujungnya kernel_matrix[x_1,x_2] = ... untuk menyimpan keputusan setiap pengiraan. Perhatikan bahawa kita telah memanfaatkan simetri matriks kernel untuk mengurangkan bilangan pengiraan sebanyak 1/2. Kita juga hanya menetapkan elemen pepenjuru kepada 1, seperti sepatutnya tanpa hingar. Bergantung kepada pelaksanaan dan ketepatan yang diperlukan, anda juga boleh menggunakan elemen pepenjuru untuk menganggar hingar atau mengetahui tentangnya untuk tujuan pengurangan ralat.

Setelah matriks kernel diisi sepenuhnya, kita ulangi proses untuk data ujian dan isi test_matrix. Ini sebenarnya juga merupakan matriks kernel; kita hanya memberikannya nama yang berbeza untuk membezakan keduanya.

# To use a simulator
from qiskit.primitives import StatevectorSampler

# Remember to insert your token in the QiskitRuntimeService constructor to use real quantum computers
# service = QiskitRuntimeService()
# backend = service.least_busy(
# operational=True, simulator=False, min_num_qubits=fm.num_qubits
# )

num_shots = 10000

# Evaluate the problem using state vector-based primitives from Qiskit.
sampler = StatevectorSampler()

for x1 in range(0, train_size):
for x2 in range(x1 + 1, train_size):
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 = unitary_overlap(unitary1, unitary2)
overlap_circ.measure_all()

# These lines run the qiskit sampler primitive.
counts = (
sampler.run([overlap_circ], shots=num_shots)
.result()[0]
.data.meas.get_int_counts()
)

# Assign the probability of the 0 state to the kernel matrix, and the transposed element (since this is an inner product)
kernel_matrix[x1, x2] = counts.get(0, 0.0) / num_shots
kernel_matrix[x2, x1] = counts.get(0, 0.0) / num_shots
# Fill in on-diagonal elements with 1, again, since this is an inner-product corresponding to probability (or alter the code to check these entries and verify they yield 1)
kernel_matrix[x1, x1] = 1

print("training done")

# Similar process to above, but for testing data.
for x1 in range(0, test_size):
for x2 in range(0, train_size):
unitary1 = fm.assign_parameters(list(X_test[x1]) + [np.pi / 2])
unitary2 = fm.assign_parameters(list(X_train[x2]) + [np.pi / 2])

# Create the overlap circuit
overlap_circ = unitary_overlap(unitary1, unitary2)
overlap_circ.measure_all()

counts = (
sampler.run([overlap_circ], shots=num_shots)
.result()[0]
.data.meas.get_int_counts()
)

test_matrix[x1, x2] = counts.get(0, 0.0) / num_shots

print("test matrix done")
training done
test matrix done

Langkah 4: Pasca-proses, kembalikan keputusan dalam format klasik

Setelah kita mempunyai matriks kernel dan test_matrix berformat serupa dari kaedah kernel kuantum, kita boleh menggunakan algoritma pembelajaran mesin klasik untuk membuat ramalan tentang data ujian kita dan menyemak ketepatannya. Kita akan mulakan dengan mengimport sklearn.svc Scikit-Learn, satu pengelas vektor sokongan (SVC). Kita mesti menyatakan bahawa kita mahu SVC menggunakan kernel pra-dikira kita menggunakan kernel = precomputed.

# import a support vector classifier from a classical ML package.
from sklearn.svm import SVC

# Specify that you want to use a pre-computed kernel matrix
qml_svc = SVC(kernel="precomputed")

Menggunakan SVC.fit, kita kini boleh memasukkan matriks kernel dan label latihan untuk mendapatkan padanan. SVC.score kemudian akan menilai data ujian kita terhadap padanan tersebut menggunakan test_matrix kita, dan mengembalikan ketepatan kita.

# Feed in the pre-computed matrix and the labels of the training data. The classical algorithm gives you a fit.
qml_svc.fit(kernel_matrix, train_labels)

# Now use the .score to test your data, using the matrix of test data, and test labels as your inputs.
qml_score_precomputed_kernel = qml_svc.score(test_matrix, test_labels)
print(f"Precomputed kernel classification test score: {qml_score_precomputed_kernel}")
Precomputed kernel classification test score: 1.0

Kita dapati bahawa ketepatan model terlatih kita ialah 100%. Ini sangat baik, dan menunjukkan bahawa QKE boleh berfungsi. Tetapi itu sangat berbeza dari kelebihan kuantum. Kernel klasik kemungkinan besar juga akan dapat menyelesaikan masalah pengelasan ini dengan ketepatan 100%. Masih banyak kerja yang perlu dilakukan untuk mencirikan jenis data dan hubungan data yang berbeza untuk melihat di mana kernel kuantum akan paling berguna dalam era utiliti semasa. Kami serahkan kepada pelajar untuk mengubah suai bahagian-bahagian aliran kerja ini dan mengkaji keberkesanan pelbagai peta ciri kuantum. Berikut adalah beberapa perkara yang perlu dipertimbangkan:

  • Seberapa kukuh ketepatannya? Adakah ia kekal untuk pelbagai jenis data atau hanya data latihan tertentu ini?
  • Struktur apa dalam data anda yang membuatkan anda menyangka bahawa peta ciri kuantum berguna?
  • Bagaimana ketepatan terjejas oleh meningkat/mengurangkan jumlah data latihan?
  • Peta ciri apa yang boleh anda gunakan dan bagaimana keputusan berbeza-beza dengan peta ciri?
  • Bagaimana ketepatan dan masa jalan terjejas oleh meningkatkan bilangan ciri?
  • Corak mana, jika ada, yang anda jangkakan akan kekal pada komputer kuantum sebenar?

Penskalaan kepada lebih banyak ciri dan qubit

Dalam bahagian ini, kita akan mengulangi pengiraan satu elemen matriks tunggal, tetapi untuk bilangan ciri yang jauh lebih besar, merintis jalan menuju utiliti berskala. Sekatan kepada satu elemen matriks dilakukan supaya proses dapat ditunjukkan tanpa menggunakan terlalu banyak masa yang diperuntukkan pada komputer kuantum.

Langkah 1: Petakan input klasik kepada masalah kuantum

Kita akan mengandaikan titik permulaan set data di mana setiap titik data mempunyai 42 ciri. Seperti dalam contoh pertama, kita akan mengira satu elemen matriks kernel tunggal, memerlukan dua titik data. Dua titik di bawah mempunyai 42 ciri dan satu pemboleh ubah kategori tunggal (±1\pm 1).

# Two mock data points, including category labels, as in training

large_data = [
[
-0.028,
-1.49,
-1.698,
0.107,
-1.536,
-1.538,
-1.356,
-1.514,
-0.109,
-1.8,
-0.122,
-1.651,
-1.955,
-0.123,
-1.732,
0.091,
-0.048,
-0.128,
-0.026,
0.082,
-1.263,
0.065,
0.004,
-0.055,
-0.08,
-0.173,
-1.734,
-0.39,
-1.451,
0.078,
-1.578,
-0.025,
-0.184,
-0.119,
-1.336,
0.055,
-0.204,
-1.578,
0.132,
-0.121,
-1.599,
-0.187,
-1,
],
[
-1.414,
-1.439,
-1.606,
0.246,
-1.673,
0.002,
-1.317,
-1.262,
-0.178,
-1.814,
0.013,
-1.619,
-1.86,
-0.25,
-0.212,
-0.214,
-0.033,
0.071,
-0.11,
-1.607,
0.441,
-0.143,
-0.009,
-1.655,
-1.579,
0.381,
-1.86,
-0.079,
-0.088,
-0.058,
-1.481,
-0.064,
-0.065,
-1.507,
0.177,
-0.131,
-0.153,
0.07,
-1.627,
0.593,
-1.547,
-0.16,
-1,
],
]
train_data = [large_data[0][:-1], large_data[1][:-1]]

Ingat bahawa zz_feature_map menghasilkan litar yang agak dalam dalam kes ciri yang agak sedikit (14 ciri). Apabila kita meningkatkan bilangan ciri, kita perlu memantau kedalaman litar dengan teliti. Untuk menggambarkan ini, kita akan mencuba menggunakan zz_feature_map terlebih dahulu dan menyemak kedalaman litar yang terhasil.

from qiskit.circuit.library import zz_feature_map

fm = zz_feature_map(
feature_dimension=np.shape(train_data)[1], entanglement="linear", reps=1
)

unitary1 = fm.assign_parameters(train_data[0])
unitary2 = fm.assign_parameters(train_data[1])
from qiskit.circuit.library import unitary_overlap

overlap_circ = unitary_overlap(unitary1, unitary2)
overlap_circ.measure_all()

print("circuit depth = ", overlap_circ.decompose(reps=2).depth())
print(
"two-qubit depth",
overlap_circ.decompose().depth(lambda instr: len(instr.qubits) > 1),
)
# overlap_circ.draw("mpl", scale=0.6, style="iqp")
circuit depth =  251
two-qubit depth 165

Seperti yang diterangkan sebelumnya, menentukan dengan tepat seberapa dalam itu terlalu dalam adalah bernuansa. Tetapi kedalaman dua-qubit melebihi 100, bahkan sebelum transpilasi, adalah mustahil. Inilah sebabnya peta ciri tersuai telah ditekankan sepanjang pelajaran ini. Jika anda mengetahui sesuatu tentang struktur keseluruhan set data anda, anda patut mereka bentuk peta pengelitan dengan mengambil kira struktur tersebut. Di sini, kerana kita hanya mengira hasil darab dalaman antara dua titik data sedemikian, kita mengutamakan kedalaman litar yang rendah berbanding pertimbangan terperinci tentang struktur data.

from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit

# Prepare feature map for computing overlap

entangler_map = [
[3, 4],
[2, 5],
[1, 4],
[2, 3],
[4, 6],
[7, 9],
[10, 11],
[9, 12],
[8, 11],
[9, 10],
[11, 13],
[14, 16],
[17, 18],
[16, 19],
[15, 18],
[16, 17],
[18, 20],
]
# Use the entangler map above to build a feature map

num_features = np.shape(train_data)[1]
num_qubits = int(num_features / 2)

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)
from qiskit.circuit.library import unitary_overlap

# Assign features of each data point to a unitary, an instance of the general feature map.

unitary1 = fm.assign_parameters(list(train_data[0]) + [np.pi / 2])
unitary2 = fm.assign_parameters(list(train_data[1]) + [np.pi / 2])

# Create the overlap circuit

overlap_circ = unitary_overlap(unitary1, unitary2)
overlap_circ.measure_all()

Kita tidak perlu menyemak kedalaman buat masa ini, kerana yang benar-benar penting ialah kedalaman dua-qubit yang telah ditranspilasi.

Langkah 2: Optimumkan masalah untuk pelaksanaan kuantum

Kita mulakan dengan memilih Backend yang paling kurang sibuk, kemudian mengoptimumkan litar kita untuk dijalankan pada Backend tersebut.

# Import needed packages
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService

# Get the least busy backend
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=fm.num_qubits
)
print(backend)
<IBMBackend('ibm_brisbane')>

Pada kerja berskala kecil, pengurus laluan pratetap sering mengembalikan litar yang sama dengan kedalaman yang sama, secara boleh dipercayai. Tetapi dalam litar yang sangat besar dan kompleks, pengurus laluan boleh mengembalikan litar yang ditranspilasi berbeza setiap kali ia dijalankan. Ini kerana ia menggunakan heuristik, dan kerana litar yang sangat besar akan mempunyai landskap pengoptimuman yang rumit. Sering kali berguna untuk mentranspilasi beberapa kali dan mengambil litar yang paling cetek. Ini hanya memperkenalkan overhead klasik dan boleh meningkatkan keputusan dari komputer kuantum secara ketara.

Di sini, kita mentranspilasi litar pertindanan unitari 20 kali, dan melihat kedalaman litar yang diperoleh.

# Apply level 3 optimization to our overlap circuit
transpiled_qcs = []
transpiled_depths = []
transpiled_twoqubit_depths = []
for i in range(1, 20):
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
overlap_ibm = pm.run(overlap_circ)
transpiled_qcs.append(overlap_ibm)
transpiled_depths.append(overlap_ibm.decompose().depth())
transpiled_twoqubit_depths.append(
overlap_ibm.decompose().depth(lambda instr: len(instr.qubits) > 1)
)

print("circuit depth = ", overlap_ibm.decompose().depth())
circuit depth =  61
print(transpiled_depths)
print(transpiled_twoqubit_depths)
[61, 60, 60, 69, 60, 60, 60, 65, 60, 60, 69, 61, 77, 77, 65, 60, 60, 77, 61]
[13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13]

Di sini anda dapat melihat terdapat beberapa variasi dalam kedalaman get total dengan pelbagai laluan transpilasi. Litar kita belum cukup dalam/lebar untuk melihat variasi dalam kedalaman dua-qubit yang ditranspilasi. Kita akan menggunakan transpiled_qcs[1], yang mempunyai kedalaman 60, sedikit lebih rendah daripada kedalaman litar terdalam yang diperoleh, iaitu 77.

overlap_ibm = transpiled_qcs[1]

Langkah 3: Laksanakan menggunakan Qiskit Runtime Primitives

Apabila kita semakin mendekati utiliti, simulator tidak akan berguna. Hanya sintaks untuk komputer kuantum sebenar yang ditunjukkan di sini.

# Run on ibm_osaka, 7-12-24, required 22 sec.

# Import our runtime primitive
from qiskit_ibm_runtime import SamplerV2 as Sampler

# Open a Runtime session:
session = Session(backend=backend)
num_shots = 10000
# Use sampler and get the counts

sampler = Sampler(mode=session)
options = sampler.options
options.dynamical_decoupling.enable = True
options.twirling.enable_gates = True
counts = (
sampler.run([overlap_ibm], shots=num_shots).result()[0].data.meas.get_int_counts()
)

# Close session after done
session.close()

Langkah 4: Pasca-proses, kembalikan keputusan dalam format klasik

Seperti yang diterangkan dalam pengenalan, pengukuran yang paling berguna di sini ialah kebarangkalian mengukur keadaan sifar 00000|00000\rangle.

counts.get(0, 0.0) / num_shots
0.0138

Proses untuk elemen matriks kernel tunggal ini boleh diulang antara pasangan data lain dalam set anda untuk mendapatkan matriks kernel penuh. Dimensi matriks kernel ditentukan oleh bilangan titik dalam data latihan anda, bukan bilangan ciri. Jadi kos pengiraan untuk memanipulasi matriks kernel menjadi model ramalan tidak berskala seperti bilangan ciri atau qubit. Walaupun untuk set data yang agak kecil dengan bilangan ciri yang besar, data tetap perlu dipadankan dengan peta ciri yang menghasilkan pengelasan yang berkesan.

Penskalaan dan kerja masa hadapan

Kaedah kernel memerlukan kita mengukur 0|0\rangle dengan seakurat mungkin. Tetapi ralat get dan ralat bacaan bermakna terdapat kebarangkalian bukan sifar pp bahawa mana-mana qubit yang diberikan akan tersilap diukur dalam keadaan 1|1\rangle. Walaupun dengan penyederhanaan berlebihan bahawa kebarangkalian 0|0\rangle sepatutnya 100%100\%, untuk banyak ciri yang dikodkan pada, katakan, NN bit, kebarangkalian mengukur semua bit dengan betul sebagai 0|0\rangle berkurang menjadi (1p)N(1-p)^N. Apabila NN menjadi besar, kaedah ini semakin kurang boleh dipercayai. Mengatasi kesukaran ini dan menskalakan anggaran kernel kepada lebih banyak ciri adalah kawasan penyelidikan semasa. Untuk mengetahui lebih lanjut tentang isu ini, lihat karya oleh Thanasilp, Wang, Cerezo, dan Holmes. Kami mengesyorkan anda meneroka apa yang boleh dilakukan dengan komputer kuantum semasa, dan juga menantikan apa yang akan mungkin dalam era pembetulan ralat.

Ulasan

Mengira kernel kuantum melibatkan

  • mengira entri matriks kernel, menggunakan pasangan titik data latihan
  • mengekodkan data dan memetakannya melalui pemetaan ciri
  • mengoptimumkan litar anda untuk dijalankan pada komputer kuantum sebenar / Backend

Kernel kuantum kemudian boleh digunakan dalam algoritma pembelajaran mesin klasik, seperti dalam pelajaran ini.

Beberapa perkara utama yang perlu diingat semasa menggunakan kernel kuantum termasuk:

  • Adakah set data kemungkinan mendapat manfaat daripada kaedah kernel kuantum?
  • Cuba peta ciri dan skim pengelitan yang berbeza.
  • Adakah kedalaman litar boleh diterima?
  • Cuba jalankan pengurus laluan beberapa kali dan gunakan litar berkedalaman terkecil yang boleh anda peroleh.

Kaedah kernel kuantum berpotensi menjadi alat yang berkuasa apabila terdapat padanan yang sesuai antara set data dengan ciri yang mesra kuantum, dan peta ciri kuantum yang sesuai. Untuk lebih memahami di mana kernel kuantum kemungkinan berguna, kami mengesyorkan membaca Liu, Arunachalam & Temme (2021).