Lewati ke konten utama

Mitigasi kesalahan readout untuk primitif Sampler menggunakan M3

Estimasi penggunaan: kurang dari satu menit pada prosesor Heron r2 (CATATAN: Ini hanya estimasi. Waktu eksekusi aktual bisa berbeda.)

Latar belakang

Tidak seperti primitif Estimator, primitif Sampler tidak memiliki dukungan bawaan untuk mitigasi kesalahan. Beberapa metode yang didukung oleh Estimator dirancang khusus untuk nilai ekspektasi, sehingga tidak berlaku untuk primitif Sampler. Pengecualiannya adalah mitigasi kesalahan readout, yang merupakan metode yang sangat efektif dan juga berlaku untuk primitif Sampler.

Addon M3 Qiskit mengimplementasikan metode efisien untuk mitigasi kesalahan readout. Tutorial ini menjelaskan cara menggunakan addon M3 Qiskit untuk memitigasi kesalahan readout pada primitif Sampler.

Apa itu kesalahan readout?

Tepat sebelum pengukuran, keadaan register qubit digambarkan oleh superposisi dari basis komputasional, atau oleh matriks densitas. Pengukuran register qubit ke dalam register bit klasik kemudian berlangsung dalam dua langkah. Pertama, pengukuran kuantum yang sebenarnya dilakukan. Ini berarti keadaan register qubit diproyeksikan ke satu basis yang dicirikan oleh string dari 11 dan 00. Langkah kedua adalah membaca string bit yang mencirikan basis ini dan menuliskannya ke memori komputer klasik. Kita menyebut langkah ini readout. Ternyata langkah kedua (readout) menimbulkan lebih banyak kesalahan daripada langkah pertama (proyeksi ke basis). Ini masuk akal jika kamu ingat bahwa readout memerlukan pendeteksian keadaan kuantum mikroskopik dan memperkuatnya ke ranah makroskopik. Resonator readout dipasangkan ke qubit (transmon), sehingga mengalami pergeseran frekuensi yang sangat kecil. Sebuah pulsa gelombang mikro kemudian dipantulkan dari resonator, yang mengalami perubahan kecil pada karakteristiknya. Pulsa yang dipantulkan kemudian diperkuat dan dianalisis. Ini adalah proses yang rumit dan rentan terhadap berbagai kesalahan.

Poin pentingnya adalah bahwa, meskipun pengukuran kuantum dan readout sama-sama rentan terhadap kesalahan, yang terakhir mengalami kesalahan dominan yang disebut kesalahan readout, yang menjadi fokus dalam tutorial ini.

Latar belakang teori

Jika string bit yang disampling (tersimpan di memori klasik) berbeda dari string bit yang mencirikan keadaan kuantum yang diproyeksikan, kita mengatakan telah terjadi kesalahan readout. Kesalahan ini teramati bersifat acak dan tidak berkorelasi dari satu sampel ke sampel berikutnya. Terbukti berguna untuk memodelkan kesalahan readout sebagai saluran klasik yang berisik. Yaitu, untuk setiap pasangan string bit ii dan jj, ada probabilitas tetap bahwa nilai sebenarnya jj akan dibaca secara keliru sebagai ii.

Lebih tepatnya, untuk setiap pasangan string bit (i,j)(i, j), ada probabilitas (kondisional) Mi,j{M}_{i,j} bahwa ii dibaca, dengan syarat nilai sebenarnya adalah j.j. Yaitu,

Mi,j=Pr(readout value is itrue value is j) for i,j(0,...,2n1),(1) {M}_{i,j} = \Pr(\text{readout value is } i | \text{true value is } j) \text{ for } i,j \in (0,...,2^n - 1), \tag{1}

di mana nn adalah jumlah bit dalam register readout. Sebagai gambaran konkret, kita asumsikan ii adalah bilangan bulat desimal yang representasi binernya adalah string bit yang melabeli basis komputasional. Kita menyebut matriks 2n×2n2^n \times 2^n M{M} sebagai matriks penugasan. Untuk nilai sebenarnya jj yang tetap, menjumlahkan probabilitas atas semua hasil berisik ii harus menghasilkan 11. Yaitu

i=02n1Mi,j=1 for all j \sum_{i=0}^{2^n - 1} {M}_{i,j} = 1 \text{ for all } j

Matriks tanpa entri negatif yang memenuhi (1) disebut kiri-stokastik. Matriks kiri-stokastik juga disebut kolom-stokastik karena setiap kolomnya berjumlah 11. Kita secara eksperimental menentukan nilai perkiraan untuk setiap elemen Mi,j{M}_{i,j} dengan berulang kali mempersiapkan setiap basis j|j \rangle dan kemudian menghitung frekuensi kemunculan string bit yang disampling.

Jika sebuah eksperimen melibatkan estimasi distribusi probabilitas atas string bit keluaran melalui pengambilan sampel berulang, maka kita dapat menggunakan M{M} untuk memitigasi kesalahan readout di tingkat distribusi. Langkah pertama adalah mengulangi sirkuit tetap yang diminati berkali-kali, membuat histogram string bit yang disampling. Histogram yang dinormalisasi adalah distribusi probabilitas terukur atas 2n2^n string bit yang mungkin, yang kita notasikan sebagai p~R2n{\tilde{p}} \in \mathbb{R}^{2^n}. Probabilitas (estimasi) p~i{{\tilde{p}}}_i dari pengambilan sampel string bit ii sama dengan jumlah atas semua string bit sebenarnya jj, masing-masing tertimbang oleh probabilitas bahwa string tersebut keliru dianggap sebagai ii. Pernyataan ini dalam bentuk matriks adalah

