Lewati ke konten utama

Menggabungkan opsi mitigasi error dengan primitif Estimator

Estimasi penggunaan: 7 menit pada prosesor Heron r2 (CATATAN: Ini hanya estimasi. Waktu eksekusi kamu bisa berbeda.)

Hasil pembelajaran

Kami menyarankan pengguna untuk familiar dengan topik-topik berikut sebelum mengikuti tutorial ini:

  • Dasar-dasar dynamical decoupling, mitigasi error pengukuran, gate twirling, dan zero-noise extrapolation, sebagaimana dijelaskan dalam panduan ini.

Prasyarat

Setelah mengikuti tutorial ini, pengguna diharapkan memahami:

  • Bagaimana teknik-teknik mitigasi error tersebut diterapkan secara selektif pada perangkat keras.
  • Bagaimana perbandingannya dalam hal kemampuan memitigasi noise perangkat keras.

Latar Belakang

Tutorial ini mengeksplorasi opsi penekanan error dan mitigasi error yang tersedia dalam primitif Estimator dari Qiskit Runtime. Tutorial ini menunjukkan cara mengimplementasikan setiap metode berikut secara individual:

  • Dynamical decoupling
  • Mitigasi error pengukuran
  • Gate twirling
  • Zero-noise extrapolation (ZNE)

Perlu dicatat bahwa alternatif untuk mengimplementasikan teknik-teknik ini secara individual adalah mengimplementasikannya menggunakan resilience level, di mana resilience_level mengambil nilai 0, 1, 2:

  • 0 : Tidak ada mitigasi yang diterapkan.
  • 1 : Mitigasi error pengukuran diterapkan.
  • 2 : Gate twirling, mitigasi error pengukuran, dan ZNE diterapkan.

Dalam tutorial ini, kamu akan membuat Circuit dan observable, lalu mengirimkan job menggunakan primitif Estimator dengan berbagai kombinasi pengaturan mitigasi error. Kemudian, kamu akan memplot hasilnya untuk mengamati efek dari berbagai pengaturan tersebut. Sebagian besar tutorial menggunakan Circuit 10-Qubit agar visualisasi lebih mudah, dan di bagian akhir, kamu akan memperbesar skala workflow hingga 50 qubit.

Persyaratan

Sebelum memulai panduan ini, pastikan kamu sudah menginstal hal-hal berikut:

  • Qiskit SDK v2.1 atau lebih baru, dengan dukungan visualisasi
  • Qiskit Runtime v0.40 atau lebih baru (pip install qiskit-ibm-runtime)

Setup

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-ibm-runtime
import matplotlib.pyplot as plt
import numpy as np

from qiskit.circuit.library import efficient_su2, unitary_overlap
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import Batch, EstimatorV2 as Estimator

Contoh simulator skala kecil

Kami akan melewati langkah ini karena mitigasi error runtime tidak didukung pada simulator.

Contoh perangkat keras

Langkah 1: Petakan input klasik ke masalah kuantum

Panduan ini mengasumsikan bahwa masalah klasik sudah dipetakan ke domain kuantum. Mulailah dengan membuat Circuit dan observable untuk diukur. Meskipun teknik-teknik yang digunakan di sini berlaku untuk berbagai jenis Circuit, demi kesederhanaan panduan ini menggunakan Circuit efficient_su2 yang tersedia di library Circuit Qiskit.

efficient_su2 adalah Circuit kuantum berparameter yang dirancang agar bisa dieksekusi secara efisien pada perangkat keras kuantum dengan konektivitas qubit terbatas, namun tetap cukup ekspresif untuk memecahkan masalah dalam domain aplikasi seperti optimasi dan kimia. Circuit ini dibangun dengan menggabungkan lapisan-lapisan Gate qubit tunggal berparameter secara bergantian dengan lapisan yang berisi pola tetap Gate dua-Qubit, untuk sejumlah pengulangan yang dipilih. Pola Gate dua-Qubit dapat ditentukan oleh pengguna. Di sini kamu bisa menggunakan pola bawaan pairwise karena meminimalkan kedalaman Circuit dengan memaketkan Gate dua-Qubit sepadat mungkin. Pola ini dapat dieksekusi hanya dengan konektivitas qubit linear.

n_qubits = 10
reps = 1

circuit = efficient_su2(n_qubits, entanglement="pairwise", reps=reps)

circuit.decompose().draw("mpl", scale=0.7)

Output of the previous code cell

Sebagai observable kita, ambil operator Pauli ZZ yang bekerja pada qubit terakhir, ZIIZ I \cdots I. Perlu diingat bahwa fakta bahwa qubit terakhir bersesuaian dengan elemen pertama string ini disebabkan oleh penggunaan notasi little-endian oleh Qiskit.