p~=Mp,,(2) {\tilde{p}} = {M} {\vec{p}}, \tag{2},

di mana p{\vec{p}} adalah distribusi sebenarnya. Dengan kata lain, kesalahan readout berefek mengalikan distribusi ideal atas string bit p{\vec{p}} dengan matriks penugasan M{M} untuk menghasilkan distribusi yang diamati p~{\tilde{p}}. Kita telah mengukur p~{\tilde{p}} dan M{M}, tetapi tidak memiliki akses langsung ke p{\vec{p}}. Pada prinsipnya, kita akan mendapatkan distribusi sebenarnya dari string bit untuk sirkuit kita dengan menyelesaikan persamaan (2) untuk p{\vec{p}} secara numerik.

Sebelum melanjutkan, ada baiknya mencatat beberapa fitur penting dari pendekatan naif ini.

  • Dalam praktiknya, persamaan (2) tidak diselesaikan dengan membalik M{M}. Rutinitas aljabar linear dalam pustaka perangkat lunak menggunakan metode yang lebih stabil, akurat, dan efisien.
  • Ketika mengestimasi M{M}, kita mengasumsikan bahwa hanya kesalahan readout yang terjadi. Khususnya, kita asumsikan tidak ada kesalahan persiapan keadaan dan pengukuran kuantum — atau setidaknya sudah dimitigasi dengan cara lain. Sejauh ini adalah asumsi yang baik, M{M} benar-benar hanya merepresentasikan kesalahan readout. Tetapi ketika kita menggunakan M{M} untuk mengoreksi distribusi terukur atas string bit, kita tidak membuat asumsi seperti itu. Faktanya, kita mengharapkan sirkuit yang menarik memperkenalkan noise, misalnya kesalahan Gate. Distribusi "sebenarnya" masih mencakup efek dari kesalahan apa pun yang tidak dimitigasi.

Metode ini, meskipun berguna dalam beberapa situasi, memiliki beberapa keterbatasan.

Sumber daya ruang dan waktu yang dibutuhkan untuk mengestimasi M{M} tumbuh secara eksponensial terhadap nn:

  • Estimasi M{M} dan p~{\tilde{p}} rentan terhadap kesalahan statistik akibat pengambilan sampel yang terbatas. Noise ini dapat diperkecil semau kita dengan biaya lebih banyak shots (hingga skala waktu pergeseran parameter perangkat keras yang mengakibatkan kesalahan sistematis pada M{M}). Namun, jika tidak ada asumsi yang dibuat tentang string bit yang diamati saat melakukan mitigasi, jumlah shots yang diperlukan untuk mengestimasi M{M} tumbuh setidaknya secara eksponensial terhadap nn.
  • M{M} adalah matriks 2n×2n2^n \times 2^n. Ketika n>10n>10, jumlah memori yang diperlukan untuk menyimpan M{M} lebih besar dari memori yang tersedia di laptop bertenaga tinggi.

Keterbatasan lainnya adalah:

  • Distribusi yang dipulihkan p{\vec{p}} mungkin memiliki satu atau lebih probabilitas negatif (meskipun totalnya tetap satu). Salah satu solusinya adalah meminimalkan Mpp~2||{M} {\vec{p}} - {\tilde{p}}||^2 dengan syarat bahwa setiap entri dalam p{\vec{p}} tidak negatif. Namun, waktu eksekusi metode seperti itu jauh lebih lama daripada menyelesaikan persamaan (2) secara langsung.
  • Prosedur mitigasi ini bekerja pada tingkat distribusi probabilitas atas string bit. Khususnya, ia tidak dapat mengoreksi kesalahan pada string bit individual yang diamati.

Addon M3 Qiskit: Skala ke string bit yang lebih panjang

Menyelesaikan persamaan (2) menggunakan rutinitas aljabar linear numerik standar dibatasi hingga string bit sepanjang sekitar 10 bit. M3, bagaimanapun, dapat menangani string bit yang jauh lebih panjang. Dua properti kunci M3 yang memungkinkan ini adalah:

  • Korelasi kesalahan readout orde tiga dan lebih tinggi di antara kumpulan bit diasumsikan dapat diabaikan dan diabaikan. Pada prinsipnya, dengan biaya lebih banyak shots, seseorang bisa mengestimasi korelasi yang lebih tinggi juga.
  • Daripada membangun M{M} secara eksplisit, kita menggunakan matriks efektif yang jauh lebih kecil yang mencatat probabilitas hanya untuk string bit yang dikumpulkan saat membangun p~{\tilde{p}}.

Secara garis besar, prosedurnya bekerja sebagai berikut.

Pertama, kita membangun blok bangunan yang dapat digunakan untuk membangun deskripsi efektif yang disederhanakan dari M{M}. Kemudian, kita berulang kali menjalankan sirkuit yang diminati dan mengumpulkan string bit yang kita gunakan untuk membangun baik p~{\tilde{p}} maupun, dengan bantuan blok bangunan, M{M} yang efektif.