# Z on the last qubit (index -1) with coefficient 1.0
observable = SparsePauliOp.from_sparse_list(
[("Z", [-1], 1.0)], num_qubits=n_qubits
)

Pada titik ini, kamu bisa langsung menjalankan Circuit dan mengukur observable. Namun, kamu juga ingin membandingkan keluaran perangkat kuantum dengan jawaban yang benar — yaitu nilai teoritis observable jika Circuit dieksekusi tanpa error. Untuk Circuit kuantum kecil, kamu bisa menghitung nilai ini dengan mensimulasikan Circuit pada komputer klasik, tapi ini tidak memungkinkan untuk Circuit yang lebih besar dengan skala utilitas. Kamu bisa menyiasati masalah ini dengan teknik "mirror Circuit" (juga dikenal sebagai "compute-uncompute"), yang berguna untuk menguji performa perangkat kuantum.

Mirror Circuit

Dalam teknik mirror Circuit, kamu menggabungkan Circuit dengan Circuit inversnya, yang dibentuk dengan membalik setiap Gate dari Circuit dalam urutan terbalik. Circuit yang dihasilkan mengimplementasikan operator identitas, yang bisa disimulasikan secara sepele. Karena struktur Circuit asli dipertahankan dalam mirror Circuit, mengeksekusi mirror Circuit tetap memberikan gambaran bagaimana perangkat kuantum akan berkinerja pada Circuit aslinya.

Sel kode berikut menetapkan parameter acak ke Circuit kamu, lalu membangun mirror Circuit menggunakan kelas unitary_overlap. Sebelum melakukan mirroring pada Circuit, tambahkan instruksi barrier untuk mencegah Transpiler menggabungkan dua bagian Circuit di kedua sisi barrier dan menghasilkan Circuit yang sudah di-transpile tanpa Gate apapun.

# Generate random parameters
rng = np.random.default_rng(1234)
params = rng.uniform(-np.pi, np.pi, size=circuit.num_parameters)

# Assign the parameters to the circuit
assigned_circuit = circuit.assign_parameters(params)

# Add a barrier to prevent circuit optimization of mirrored operators
assigned_circuit.barrier()

# Construct mirror circuit
mirror_circuit = unitary_overlap(assigned_circuit, assigned_circuit)

mirror_circuit.decompose().draw("mpl", scale=0.7)

Output of the previous code cell

Langkah 2: Optimalkan masalah untuk eksekusi pada perangkat keras kuantum

Kamu harus mengoptimalkan Circuit sebelum menjalankannya pada perangkat keras. Proses ini melibatkan beberapa langkah:

  • Pilih tata letak qubit yang memetakan qubit virtual dari Circuit ke qubit fisik pada perangkat keras.
  • Sisipkan swap Gate sesuai kebutuhan untuk merutekan interaksi antara qubit yang tidak terhubung.
  • Terjemahkan Gate dalam Circuit ke instruksi Instruction Set Architecture (ISA) yang bisa langsung dieksekusi pada perangkat keras.
  • Lakukan optimasi Circuit untuk meminimalkan kedalaman Circuit dan jumlah Gate.

Transpiler yang terintegrasi dalam Qiskit bisa melakukan semua langkah ini untukmu. Karena contoh ini menggunakan Circuit yang efisien untuk perangkat keras, Transpiler seharusnya bisa memilih tata letak qubit yang tidak memerlukan penambahan swap Gate untuk merutekan interaksi.

Kamu perlu memilih perangkat keras yang akan digunakan sebelum mengoptimalkan Circuit. Sel kode berikut meminta perangkat yang paling tidak sibuk dengan minimal 127 qubit.

service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
print(backend)
<IBMBackend('ibm_fez')>

Kamu bisa men-transpile Circuit ke Backend yang dipilih dengan membuat pass manager lalu menjalankannya pada Circuit. Cara mudah membuat pass manager adalah menggunakan fungsi generate_preset_pass_manager. Lihat Transpile with pass managers untuk penjelasan lebih detail tentang transpilasi dengan pass manager.

pass_manager = generate_preset_pass_manager(
optimization_level=3, backend=backend, seed_transpiler=1234
)
isa_circuit = pass_manager.run(mirror_circuit)

isa_circuit.draw("mpl", idle_wires=False, scale=0.7, fold=-1)

Output of the previous code cell

Circuit yang sudah di-transpile kini hanya berisi instruksi ISA. Semua Gate telah didekomposisi dalam bentuk Gate X\sqrt{X} dan rotasi RzR_z, serta CZ gate.