Lebih tepatnya,

  • Matriks penugasan satu qubit diestimasi untuk setiap qubit. Untuk melakukannya, kita berulang kali mempersiapkan register qubit dalam keadaan semua-nol 0...0|0 ... 0 \rangle dan kemudian dalam keadaan semua-satu 1...1|1 ... 1 \rangle, dan mencatat probabilitas untuk setiap qubit yang dibaca secara keliru.

  • Korelasi orde tiga dan lebih tinggi diasumsikan dapat diabaikan.

    Sebaliknya kita membangun sejumlah nn matriks penugasan satu qubit 2×22 \times 2, dan sejumlah n(n1)/2n(n-1)/2 matriks penugasan dua qubit 4×44 \times 4. Matriks satu- dan dua-qubit ini disimpan untuk digunakan kemudian.

  • Setelah berulang kali mengambil sampel dari sirkuit untuk membangun p~{\tilde{p}}, kita membangun aproksimasi efektif dari M{M} menggunakan hanya string bit yang disampling saat membangun p~{\tilde{p}}. Matriks efektif ini dibangun menggunakan matriks satu- dan dua-qubit yang dijelaskan pada poin sebelumnya. Dimensi linear matriks ini paling banyak sebesar jumlah shots yang digunakan dalam membangun p~{\tilde{p}}, yang jauh lebih kecil dari dimensi 2n2^n dari matriks penugasan penuh M{M}.

Untuk detail teknis tentang M3, kamu dapat merujuk ke Scalable Mitigation of Measurement Errors on Quantum Computers.

Penerapan M3 pada algoritma kuantum

Kita akan menerapkan mitigasi readout M3 pada masalah hidden shift. Masalah hidden shift, dan masalah yang berkaitan erat seperti hidden subgroup problem, awalnya dikonseptualisasikan dalam pengaturan toleran-kesalahan (lebih tepatnya, sebelum QPU toleran-kesalahan terbukti mungkin!). Tetapi mereka juga dipelajari dengan prosesor yang tersedia saat ini. Contoh percepatan eksponensial algoritmik yang diperoleh untuk varian masalah hidden shift pada QPU IBM® 127-qubit dapat ditemukan di makalah ini (versi arXiv).

Dalam pembahasan berikut, semua aritmetika bersifat Boolean. Yaitu, untuk a,bZ2={0,1}a, b \in \mathbb{Z}_2 = \{0, 1\}, penjumlahan a+ba + b adalah fungsi XOR logis. Selanjutnya, perkalian a×ba \times b (atau aba b) adalah fungsi AND logis. Untuk x,y{0,1}nx, y \in \{0, 1\}^n, x+yx + y didefinisikan dengan penerapan XOR bitwise. Produk titik :Z2nZ2\cdot: {\mathbb{Z}_2^n} \rightarrow \mathbb{Z}_2 didefinisikan oleh xy=ixiyix \cdot y = \sum_i x_i y_i.

Operator Hadamard dan transformasi Fourier

Dalam mengimplementasikan algoritma kuantum, sangat umum menggunakan operator Hadamard sebagai transformasi Fourier. Basis komputasional kadang-kadang disebut keadaan klasik. Mereka berada dalam hubungan satu-ke-satu dengan string bit klasik. Operator Hadamard nn-qubit pada keadaan klasik dapat dipandang sebagai transformasi Fourier pada hiperkubus Boolean:

Hn=12nx,yZ2n(1)xyyx.H^{\otimes n} = \frac{1}{\sqrt{2^n}} \sum_{x,y \in {\mathbb{Z}_2^n}} (-1)^{x \cdot y} {|{y}\rangle}{\langle{x}|}.

Pertimbangkan keadaan s{|{s}\rangle} yang bersesuaian dengan string bit tetap ss. Menerapkan HnH^{\otimes n}, dan menggunakan xs=δx,s{\langle {x}|{s}\rangle} = \delta_{x,s}, kita melihat bahwa transformasi Fourier dari s{|{s}\rangle} dapat ditulis sebagai

Hns=12nyZ2n(1)syy. H^{\otimes n} {|{s}\rangle} = \frac{1}{\sqrt{2^n}} \sum_{y \in {\mathbb{Z}_2^n}} (-1)^{s \cdot y} {|{y}\rangle}.

Hadamard merupakan inversnya sendiri, yaitu, HnHn=(HH)n=InH^{\otimes n} H^{\otimes n} = (H H)^{\otimes n} = I^{\otimes n}. Dengan demikian, transformasi Fourier invers adalah operator yang sama, HnH^{\otimes n}. Secara eksplisit, kita memiliki,

s=HnHns=Hn12nyZ2n(1)syy. {|{s}\rangle} = H^{\otimes n} H^{\otimes n} {|{s}\rangle} = H^{\otimes n} \frac{1}{\sqrt{2^n}} \sum_{y \in {\mathbb{Z}_2^n}} (-1)^{s \cdot y} {|{y}\rangle}.

Masalah hidden shift

Kita mempertimbangkan contoh sederhana dari masalah hidden shift. Masalahnya adalah mengidentifikasi pergeseran konstan pada input ke sebuah fungsi. Fungsi yang kita pertimbangkan adalah produk titik. Ini adalah anggota paling sederhana dari kelas fungsi besar yang mengakui percepatan kuantum untuk masalah hidden shift melalui teknik yang mirip dengan yang disajikan di bawah.

Misalkan x,yZ2mx,y \in {\mathbb{Z}_2^m} adalah string bit dengan panjang mm. Kita mendefinisikan f:Z2m×Z2m{1,1}{f}: {\mathbb{Z}_2^m} \times {\mathbb{Z}_2^m} \rightarrow \{-1,1\} oleh

f(x,y)=(1)xy. {f}(x, y) = (-1)^{x \cdot y}.

Misalkan a,bZ2ma,b \in {\mathbb{Z}_2^m} adalah string bit tetap dengan panjang mm. Kita selanjutnya mendefinisikan g:Z2m×Z2m{1,1}g: {\mathbb{Z}_2^m} \times {\mathbb{Z}_2^m} \rightarrow \{-1,1\} oleh

g(x,y)=f(x+a,y+b)=(1)(x+a)(y+b), g(x, y) = {f}(x+a, y+b) = (-1)^{(x+a) \cdot (y+b)},

di mana aa dan bb adalah parameter (tersembunyi). Kita diberikan dua kotak hitam, satu mengimplementasikan ff, dan satu lagi gg. Kita asumsikan bahwa kita mengetahui bahwa mereka menghitung fungsi yang didefinisikan di atas, kecuali kita tidak tahu aa maupun bb. Permainannya adalah menentukan string bit tersembunyi (pergeseran) aa dan bb dengan membuat kueri ke ff dan gg. Jelas bahwa jika kita memainkan permainan ini secara klasik, kita membutuhkan O(2m)O(2m) kueri untuk menentukan aa dan bb. Misalnya, kita dapat mengkueri gg dengan semua pasangan string sehingga satu elemen pasangan adalah semua nol, dan elemen lainnya memiliki tepat satu elemen bernilai 11. Pada setiap kueri, kita mempelajari satu elemen dari aa atau bb. Namun, kita akan melihat bahwa, jika kotak hitam diimplementasikan sebagai Circuit kuantum, kita dapat menentukan aa dan bb dengan satu kueri ke masing-masing ff dan gg.

Dalam konteks kompleksitas algoritmik, kotak hitam disebut oracle. Selain bersifat buram, oracle memiliki sifat bahwa ia mengonsumsi input dan menghasilkan output secara instan, tanpa menambah apa pun pada anggaran kompleksitas algoritma tempat ia ditanamkan. Faktanya, dalam kasus yang ada, oracle yang mengimplementasikan ff dan gg akan terbukti efisien.

Circuit kuantum untuk ff dan gg

Kita memerlukan bahan-bahan berikut untuk mengimplementasikan ff dan gg sebagai Circuit kuantum.

Untuk keadaan klasik satu qubit x1,y1{|{x_1}\rangle}, {|{y_1}\rangle}, dengan x1,y1Z2x_1,y_1 \in \mathbb{Z}_2, Gate controlled-ZZ CZ{CZ} dapat ditulis sebagai

CZx1y1x1=(1)x1y1x1x1y1.{CZ} {|{x_1}\rangle}{|{y_1}\rangle}{x_1} = (-1)^{x_1 y_1} {|{x_1}\rangle}{x_1}{|{y_1}\rangle}.

Kita akan beroperasi dengan mm Gate CZ, satu pada (x1,y1)(x_1, y_1), dan satu pada (x2,y2)(x_2, y_2), dan seterusnya, hingga (xm,ym)(x_m, y_m). Kita menyebut operator ini CZx,y{CZ}_{x,y}.

Uf=CZx,yU_f = {CZ}_{x,y} adalah versi kuantum dari f=f(x,y){f} = {f}(x,y):

Ufxy=CZx,yxy=(1)xyxy.%\CZ_{x,y} {|#1\rangle}{z} = U_f {|{x}\rangle}{|{y}\rangle} = {CZ}_{x,y} {|{x}\rangle}{|{y}\rangle} = (-1)^{x \cdot y} {|{x}\rangle}{|{y}\rangle}.

Kita juga perlu mengimplementasikan pergeseran string bit. Kita notasikan operator pada register xx sebagai Xa1XamX^{a_1}\cdots X^{a_m} dengan XaX_a dan demikian pula pada register yy dengan Xb=Xb1XbmX_b = X^{b_1}\cdots X^{b_m}. Operator-operator ini menerapkan XX di mana pun sebuah bit bernilai 11, dan identitas II di mana pun nilainya 00. Maka kita punya

XaXbxy=x+ay+b. X_a X_b {|{x}\rangle}{|{y}\rangle} = {|{x+a}\rangle}{|{y+b}\rangle}.

Kotak hitam kedua gg diimplementasikan oleh uniter UgU_g, yang diberikan oleh

Ug=XaXbCZx,yXaXb.%U_g {|{x}\rangle}{|{y}\rangle} = X_aX_b \CZ_{x,y} X_aX_b {|{x}\rangle}{|{y}\rangle}. U_g = X_aX_b {CZ}_{x,y} X_aX_b.

Untuk melihat ini, kita menerapkan operator dari kanan ke kiri ke keadaan xy{|{x}\rangle}{|{y}\rangle}. Pertama

XaXbxy=x+ay+b. X_a X_b {|{x}\rangle}{|{y}\rangle} = {|{x+a}\rangle}{|{y+b}\rangle}.

Kemudian,

CZx,yx+ay+b=(1)(x+a)(y+b)x+ay+b. {CZ}_{x,y} {|{x+a}\rangle}{|{y+b}\rangle} = (-1)^{(x+a)\cdot (y+b)} {|{x+a}\rangle}{|{y+b}\rangle}.

Akhirnya,

XaXb(1)(x+a)(y+b)x+ay+b=(1)(x+a)(y+b)xy, X^a X^b (-1)^{(x+a)\cdot (y+b)} {|{x+a}\rangle}{|{y+b}\rangle} = (-1)^{(x+a)\cdot (y+b)} {|{x}\rangle}{|{y}\rangle},

yang memang merupakan versi kuantum dari f(x+a,y+b)f(x+a, y+b).

Algoritma hidden shift

Sekarang kita menyatukan semua bagian untuk menyelesaikan masalah hidden shift. Kita mulai dengan menerapkan Hadamard ke register yang diinisialisasi ke keadaan semua-nol.

H2m=HmHm0m0m=122mx,yZ2m(1)xyxy.H^{\otimes 2m} = H^{\otimes m} \otimes H^{\otimes m} {{|{0}\rangle}^{\otimes m}}{{|{0}\rangle}^{\otimes m}} = \frac{1}{\sqrt{2^{2m}}} \sum_{x, y \in {\mathbb{Z}_2^m}} (-1)^{x \cdot y} {|{x}\rangle}{|{y}\rangle}.

Selanjutnya, kita mengkueri oracle gg untuk tiba di

UgH2m0m0m=122mx,yZ2m(1)(x+a)(y+b)xyU_g H^{\otimes 2m} {{|{0}\rangle}^{\otimes m}}{{|{0}\rangle}^{\otimes m}} = \frac{1}{\sqrt{2^{2m}}} \sum_{x, y \in {\mathbb{Z}_2^m}} (-1)^{(x+a) \cdot (y+b)} {|{x}\rangle}{|{y}\rangle} 122mx,yZ2m(1)xy+xb+yaxy.\approx \frac{1}{\sqrt{2^{2m}}} \sum_{x, y \in {\mathbb{Z}_2^m}} (-1)^{x \cdot y + x \cdot b + y \cdot a} {|{x}\rangle}{|{y}\rangle}.

Pada baris terakhir, kita menghilangkan faktor fase global konstan (1)ab(-1)^{a \cdot b}, dan menandai kesetaraan hingga fase dengan \approx. Selanjutnya, menerapkan oracle ff memperkenalkan faktor lain (1)xy(-1)^{x \cdot y}, membatalkan yang sudah ada. Kita kemudian memiliki:

UfUgH2m0m0m122mx,yZ2m(1)xb+yaxy.U_f U_g H^{\otimes 2m} {{|{0}\rangle}^{\otimes m}}{{|{0}\rangle}^{\otimes m}} \approx \frac{1}{\sqrt{2^{2m}}} \sum_{x, y \in {\mathbb{Z}_2^m}} (-1)^{x \cdot b + y \cdot a} {|{x}\rangle}{|{y}\rangle}.

Langkah terakhir adalah menerapkan transformasi Fourier invers, H2m=HmHmH^{\otimes 2m} = H^{\otimes m} \otimes H^{\otimes m}, menghasilkan

H2mUfUgH2m0m0mba.H^{\otimes 2m} U_f U_g H^{\otimes 2m} {{|{0}\rangle}^{\otimes m}}{{|{0}\rangle}^{\otimes m}} \approx {|{b}\rangle}{|{a}\rangle}.

Circuit telah selesai. Tanpa adanya noise, pengambilan sampel dari register kuantum akan menghasilkan string bit b,ab, a dengan probabilitas 11.

Produk dalam Boolean adalah contoh dari apa yang disebut fungsi bent. Kita tidak akan mendefinisikan fungsi bent di sini tetapi hanya mencatat bahwa mereka "sangat tahan terhadap serangan yang berusaha mengeksploitasi ketergantungan keluaran pada beberapa subruang linear dari masukan." Kutipan ini berasal dari artikel Quantum algorithms for highly non-linear Boolean functions, yang memberikan algoritma hidden shift yang efisien untuk beberapa kelas fungsi bent. Algoritma dalam tutorial ini muncul di Bagian 3.1 dari artikel tersebut.

Dalam kasus yang lebih umum, Circuit untuk menemukan hidden shift sZns \in \mathbb{Z}^n adalah

HnUf~HnUgHn0n=s. H^{\otimes n} U_{\tilde{f}} H^{\otimes n} U_g H^{\otimes n} {|{0}\rangle}^{\otimes n} = {|{s}\rangle}.

Dalam kasus umum, ff dan gg adalah fungsi dari satu variabel. Contoh produk titik kita memiliki bentuk ini jika kita biarkan f(x,y)f(z)f(x, y) \to f(z), dengan zz sama dengan penggabungan xx dan yy, dan ss sama dengan penggabungan aa dan bb. Kasus umum memerlukan tepat dua oracle: Satu oracle untuk gg dan satu untuk f~\tilde{f}, di mana yang terakhir adalah fungsi yang dikenal sebagai dual dari fungsi bent ff. Fungsi produk titik memiliki sifat self-dual f~=f\tilde{f}=f.

Dalam Circuit kita untuk hidden shift pada produk titik, kita menghilangkan lapisan tengah Hadamard yang muncul dalam Circuit untuk kasus umum. Meskipun dalam kasus umum lapisan ini diperlukan, kita menghemat sedikit kedalaman dengan menghilangkannya, dengan mengorbankan sedikit pasca-pemrosesan karena keluarannya adalah ba{|{b}\rangle}{|{a}\rangle} bukan yang diinginkan ab{|{a}\rangle}{|{b}\rangle}.

Persyaratan

Sebelum memulai tutorial ini, pastikan kamu telah menginstal hal-hal berikut:

  • Qiskit SDK v2.1 atau lebih baru, dengan dukungan visualisasi
  • Qiskit Runtime v0.41 atau lebih baru (pip install qiskit-ibm-runtime)
  • Addon M3 Qiskit v3.0 (pip install mthree)

Setup

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib mthree qiskit qiskit-ibm-runtime
from collections.abc import Iterator, Sequence
from random import Random
from qiskit.circuit import (
CircuitInstruction,
QuantumCircuit,
QuantumRegister,
Qubit,
)
from qiskit.circuit.library import CZGate, HGate, XGate
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
import timeit
import matplotlib.pyplot as plt
from qiskit_ibm_runtime import SamplerV2 as Sampler
import mthree

Step 1: Map classical inputs to a quantum problem

Pertama, kita menulis fungsi-fungsi untuk mengimplementasikan masalah hidden shift sebagai QuantumCircuit.

def apply_hadamards(qubits: Sequence[Qubit]) -> Iterator[CircuitInstruction]:
"""Apply a Hadamard gate to every qubit."""
for q in qubits:
yield CircuitInstruction(HGate(), [q], [])

def apply_shift(
qubits: Sequence[Qubit], shift: int
) -> Iterator[CircuitInstruction]:
"""Apply X gates where the bits of the shift are equal to 1."""
for i, q in zip(range(shift.bit_length()), qubits):
if shift >> i & 1:
yield CircuitInstruction(XGate(), [q], [])

def oracle_f(qubits: Sequence[Qubit]) -> Iterator[CircuitInstruction]:
"""Apply the f oracle."""
for i in range(0, len(qubits) - 1, 2):
yield CircuitInstruction(CZGate(), [qubits[i], qubits[i + 1]])

def oracle_g(
qubits: Sequence[Qubit], shift: int
) -> Iterator[CircuitInstruction]:
"""Apply the g oracle."""
yield from apply_shift(qubits, shift)
yield from oracle_f(qubits)
yield from apply_shift(qubits, shift)

def determine_hidden_shift(
qubits: Sequence[Qubit], shift: int
) -> Iterator[CircuitInstruction]:
"""Determine the hidden shift."""
yield from apply_hadamards(qubits)
yield from oracle_g(qubits, shift)
# We omit this layer in exchange for post processing
# yield from apply_hadamards(qubits)
yield from oracle_f(qubits)
yield from apply_hadamards(qubits)

def run_hidden_shift_circuit(n_qubits, rng):
hidden_shift = rng.getrandbits(n_qubits)

qubits = QuantumRegister(n_qubits, name="q")
circuit = QuantumCircuit.from_instructions(
determine_hidden_shift(qubits, hidden_shift), qubits=qubits
)
circuit.measure_all()
# Format the hidden shift as a string.
hidden_shift_string = format(hidden_shift, f"0{n_qubits}b")
return (circuit, hidden_shift, hidden_shift_string)

def display_circuit(circuit):
return circuit.remove_final_measurements(inplace=False).draw(
"mpl", idle_wires=False, scale=0.5, fold=-1
)

Kita akan mulai dengan contoh kecil:

n_qubits = 6
random_seed = 12345
rng = Random(random_seed)
circuit, hidden_shift, hidden_shift_string = run_hidden_shift_circuit(
n_qubits, rng
)

print(f"Hidden shift string {hidden_shift_string}")

display_circuit(circuit)
Hidden shift string 011010

Output of the previous code cell

Step 2: Optimize circuits for quantum hardware execution

job_tags = [
f"shift {hidden_shift_string}",
f"n_qubits {n_qubits}",
f"seed = {random_seed}",
]
job_tags
['shift 011010', 'n_qubits 6', 'seed = 12345']
# Uncomment this to run the circuits on a quantum computer on IBMCloud.
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=100
)

# from qiskit_ibm_runtime.fake_provider import FakeMelbourneV2
# backend = FakeMelbourneV2()
# backend.refresh(service)

print(f"Using backend {backend.name}")

def get_isa_circuit(circuit, backend):
pass_manager = generate_preset_pass_manager(
optimization_level=3, backend=backend, seed_transpiler=1234
)
isa_circuit = pass_manager.run(circuit)
return isa_circuit

isa_circuit = get_isa_circuit(circuit, backend)
display_circuit(isa_circuit)
Using backend ibm_kingston

Output of the previous code cell

Step 3: Execute circuits using Qiskit primitives

# submit job for solving the hidden shift problem using the Sampler primitive
NUM_SHOTS = 50_000

def run_sampler(backend, isa_circuit, num_shots):
sampler = Sampler(mode=backend)
sampler.options.environment.job_tags
pubs = [(isa_circuit, None, NUM_SHOTS)]
job = sampler.run(pubs)
return job

def setup_mthree_mitigation(isa_circuit, backend):
# retrieve the final qubit mapping so mthree knows which qubits to calibrate
qubit_mapping = mthree.utils.final_measurement_mapping(isa_circuit)

# submit jobs for readout error calibration
mit = mthree.M3Mitigation(backend)
mit.cals_from_system(qubit_mapping, rep_delay=None)

return mit, qubit_mapping
job = run_sampler(backend, isa_circuit, NUM_SHOTS)
mit, qubit_mapping = setup_mthree_mitigation(isa_circuit, backend)

Step 4: Post-process and return results in classical format

Dalam pembahasan teoritis di atas, kita sudah menentukan bahwa untuk input abab, kita mengharapkan output baba. Komplikasi tambahan adalah, agar memiliki Circuit (sebelum transpilasi) yang lebih sederhana, kita menyisipkan gate CZ yang diperlukan di antara pasangan Qubit yang bertetangga. Hal ini setara dengan menyilangkan bitstring aa dan bb menjadi a1b1a2b2a1 b1 a2 b2 \ldots. String output baba akan disilangkan dengan cara serupa: b1a1b2a2b1 a1 b2 a2 \ldots. Fungsi unscramble di bawah mengubah string output dari b1a1b2a2b1 a1 b2 a2 \ldots menjadi a1b1a2b2a1 b1 a2 b2 \ldots agar string input dan output bisa dibandingkan secara langsung.

# retrieve bitstring counts
def get_bitstring_counts(job):
result = job.result()
pub_result = result[0]
counts = pub_result.data.meas.get_counts()
return counts, pub_result
counts, pub_result = get_bitstring_counts(job)

Jarak Hamming antara dua bitstring adalah jumlah indeks di mana bit-bitnya berbeda.

def hamming_distance(s1, s2):
weight = 0
for c1, c2 in zip(s1, s2):
(c1, c2) = (int(c1), int(c2))
if (c1 == 1 and c2 == 1) or (c1 == 0 and c2 == 0):
weight += 1

return weight
# Replace string of form a1b1a2b2... with b1a1b2a1...
# That is, reverse order of successive pairs of bits.
def unscramble(bitstring):
ps = [bitstring[i : i + 2][::-1] for i in range(0, len(bitstring), 2)]
return "".join(ps)

def find_hidden_shift_bitstring(counts, hidden_shift_string):
# convert counts to probabilities
probs = {
unscramble(bitstring): count / NUM_SHOTS
for bitstring, count in counts.items()
}

# Retrieve the most probable bitstring.
most_probable = max(probs, key=lambda x: probs[x])

print(f"Expected hidden shift string: {hidden_shift_string}")
if most_probable == hidden_shift_string:
print("Most probable bitstring matches hidden shift 😊.")
else:
print("Most probable bitstring didn't match hidden shift ☹️.")
print("Top 10 bitstrings and their probabilities:")
display(
{
k: (v, hamming_distance(hidden_shift_string, k))
for k, v in sorted(
probs.items(), key=lambda x: x[1], reverse=True
)[:10]
}
)

return probs, most_probable
probs, most_probable = find_hidden_shift_bitstring(
counts, hidden_shift_string
)
Expected hidden shift string: 011010
Most probable bitstring matches hidden shift 😊.
Top 10 bitstrings and their probabilities:
{'011010': (0.9743, 6),
'001010': (0.00812, 5),
'010010': (0.0063, 5),
'011000': (0.00554, 5),
'011011': (0.00492, 5),
'011110': (0.00044, 5),
'001000': (0.00012, 4),
'010000': (8e-05, 4),
'001011': (6e-05, 4),
'000010': (6e-05, 4)}

Mari kita catat probabilitas bitstring paling mungkin sebelum menerapkan mitigasi kesalahan readout dengan M3.

max_probability_before_M3 = probs[most_probable]
max_probability_before_M3
0.9743

Sekarang kita terapkan koreksi readout yang dipelajari oleh M3 pada counts. Fungsi apply_corrections mengembalikan distribusi kuasi-probabilitas. Ini adalah daftar objek float yang berjumlah 11. Namun beberapa nilainya bisa negatif.

def perform_mitigation(mit, counts, qubit_mapping):
# mitigate readout error
quasis = mit.apply_correction(counts, qubit_mapping)

# print results
most_probable_after_m3 = unscramble(max(quasis, key=lambda x: quasis[x]))

is_hidden_shift_identified = most_probable_after_m3 == hidden_shift_string
if is_hidden_shift_identified:
print("Most probable bitstring matches hidden shift 😊.")
else:
print("Most probable bitstring didn't match hidden shift ☹️.")
print("Top 10 bitstrings and their quasi-probabilities:")
topten = {
unscramble(k): f"{v:.2e}"
for k, v in sorted(quasis.items(), key=lambda x: x[1], reverse=True)[
:10
]
}
max_probability_after_M3 = float(topten[most_probable_after_m3])
display(topten)

return max_probability_after_M3, is_hidden_shift_identified
print(f"Expected hidden shift string: {hidden_shift_string}")
max_probability_after_M3, is_hidden_shift_identified = perform_mitigation(
mit, counts, qubit_mapping
)
Expected hidden shift string: 011010
Most probable bitstring matches hidden shift 😊.
Top 10 bitstrings and their quasi-probabilities:
{'011010': '1.01e+00',
'001010': '8.75e-04',
'001000': '7.38e-05',
'010000': '4.51e-05',
'111000': '2.18e-05',
'001011': '1.74e-05',
'000010': '6.42e-06',
'011001': '-7.18e-06',
'011000': '-4.53e-04',
'010010': '-1.28e-03'}

Membandingkan identifikasi hidden shift string sebelum dan sesudah penerapan koreksi M3

def compare_before_and_after_M3(
max_probability_before_M3,
max_probability_after_M3,
is_hidden_shift_identified,
):
is_probability_improved = (
max_probability_after_M3 > max_probability_before_M3
)
print(f"Most probable probability before M3: {max_probability_before_M3}")
print(f"Most probable probability after M3: {max_probability_after_M3}")
if is_hidden_shift_identified and is_probability_improved:
print("Readout error mitigation effective! 😊")
else:
print("Readout error mitigation not effective. ☹️")
compare_before_and_after_M3(
max_probability_before_M3,
max_probability_after_M3,
is_hidden_shift_identified,
)
Most probable probability before M3: 0.9743
Most probable probability after M3: 1.01
Readout error mitigation effective! 😊

Memplot bagaimana waktu CPU yang dibutuhkan M3 skala dengan jumlah shots

# Collect samples for numbers of shots varying from 5000 to 25000.
shots_range = range(5000, NUM_SHOTS + 1, 2500)
times = []
for shots in shots_range:
print(f"Applying M3 correction to {shots} shots...")
t0 = timeit.default_timer()
_ = mit.apply_correction(
pub_result.data.meas.slice_shots(range(shots)).get_counts(),
qubit_mapping,
)
t1 = timeit.default_timer()
print(f"\tDone in {t1 - t0} seconds.")
times.append(t1 - t0)

fig, ax = plt.subplots()
ax.plot(shots_range, times, "o--")
ax.set_xlabel("Shots")
ax.set_ylabel("Time (s)")
ax.set_title("Time to apply M3 correction")
Applying M3 correction to 5000 shots...
Done in 0.003321983851492405 seconds.
Applying M3 correction to 7500 shots...
Done in 0.004425413906574249 seconds.
Applying M3 correction to 10000 shots...
Done in 0.006366567220538855 seconds.
Applying M3 correction to 12500 shots...
Done in 0.0071477219462394714 seconds.
Applying M3 correction to 15000 shots...
Done in 0.00860048783943057 seconds.
Applying M3 correction to 17500 shots...
Done in 0.010026784148067236 seconds.
Applying M3 correction to 20000 shots...
Done in 0.011459112167358398 seconds.
Applying M3 correction to 22500 shots...
Done in 0.012727141845971346 seconds.
Applying M3 correction to 25000 shots...
Done in 0.01406092382967472 seconds.
Applying M3 correction to 27500 shots...
Done in 0.01546052098274231 seconds.
Applying M3 correction to 30000 shots...
Done in 0.016769016161561012 seconds.
Applying M3 correction to 32500 shots...
Done in 0.019537431187927723 seconds.
Applying M3 correction to 35000 shots...
Done in 0.019739801064133644 seconds.
Applying M3 correction to 37500 shots...
Done in 0.021093040239065886 seconds.
Applying M3 correction to 40000 shots...
Done in 0.022840639110654593 seconds.
Applying M3 correction to 42500 shots...
Done in 0.023974396288394928 seconds.
Applying M3 correction to 45000 shots...
Done in 0.026412792038172483 seconds.
Applying M3 correction to 47500 shots...
Done in 0.026364430785179138 seconds.
Applying M3 correction to 50000 shots...
Done in 0.02820305060595274 seconds.
Text(0.5, 1.0, 'Time to apply M3 correction')

Output of the previous code cell

Menafsirkan plot

Plot di atas menunjukkan bahwa waktu yang dibutuhkan untuk menerapkan koreksi M3 skala secara linier terhadap jumlah shots.

Scaling up

n_qubits = 80
rng = Random(12345)
circuit, hidden_shift, hidden_shift_string = run_hidden_shift_circuit(
n_qubits, rng
)

print(f"Hidden shift string {hidden_shift_string}")
Hidden shift string 00000010100110101011101110010001010000110011101001101010101001111001100110000111
isa_circuit = get_isa_circuit(circuit, backend)
job = run_sampler(backend, isa_circuit, NUM_SHOTS)
mit, qubit_mapping = setup_mthree_mitigation(isa_circuit, backend)
counts, pub_result = get_bitstring_counts(job)
probs, most_probable = find_hidden_shift_bitstring(
counts, hidden_shift_string
)
Expected hidden shift string: 00000010100110101011101110010001010000110011101001101010101001111001100110000111
Most probable bitstring matches hidden shift 😊.
Top 10 bitstrings and their probabilities:
{'00000010100110101011101110010001010000110011101001101010101001111001100110000111': (0.50402,
80),
'00000010100110101011101110010001010000110011100001101010101001111001100110000111': (0.0396,
79),
'00000010100110101011101110010001010000110011101001101010101001111001100100000111': (0.0323,
79),
'00000010100110101011101110010001010000110011101001101010101001101001100110000111': (0.01936,
79),
'00000010100110101011101110010011010000110011101001101010101001111001100110000111': (0.01432,
79),
'00000010100110101011101110010001010000110011101001101010101001011001100110000111': (0.0101,
79),
'00000010100110101011101110010001010000110011101001101010101001110001100110000111': (0.00924,
79),
'00000010100110101011101110010001010000010011101001101010101001111001100110000111': (0.00908,
79),
'00000010100110101011100110010001010000110011101001101010101001111001100110000111': (0.00888,
79),
'00000010100110101011101110010001010000110011101001100010101001111001100110000111': (0.0082,
79)}

Kita melihat bahwa hidden shift string yang benar berhasil ditemukan. Selain itu, sembilan bitstring berikutnya yang paling mungkin hanya salah di satu posisi. Catat probabilitas paling mungkin:

max_probability_before_M3 = probs[most_probable]
max_probability_before_M3
0.50402
print(f"Expected hidden shift string: {hidden_shift_string}")
max_probability_after_M3, is_hidden_shift_identified = perform_mitigation(
mit, counts, qubit_mapping
)
Expected hidden shift string: 00000010100110101011101110010001010000110011101001101010101001111001100110000111
Most probable bitstring matches hidden shift 😊.
Top 10 bitstrings and their quasi-probabilities:
{'00000010100110101011101110010001010000110011101001101010101001111001100110000111': '9.85e-01',
'00000010100110101011101110010001010000110011100001101010101001111001100110000111': '6.84e-03',
'00000010100110101011100110010001010000110011101001101010101001111001100110000111': '3.87e-03',
'00000010100110101011101110010011010000110011101001101010101001111001100110000111': '3.42e-03',
'00000010100110101011101110010001010000110011101001101010101001111001100100000111': '3.30e-03',
'00000010100110101011101110010001010000110011101001101010101001110001100110000111': '3.28e-03',
'00000010100010101011101110010001010000110011101001101010101001111001100110000111': '2.62e-03',
'00000010100110101011101110010001010000110011101001101010101001101001100110000111': '2.43e-03',
'00000010100110101011101110010000010000110011101001101010101001111001100110000111': '1.73e-03',
'00000010100110101011101110010001010000110011101001101010101001111001000110000111': '1.63e-03'}
compare_before_and_after_M3(
max_probability_before_M3,
max_probability_after_M3,
is_hidden_shift_identified,
)
Most probable probability before M3: 0.54348
Most probable probability after M3: 0.99
Readout error mitigation effective! 😊

Hasil ini menunjukkan bahwa kesalahan readout adalah sumber kesalahan yang dominan dan mitigasi M3 efektif.

Source: IBM Quantum docs — updated 15 Jan 2026
English version on doQumentation — updated 7 Mei 2026
This translation based on the English version of 9 Apr 2026