Proses transpilasi telah memetakan qubit virtual dari Circuit ke qubit fisik pada perangkat keras. Informasi tentang tata letak qubit disimpan dalam atribut layout dari Circuit yang sudah di-transpile. Observable juga didefinisikan dalam qubit virtual, sehingga kamu perlu menerapkan layout ini ke observable, yang bisa dilakukan dengan metode apply_layout dari SparsePauliOp.

isa_observable = observable.apply_layout(isa_circuit.layout)

print("Original observable:")
print(observable)
print()
print("Observable with layout applied:")
print(isa_observable)
Original observable:
SparsePauliOp(['ZIIIIIIIII'],
coeffs=[1.+0.j])

Observable with layout applied:
SparsePauliOp(['IIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j])

Langkah 3: Eksekusi menggunakan primitif Qiskit

Kamu sekarang siap menjalankan Circuit menggunakan primitif Estimator.

Di sini kamu akan mengirimkan lima job terpisah, dimulai tanpa penekanan atau mitigasi error, lalu secara bertahap mengaktifkan berbagai opsi penekanan dan mitigasi error yang tersedia di Qiskit Runtime. Untuk informasi tentang opsi-opsi tersebut, lihat halaman berikut:

Karena job-job ini bisa berjalan secara independen satu sama lain, kamu bisa menggunakan batch mode agar Qiskit Runtime dapat mengoptimalkan waktu eksekusinya.

pub = (isa_circuit, isa_observable)

jobs = []

with Batch(backend=backend) as batch:
estimator = Estimator(mode=batch)
estimator.options.environment.job_tags = [
"TUT_CEM_SS"
] # add tag for this small scale job
# Set number of shots
estimator.options.default_shots = 100_000
# Disable runtime compilation and error mitigation
estimator.options.resilience_level = 0

# Run job with no error mitigation
job0 = estimator.run([pub])
jobs.append(job0)

# Add dynamical decoupling (DD)
estimator.options.dynamical_decoupling.enable = True
estimator.options.dynamical_decoupling.sequence_type = "XpXm"
job1 = estimator.run([pub])
jobs.append(job1)

# Add readout error mitigation (DD + TREX)
estimator.options.resilience.measure_mitigation = True
job2 = estimator.run([pub])
jobs.append(job2)

# Add gate twirling (DD + TREX + Gate Twirling)
estimator.options.twirling.enable_gates = True
estimator.options.twirling.num_randomizations = "auto"
job3 = estimator.run([pub])
jobs.append(job3)

# Add zero-noise extrapolation (DD + TREX + Gate Twirling + ZNE)
estimator.options.resilience.zne_mitigation = True
estimator.options.resilience.zne.noise_factors = (1, 3, 5)
estimator.options.resilience.zne.extrapolator = ("exponential", "linear")
job4 = estimator.run([pub])
jobs.append(job4)

Langkah 4: Pasca-proses dan kembalikan hasil dalam format klasik yang diinginkan

Terakhir, kamu bisa menganalisis data. Di sini kamu akan mengambil hasil job, mengekstrak nilai ekspektasi yang diukur, dan memplotnya beserta error bar satu standar deviasi.

# Retrieve the job results
results = [job.result() for job in jobs]

# Unpack the PUB results (there's only one PUB result in each job result)
pub_results = [result[0] for result in results]

# Unpack the expectation values and standard errors
expectation_vals = np.array(
[float(pub_result.data.evs) for pub_result in pub_results]
)
standard_errors = np.array(
[float(pub_result.data.stds) for pub_result in pub_results]
)

# Plot the expectation values
fig, ax = plt.subplots()
labels = ["No mitigation", "+ DD", "+ TREX", "+ Twirling", "+ ZNE"]
ax.bar(
range(len(labels)),
expectation_vals,
yerr=standard_errors,
label="experiment",
)
ax.axhline(y=1.0, color="gray", linestyle="--", label="ideal")
ax.set_xticks(range(len(labels)))
ax.set_xticklabels(labels)
ax.set_ylabel("Expectation value")
ax.legend(loc="upper left")

plt.show()

Output of the previous code cell

Pada skala kecil ini, sulit untuk melihat efek dari sebagian besar teknik mitigasi error, tetapi zero-noise extrapolation memberikan peningkatan yang cukup terlihat. Namun, perhatikan bahwa peningkatan ini tidak gratis, karena hasil ZNE juga memiliki error bar yang lebih besar.

Contoh perangkat keras skala besar

Ketika mengembangkan eksperimen, berguna untuk memulai dengan Circuit kecil agar visualisasi dan simulasi lebih mudah. Setelah kamu mengembangkan dan menguji workflow pada Circuit 10-Qubit, kamu bisa memperbesarnya hingga 50 qubit. Sel kode berikut mengulangi semua langkah dalam panduan ini, tetapi kini diterapkan pada Circuit 50-Qubit.

n_qubits = 50
reps = 1

# Construct circuit and observable
circuit = efficient_su2(n_qubits, entanglement="pairwise", reps=reps)
observable = SparsePauliOp.from_sparse_list(
[("Z", [-1], 1.0)], num_qubits=n_qubits
)

# Assign parameters to circuit
params = rng.uniform(-np.pi, np.pi, size=circuit.num_parameters)
assigned_circuit = circuit.assign_parameters(params)
assigned_circuit.barrier()

# Construct mirror circuit
mirror_circuit = unitary_overlap(assigned_circuit, assigned_circuit)

# Transpile circuit and observable
isa_circuit = pass_manager.run(mirror_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)

# Run jobs
pub = (isa_circuit, isa_observable)

jobs = []

with Batch(backend=backend) as batch:
estimator = Estimator(mode=batch)
estimator.options.environment.job_tags = [
"TUT_CEM_LS"
] # add tag for this large scale job
# Set number of shots
estimator.options.default_shots = 100_000
# Disable runtime compilation and error mitigation
estimator.options.resilience_level = 0

# Run job with no error mitigation
job0 = estimator.run([pub])
jobs.append(job0)

# Add dynamical decoupling (DD)
estimator.options.dynamical_decoupling.enable = True
estimator.options.dynamical_decoupling.sequence_type = "XpXm"
job1 = estimator.run([pub])
jobs.append(job1)

# Add readout error mitigation (DD + TREX)
estimator.options.resilience.measure_mitigation = True
job2 = estimator.run([pub])
jobs.append(job2)

# Add gate twirling (DD + TREX + Gate Twirling)
estimator.options.twirling.enable_gates = True
estimator.options.twirling.num_randomizations = "auto"
job3 = estimator.run([pub])
jobs.append(job3)

# Add zero-noise extrapolation (DD + TREX + Gate Twirling + ZNE)
estimator.options.resilience.zne_mitigation = True
estimator.options.resilience.zne.noise_factors = (1, 3, 5)
estimator.options.resilience.zne.extrapolator = ("exponential", "linear")
job4 = estimator.run([pub])
jobs.append(job4)

# Retrieve the job results
results = [job.result() for job in jobs]

# Unpack the PUB results (there's only one PUB result in each job result)
pub_results = [result[0] for result in results]

# Unpack the expectation values and standard errors
expectation_vals = np.array(
[float(pub_result.data.evs) for pub_result in pub_results]
)
standard_errors = np.array(
[float(pub_result.data.stds) for pub_result in pub_results]
)

# Plot the expectation values
fig, ax = plt.subplots()
labels = ["No mitigation", "+ DD", "+ TREX", "+ Twirling", "+ ZNE"]
ax.bar(
range(len(labels)),
expectation_vals,
yerr=standard_errors,
label="experiment",
)
ax.axhline(y=1.0, color="gray", linestyle="--", label="ideal")
ax.set_xticks(range(len(labels)))
ax.set_xticklabels(labels)
ax.set_ylabel("Expectation value")
ax.legend(loc="upper left")

plt.show()

Output of the previous code cell

Ketika kamu membandingkan hasil 50-Qubit dengan hasil 10-Qubit sebelumnya, kamu mungkin memperhatikan hal-hal berikut (hasilmu mungkin berbeda antar run):

  • Semua eksperimen memberikan hasil yang lebih mendekati nilai ideal dan semua error bar lebih kecil.
  • Penambahan dynamical decoupling mungkin justru memperburuk performa dibandingkan kasus tanpa mitigasi. Ini tidak mengherankan, karena Circuit sangat padat. Dynamical decoupling terutama berguna ketika ada celah besar dalam Circuit, di mana qubit diam tanpa Gate yang diterapkan. Ketika celah ini tidak ada, dynamical decoupling tidak efektif, dan bahkan bisa memperburuk performa karena error pada pulsa dynamical decoupling itu sendiri. Circuit 10-Qubit mungkin terlalu kecil untuk kita amati efek ini.
  • Dengan zero-noise extrapolation, hasilnya sangat mendekati nilai ideal. Ini menunjukkan kekuatan ZNE.

Langkah selanjutnya

Rekomendasi

Jika kamu menemukan karya ini menarik, kamu mungkin tertarik dengan materi berikut tentang beberapa teknik mitigasi error dan penekanan error tambahan yang tidak disebutkan dalam tutorial ini: