Lewati ke konten utama

Fungsi biaya

Dalam pelajaran ini, kita akan belajar cara mengevaluasi sebuah fungsi biaya:

  • Pertama, kita akan belajar tentang primitif Qiskit Runtime
  • Mendefinisikan sebuah fungsi biaya C(θ)C(\vec\theta). Ini adalah fungsi yang spesifik untuk masalah tertentu yang mendefinisikan tujuan masalah bagi optimizer untuk diminimalkan (atau dimaksimalkan)
  • Mendefinisikan strategi pengukuran dengan primitif Qiskit Runtime untuk mengoptimalkan kecepatan versus akurasi

 

Diagram yang menunjukkan komponen-komponen utama dari fungsi biaya termasuk penggunaan primitif seperti Estimator dan Sampler.

Primitif

Semua sistem fisik, baik klasik maupun kuantum, bisa ada dalam keadaan yang berbeda-beda. Misalnya, sebuah mobil di jalan bisa memiliki massa, posisi, kecepatan, atau percepatan tertentu yang mencirikan keadaannya. Demikian pula, sistem kuantum juga bisa memiliki konfigurasi atau keadaan yang berbeda, namun berbeda dari sistem klasik dalam cara kita menangani pengukuran dan evolusi keadaan. Hal ini mengarah pada sifat-sifat unik seperti superposisi dan keterikatan yang eksklusif untuk mekanika kuantum. Sama seperti kita bisa menggambarkan keadaan mobil menggunakan sifat fisik seperti kecepatan atau percepatan, kita juga bisa menggambarkan keadaan sistem kuantum menggunakan observabel, yang merupakan objek matematis.

Dalam mekanika kuantum, keadaan direpresentasikan oleh vektor kolom kompleks yang dinormalisasi, atau ket (ψ|\psi\rangle), dan observabel adalah operator linear Hermitian (H^=H^\hat{H}=\hat{H}^{\dagger}) yang bekerja pada ket. Vektor eigen (λ|\lambda\rangle) dari sebuah observabel dikenal sebagai eigenstate. Mengukur observabel untuk salah satu eigenstate-nya (λ|\lambda\rangle) akan memberikan nilai eigen yang sesuai (λ\lambda) sebagai hasil baca.

Jika kamu bertanya-tanya bagaimana cara mengukur sistem kuantum dan apa yang bisa kamu ukur, Qiskit menawarkan dua primitif yang bisa membantu:

  • Sampler: Diberikan keadaan kuantum ψ|\psi\rangle, primitif ini mendapatkan probabilitas dari setiap kemungkinan keadaan basis komputasional.
  • Estimator: Diberikan observabel kuantum H^\hat{H} dan keadaan ψ|\psi\rangle, primitif ini menghitung nilai ekspektasi dari H^\hat{H}.

Primitif Sampler

Primitif Sampler menghitung probabilitas mendapatkan setiap kemungkinan keadaan k|k\rangle dari basis komputasional, diberikan sebuah Circuit kuantum yang mempersiapkan keadaan ψ|\psi\rangle. Ia menghitung

pk=kψ2kZ2n{0,1,,2n1},p_k = |\langle k | \psi \rangle|^2 \quad \forall k \in \mathbb{Z}_2^n \equiv \{0,1,\cdots,2^n-1\},

Di mana nn adalah jumlah qubit, dan kk representasi integer dari semua kemungkinan string biner output {0,1}n\{0,1\}^n (yaitu, bilangan bulat basis 22).

Qiskit Runtime Sampler menjalankan Circuit beberapa kali pada perangkat kuantum, melakukan pengukuran pada setiap jalannya, dan merekonstruksi distribusi probabilitas dari bitstring yang didapatkan. Semakin banyak jalannya (atau shot), semakin akurat hasilnya, namun ini membutuhkan lebih banyak waktu dan sumber daya kuantum.

Namun, karena jumlah kemungkinan output tumbuh secara eksponensial seiring dengan jumlah qubit nn (yaitu, 2n2^n), jumlah shot juga perlu tumbuh secara eksponensial agar bisa menangkap distribusi probabilitas yang padat. Oleh karena itu, Sampler hanya efisien untuk distribusi probabilitas yang jarang; di mana keadaan target ψ|\psi\rangle harus bisa diekspresikan sebagai kombinasi linear dari keadaan basis komputasional, dengan jumlah suku yang tumbuh paling banyak secara polinomial seiring dengan jumlah qubit:

ψ=kPoly(n)wkk.|\psi\rangle = \sum^{\text{Poly}(n)}_k w_k |k\rangle.

Sampler juga bisa dikonfigurasi untuk mengambil probabilitas dari sebagian Circuit, yang merepresentasikan subset dari total kemungkinan keadaan.

Primitif Estimator

Primitif Estimator menghitung nilai ekspektasi dari observabel H^\hat{H} untuk keadaan kuantum ψ|\psi\rangle; di mana probabilitas observabel bisa diekspresikan sebagai pλ=λψ2p_\lambda = |\langle\lambda|\psi\rangle|^2, dengan λ|\lambda\rangle adalah eigenstate dari observabel H^\hat{H}. Nilai ekspektasi kemudian didefinisikan sebagai rata-rata dari semua kemungkinan hasil λ\lambda (yaitu, nilai eigen dari observabel) dari sebuah pengukuran keadaan ψ|\psi\rangle, berbobot oleh probabilitas yang sesuai:

H^ψ:=λpλλ=ψH^ψ\langle\hat{H}\rangle_\psi := \sum_\lambda p_\lambda \lambda = \langle \psi | \hat{H} | \psi \rangle

Namun, menghitung nilai ekspektasi dari sebuah observabel tidak selalu bisa dilakukan, karena kita sering tidak mengetahui eigenbasis-nya. Qiskit Runtime Estimator menggunakan proses aljabar yang kompleks untuk memperkirakan nilai ekspektasi pada perangkat kuantum nyata dengan menguraikan observabel menjadi kombinasi dari observabel lain yang eigenbasis-nya sudah kita ketahui.

Dalam istilah yang lebih sederhana, Estimator menguraikan setiap observabel yang tidak diketahui cara mengukurnya menjadi observabel yang lebih sederhana dan bisa diukur yang disebut operator Pauli. Setiap operator bisa diekspresikan sebagai kombinasi dari 4n4^n operator Pauli.

P^k:=σkn1σk0kZ4n{0,1,,4n1},\hat{P}_k := \sigma_{k_{n-1}}\otimes \cdots \otimes \sigma_{k_0} \quad \forall k \in \mathbb{Z}_4^n \equiv \{0,1,\cdots,4^n-1\}, \\

sehingga

H^=k=04n1wkP^k\hat{H} = \sum^{4^n-1}_{k=0} w_k \hat{P}_k

di mana nn adalah jumlah qubit, kkn1k0k \equiv k_{n-1} \cdots k_0 untuk klZ4{0,1,2,3}k_l \in \mathbb{Z}_4 \equiv \{0, 1, 2, 3\} (yaitu, bilangan bulat basis 44), dan (σ0,σ1,σ2,σ3):=(I,X,Y,Z)(\sigma_0, \sigma_1, \sigma_2, \sigma_3) := (I, X, Y, Z).

Setelah melakukan dekomposisi ini, Estimator menghasilkan Circuit baru VkψV_k|\psi\rangle untuk setiap observabel P^k\hat{P}_k (dari Circuit asli), untuk secara efektif mendiagonalisasi observabel Pauli dalam basis komputasional dan mengukurnya. Kita bisa dengan mudah mengukur observabel Pauli karena kita mengetahui VkV_k sebelumnya, yang tidak demikian halnya untuk observabel lain pada umumnya.

Untuk setiap P^k\hat{P}_{k}, Estimator menjalankan Circuit yang sesuai pada perangkat kuantum beberapa kali, mengukur keadaan output dalam basis komputasional, dan menghitung probabilitas pkjp_{kj} untuk mendapatkan setiap kemungkinan output jj. Kemudian ia mencari nilai eigen λkj\lambda_{kj} dari PkP_k yang sesuai dengan setiap output jj, mengalikannya dengan wkw_k, dan menjumlahkan semua hasilnya untuk mendapatkan nilai ekspektasi dari observabel H^\hat{H} untuk keadaan ψ|\psi\rangle yang diberikan.

H^ψ=k=04n1wkj=02n1pkjλkj,\langle\hat{H}\rangle_\psi = \sum_{k=0}^{4^n-1} w_k \sum_{j=0}^{2^n-1}p_{kj} \lambda_{kj},

Karena menghitung nilai ekspektasi dari 4n4^n Pauli tidak praktis (yaitu, tumbuh secara eksponensial), Estimator hanya bisa efisien ketika sejumlah besar wkw_k bernilai nol (yaitu, dekomposisi Pauli yang jarang bukan yang padat). Secara formal kita katakan bahwa, agar komputasi ini bisa diselesaikan secara efisien, jumlah suku yang tidak nol harus tumbuh paling banyak secara polinomial seiring dengan jumlah qubit nn: H^=kPoly(n)wkP^k.\hat{H} = \sum^{\text{Poly}(n)}_k w_k \hat{P}_k.

Pembaca mungkin memperhatikan asumsi implisit bahwa pengambilan sampel probabilitas juga perlu efisien seperti yang dijelaskan untuk Sampler, yang berarti

H^ψ=kPoly(n)wkjPoly(n)pkjλkj.\langle\hat{H}\rangle_\psi = \sum_{k}^{\text{Poly}(n)} w_k \sum_{j}^{\text{Poly}(n)}p_{kj} \lambda_{kj}.

Contoh terpandu untuk menghitung nilai ekspektasi

Mari kita asumsikan keadaan qubit tunggal +:=H0=12(0+1)|+\rangle := H|0\rangle = \frac{1}{\sqrt{2}}(|0\rangle + |1\rangle), dan observabel

H^=(1221)=2XZ\begin{aligned} \hat{H} & = \begin{pmatrix} -1 & 2 \\ 2 & 1 \\ \end{pmatrix}\\[1mm] & = 2X - Z \end{aligned}

dengan nilai ekspektasi teoritis berikut H^+=+H^+=2.\langle\hat{H}\rangle_+ = \langle+|\hat{H}|+\rangle = 2.

Karena kita tidak tahu cara mengukur observabel ini, kita tidak bisa menghitung nilai ekspektasinya secara langsung, dan kita perlu mengekspresikannya kembali sebagai H^+=2X+Z+\langle\hat{H}\rangle_+ = 2\langle X \rangle_+ - \langle Z \rangle_+ . Yang bisa ditunjukkan menghasilkan hasil yang sama dengan memperhatikan bahwa +X+=1\langle+|X|+\rangle = 1, dan +Z+=0\langle+|Z|+\rangle = 0.

Mari kita lihat cara menghitung X+\langle X \rangle_+ dan Z+\langle Z \rangle_+ secara langsung. Karena XX dan ZZ tidak komutatif (yaitu, mereka tidak berbagi eigenbasis yang sama), mereka tidak bisa diukur secara bersamaan, sehingga kita memerlukan Circuit bantu:

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-aer qiskit-ibm-runtime rustworkx
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp

# The following code will work for any other initial single-qubit state and observable
original_circuit = QuantumCircuit(1)
original_circuit.h(0)

H = SparsePauliOp(["X", "Z"], [2, -1])

aux_circuits = []
for pauli in H.paulis:
aux_circ = original_circuit.copy()
aux_circ.barrier()
if str(pauli) == "X":
aux_circ.h(0)
elif str(pauli) == "Y":
aux_circ.sdg(0)
aux_circ.h(0)
else:
aux_circ.id(0)
aux_circ.measure_all()
aux_circuits.append(aux_circ)

original_circuit.draw("mpl")

Output of the previous code cell

# Auxiliary circuit for X
aux_circuits[0].draw("mpl")

Output of the previous code cell

# Auxiliary circuit for Z
aux_circuits[1].draw("mpl")

Output of the previous code cell

Sekarang kita bisa melakukan komputasi secara manual menggunakan Sampler dan memeriksa hasilnya di Estimator:

from qiskit.primitives import StatevectorSampler, StatevectorEstimator
from qiskit.result import QuasiDistribution
import numpy as np

## SAMPLER
shots = 10000
sampler = StatevectorSampler()
job = sampler.run(aux_circuits, shots=shots)

# Run the sampler job and step through results
expvals = []
for index, pauli in enumerate(H.paulis):
data_pub = job.result()[index].data
bitstrings = data_pub.meas.get_bitstrings()
counts = data_pub.meas.get_counts()
quasi_dist = QuasiDistribution(
{outcome: freq / shots for outcome, freq in counts.items()}
)

# Use the probabilities and known eigenvalues of Pauli operators to estimate the expectation value.
val = 0

if str(pauli) == "X":
val += -1 * quasi_dist.get(1, 0)
val += 1 * quasi_dist.get(0, 0)

if str(pauli) == "Y":
val += -1 * quasi_dist.get(1, 0)
val += 1 * quasi_dist.get(0, 0)

if str(pauli) == "Z":
val += 1 * quasi_dist.get(0, 0)
val += -1 * quasi_dist.get(1, 0)

expvals.append(val)

# Print expectation values

print("Sampler results:")
for pauli, expval in zip(H.paulis, expvals):
print(f" >> Expected value of {str(pauli)}: {expval:.5f}")

total_expval = np.sum(H.coeffs * expvals).real
print(f" >> Total expected value: {total_expval:.5f}")

# Use estimator for comparison
observables = [
*H.paulis,
H,
] # Note: run for individual Paulis as well as full observable H

estimator = StatevectorEstimator()
job = estimator.run([(original_circuit, observables)])
estimator_expvals = job.result()[0].data.evs

# Print results
print("Estimator results:")
for obs, expval in zip(observables, estimator_expvals):
if obs is not H:
print(f" >> Expected value of {str(obs)}: {expval:.5f}")
else:
print(f" >> Total expected value: {expval:.5f}")
Sampler results:
>> Expected value of X: 1.00000
>> Expected value of Z: 0.00420
>> Total expected value: 1.99580
Estimator results:
>> Expected value of X: 1.00000
>> Expected value of Z: 0.00000
>> Total expected value: 2.00000

Ketelitian matematis (opsional)

Mengekspresikan ψ|\psi\rangle terhadap basis eigenstate dari H^\hat{H}, ψ=λaλλ|\psi\rangle = \sum_\lambda a_\lambda |\lambda\rangle, maka:

ψH^ψ=(λaλλ)H^(λaλλ)=λλaλaλλH^λ=λλaλaλλλλ=λλaλaλλδλ,λ=λaλ2λ=λpλλ\begin{aligned} \langle \psi | \hat{H} | \psi \rangle & = \bigg(\sum_{\lambda'}a^*_{\lambda'} \langle \lambda'|\bigg) \hat{H} \bigg(\sum_{\lambda} a_\lambda | \lambda\rangle\bigg)\\[1mm] & = \sum_{\lambda}\sum_{\lambda'} a^*_{\lambda'}a_{\lambda} \langle \lambda'|\hat{H}| \lambda\rangle\\[1mm] & = \sum_{\lambda}\sum_{\lambda'} a^*_{\lambda'}a_{\lambda} \lambda \langle \lambda'| \lambda\rangle\\[1mm] & = \sum_{\lambda}\sum_{\lambda'} a^*_{\lambda'}a_{\lambda} \lambda \cdot \delta_{\lambda, \lambda'}\\[1mm] & = \sum_\lambda |a_\lambda|^2 \lambda\\[1mm] & = \sum_\lambda p_\lambda \lambda\\[1mm] \end{aligned}

Karena kita tidak mengetahui nilai eigen atau eigenstate dari observabel target H^\hat{H}, pertama-tama kita perlu mempertimbangkan diagonalisasinya. Mengingat bahwa H^\hat{H} bersifat Hermitian, terdapat transformasi uniter VV sedemikian sehingga H^=VΛV,\hat{H}=V^\dagger \Lambda V, di mana Λ\Lambda adalah matriks nilai eigen diagonal, sehingga jΛk=0\langle j | \Lambda | k \rangle = 0 jika jkj\neq k, dan jΛj=λj\langle j | \Lambda | j \rangle = \lambda_j.

Ini berarti nilai ekspektasi bisa ditulis ulang sebagai:

ψH^ψ=ψVΛVψ=ψV(j=02n1jj)Λ(k=02n1kk)Vψ=j=02n1k=02n1ψVjjΛkkVψ=j=02n1ψVjjΛjjVψ=j=02n1jVψ2λj\begin{aligned} \langle\psi|\hat{H}|\psi\rangle & = \langle\psi|V^\dagger \Lambda V|\psi\rangle\\[1mm] & = \langle\psi|V^\dagger \bigg(\sum_{j=0}^{2^n-1} |j\rangle \langle j|\bigg) \Lambda \bigg(\sum_{k=0}^{2^n-1} |k\rangle \langle k|\bigg) V|\psi\rangle\\[1mm] & = \sum_{j=0}^{2^n-1} \sum_{k=0}^{2^n-1}\langle\psi|V^\dagger |j\rangle \langle j| \Lambda |k\rangle \langle k| V|\psi\rangle\\[1mm] & = \sum_{j=0}^{2^n-1}\langle\psi|V^\dagger |j\rangle \langle j| \Lambda |j\rangle \langle j| V|\psi\rangle\\[1mm] & = \sum_{j=0}^{2^n-1}|\langle j| V|\psi\rangle|^2 \lambda_j\\[1mm] \end{aligned}

Mengingat bahwa jika sebuah sistem berada dalam keadaan ϕ=Vψ|\phi\rangle = V |\psi\rangle probabilitas mengukur j| j\rangle adalah pj=jϕ2p_j = |\langle j|\phi \rangle|^2, nilai ekspektasi di atas bisa diekspresikan sebagai:

ψH^ψ=j=02n1pjλj.\langle\psi|\hat{H}|\psi\rangle = \sum_{j=0}^{2^n-1} p_j \lambda_j.

Sangat penting untuk diperhatikan bahwa probabilitas diambil dari keadaan VψV |\psi\rangle bukan dari ψ|\psi\rangle. Inilah mengapa matriks VV sangat diperlukan. Kamu mungkin bertanya-tanya bagaimana cara mendapatkan matriks VV dan nilai eigen Λ\Lambda. Jika kamu sudah memiliki nilai eigen, maka tidak perlu menggunakan komputer kuantum karena tujuan algoritma variasional adalah menemukan nilai eigen dari H^\hat{H} ini.

Untungnya, ada cara untuk mengatasinya: setiap matriks 2n×2n2^n \times 2^n bisa ditulis sebagai kombinasi linear dari 4n4^n hasil kali tensor dari nn matriks Pauli dan identitas, yang semuanya bersifat hermitian dan uniter dengan VV dan Λ\Lambda yang sudah diketahui. Inilah yang dilakukan Estimator Runtime secara internal dengan menguraikan setiap objek Operator menjadi SparsePauliOp.

Berikut adalah Operator yang bisa digunakan:

OperatorσVΛIσ0=(1001)V0=IΛ0=I=(1001)Xσ1=(0110)V1=H=12(1111)Λ1=σ3=(1001)Yσ2=(0ii0)V2=HS=12(1111)(100i)=12(1i1i)Λ2=σ3=(1001)Zσ3=(1001)V3=IΛ3=σ3=(1001)\begin{array}{c|c|c|c} \text{Operator} & \sigma & V & \Lambda \\[1mm] \hline I & \sigma_0 = \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix} & V_0 = I & \Lambda_0 = I = \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix} \\[4mm] X & \sigma_1 = \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix} & V_1 = H =\frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} & \Lambda_1 = \sigma_3 = \begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix} \\[4mm] Y & \sigma_2 = \begin{pmatrix} 0 & -i \\ i & 0 \end{pmatrix} & V_2 = HS^\dagger =\frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix}\cdot \begin{pmatrix} 1 & 0 \\ 0 & -i \end{pmatrix} = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & -i \\ 1 & i \end{pmatrix}\quad & \Lambda_2 = \sigma_3 = \begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix} \\[4mm] Z & \sigma_3 = \begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix} & V_3 = I & \Lambda_3 = \sigma_3 = \begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix} \end{array}

Jadi mari kita tulis ulang H^\hat{H} terhadap Pauli dan identitas:

H^=kn1=03...k0=03wkn1...k0σkn1...σk0=k=04n1wkP^k,\hat{H} = \sum_{k_{n-1}=0}^3... \sum_{k_0=0}^3 w_{k_{n-1}...k_0} \sigma_{k_{n-1}}\otimes ... \otimes \sigma_{k_0} = \sum_{k=0}^{4^n-1} w_k \hat{P}_k,

di mana k=l=0n14lklkn1...k0k = \sum_{l=0}^{n-1} 4^l k_l \equiv k_{n-1}...k_0 untuk kn1,...,k0{0,1,2,3}k_{n-1},...,k_0\in \{0,1,2,3\} (yaitu, basis 44), dan P^k:=σkn1...σk0\hat{P}_{k} := \sigma_{k_{n-1}}\otimes ... \otimes \sigma_{k_0}:

ψH^ψ=k=04n1wkj=02n1jVkψ2jΛkj=k=04n1wkj=02n1pkjλkj,\begin{aligned} \langle\psi|\hat{H}|\psi\rangle & = \sum_{k=0}^{4^n-1} w_k \sum_{j=0}^{2^n-1}|\langle j| V_k|\psi\rangle|^2 \langle j| \Lambda_k |j\rangle \\[1mm] & = \sum_{k=0}^{4^n-1} w_k \sum_{j=0}^{2^n-1}p_{kj} \lambda_{kj}, \\[1mm] \end{aligned}

di mana Vk:=Vkn1...Vk0V_k := V_{k_{n-1}}\otimes ... \otimes V_{k_0} dan Λk:=Λkn1...Λk0\Lambda_k := \Lambda_{k_{n-1}}\otimes ... \otimes \Lambda_{k_0}, sehingga: Pk^=VkΛkVk.\hat{P_k}=V_k^\dagger \Lambda_k V_k.

Fungsi biaya

Secara umum, fungsi biaya digunakan untuk menggambarkan tujuan dari suatu masalah dan seberapa baik suatu trial state memenuhi tujuan tersebut. Definisi ini bisa diterapkan ke berbagai contoh dalam kimia, machine learning, keuangan, optimasi, dan lain-lain.

Mari kita lihat contoh sederhana yaitu mencari ground state dari sebuah sistem. Tujuan kita adalah meminimalkan nilai ekspektasi dari observable yang merepresentasikan energi (Hamiltonian H^\hat{\mathcal{H}}):

minθψ(θ)H^ψ(θ)\min_{\vec\theta} \langle\psi(\vec\theta)|\hat{\mathcal{H}}|\psi(\vec\theta)\rangle

Kita bisa menggunakan Estimator untuk mengevaluasi nilai ekspektasi dan meneruskan nilai ini ke optimizer untuk diminimalkan. Jika optimasi berhasil, akan dikembalikan sekumpulan nilai parameter optimal θ\vec\theta^*, dari mana kita bisa membangun solution state yang diusulkan ψ(θ)|\psi(\vec\theta^*)\rangle dan menghitung nilai ekspektasi yang diamati sebagai C(θ)C(\vec\theta^*).

Perhatikan bahwa kita hanya bisa meminimalkan fungsi biaya untuk sekumpulan state terbatas yang sedang kita pertimbangkan. Ini membawa kita pada dua kemungkinan terpisah:

  • Ansatz kita tidak mendefinisikan solution state di seluruh search space: Jika ini kasusnya, optimizer kita tidak akan pernah menemukan solusinya, dan kita perlu bereksperimen dengan ansatz lain yang mungkin bisa merepresentasikan search space kita dengan lebih akurat.
  • Optimizer kita tidak bisa menemukan solusi yang valid ini: Optimasi bisa didefinisikan secara global dan secara lokal. Kita akan mengeksplorasi apa artinya ini di bagian selanjutnya.

Pada akhirnya, kita akan menjalankan loop optimasi klasik tapi bergantung pada evaluasi fungsi biaya ke komputer kuantum. Dari perspektif ini, seseorang bisa menganggap optimasi sebagai usaha yang murni klasik di mana kita memanggil beberapa black-box quantum oracle setiap kali optimizer perlu mengevaluasi fungsi biaya.

def cost_func_vqe(params, circuit, hamiltonian, estimator):
"""Return estimate of energy from estimator

Parameters:
params (ndarray): Array of ansatz parameters
ansatz (QuantumCircuit): Parameterized ansatz circuit
hamiltonian (SparsePauliOp): Operator representation of Hamiltonian
estimator (Estimator): Estimator primitive instance

Returns:
float: Energy estimate
"""
pub = (circuit, hamiltonian, params)
cost = estimator.run([pub]).result()[0].data.evs
return cost
from qiskit.circuit.library import TwoLocal

observable = SparsePauliOp.from_list([("XX", 1), ("YY", -3)])

reference_circuit = QuantumCircuit(2)
reference_circuit.x(0)

variational_form = TwoLocal(
2,
rotation_blocks=["rz", "ry"],
entanglement_blocks="cx",
entanglement="linear",
reps=1,
)
ansatz = reference_circuit.compose(variational_form)

theta_list = (2 * np.pi * np.random.rand(1, 8)).tolist()
ansatz.decompose().draw("mpl")

Output of the previous code cell

Pertama kita akan melakukan ini menggunakan simulator: StatevectorEstimator. Ini biasanya disarankan untuk debugging, tapi kita akan segera melanjutkan debugging run dengan perhitungan pada hardware kuantum nyata. Semakin lama, masalah-masalah yang menarik sudah tidak bisa lagi disimulasikan secara klasik tanpa fasilitas superkomputer mutakhir.

estimator = StatevectorEstimator()
cost = cost_func_vqe(theta_list, ansatz, observable, estimator)
print(cost)
[-0.58744589]

Sekarang kita akan melanjutkan dengan menjalankan pada komputer kuantum nyata. Perhatikan perubahan sintaksis. Langkah-langkah yang melibatkan pass_manager akan dibahas lebih lanjut di contoh berikutnya. Salah satu langkah yang sangat penting dalam algoritma variasional adalah penggunaan Qiskit Runtime Session. Memulai sebuah Session memungkinkan kamu menjalankan beberapa iterasi dari algoritma variasional tanpa harus menunggu di antrian baru setiap kali parameter diperbarui. Ini penting jika waktu antrian panjang dan/atau banyak iterasi diperlukan. Hanya mitra di IBM Quantum® Network yang bisa menggunakan Runtime Session. Jika kamu tidak punya akses ke Session, kamu bisa mengurangi jumlah iterasi yang kamu submit pada suatu waktu, dan menyimpan parameter terbaru untuk digunakan di run mendatang. Jika kamu menyubmit terlalu banyak iterasi atau menghadapi waktu antrian yang terlalu panjang, kamu mungkin akan mendapat kode error 1217, yang merujuk pada penundaan lama antara pengiriman job.

# Estimated usage: < 1 min. Benchmarked at 7 seconds on an Eagle processor
# Load necessary packages:

from qiskit_ibm_runtime import (
QiskitRuntimeService,
Session,
EstimatorOptions,
EstimatorV2 as Estimator,
)
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

# Select the least busy backend:

service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, min_num_qubits=ansatz.num_qubits, simulator=False
)
# Or get a specific backend:
# backend = service.backend("ibm_brisbane")

# Use a pass manager to transpile the circuit and observable for the specific backend being used:

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_ansatz = pm.run(ansatz)
isa_observable = observable.apply_layout(layout=isa_ansatz.layout)

# Set estimator options
estimator_options = EstimatorOptions(resilience_level=1, default_shots=10_000)

# Open a Runtime session:

with Session(backend=backend) as session:
estimator = Estimator(mode=session, options=estimator_options)
cost = cost_func_vqe(theta_list, isa_ansatz, isa_observable, estimator)

session.close()
print(cost)

Perhatikan bahwa nilai yang diperoleh dari dua perhitungan di atas sangat mirip. Teknik untuk meningkatkan hasil akan dibahas lebih lanjut di bawah.

Contoh pemetaan ke sistem non-fisik

Masalah maximum cut (Max-Cut) adalah masalah optimasi kombinatorial yang melibatkan pembagian simpul-simpul sebuah graf menjadi dua himpunan disjoint sedemikian sehingga jumlah sisi antara dua himpunan dimaksimalkan. Lebih formal, diberikan graf tak berarah G=(V,E)G=(V,E), di mana VV adalah himpunan simpul dan EE adalah himpunan sisi, masalah Max-Cut meminta untuk mempartisi simpul menjadi dua himpunan bagian disjoint, SS dan TT, sedemikian sehingga jumlah sisi dengan satu titik ujung di SS dan titik ujung lainnya di TT dimaksimalkan.

Kita bisa menerapkan Max-Cut untuk memecahkan berbagai masalah termasuk: clustering, desain jaringan, transisi fase, dan lain-lain. Kita akan mulai dengan membuat graf masalah:

import rustworkx as rx
from rustworkx.visualization import mpl_draw

n = 4
G = rx.PyGraph()
G.add_nodes_from(range(n))
# The edge syntax is (start, end, weight)
edges = [(0, 1, 1.0), (0, 2, 1.0), (0, 3, 1.0), (1, 2, 1.0), (2, 3, 1.0)]
G.add_edges_from(edges)

mpl_draw(
G, pos=rx.shell_layout(G), with_labels=True, edge_labels=str, node_color="#1192E8"
)

Output of the previous code cell

Masalah ini bisa dinyatakan sebagai masalah optimasi biner. Untuk setiap simpul 0i<n0 \leq i < n, di mana nn adalah jumlah simpul pada graf (dalam hal ini n=4n=4), kita akan mempertimbangkan variabel biner xix_i. Variabel ini akan bernilai 11 jika simpul ii berada di salah satu kelompok yang kita beri label 11 dan 00 jika berada di kelompok lain, yang kita beri label 00. Kita juga akan menandai wijw_{ij} (elemen (i,j)(i,j) dari matriks ketetanggaan ww) sebagai bobot sisi yang menghubungkan simpul ii ke simpul jj. Karena grafnya tak berarah, wij=wjiw_{ij}=w_{ji}. Maka kita bisa merumuskan masalah kita sebagai memaksimalkan fungsi biaya berikut:

C(x)=i,j=0nwijxi(1xj)=i,j=0nwijxii,j=0nwijxixj=i,j=0nwijxii=0nj=0i2wijxixj\begin{aligned} C(\vec{x}) & =\sum_{i,j=0}^n w_{ij} x_i(1-x_j)\\[1mm] & = \sum_{i,j=0}^n w_{ij} x_i - \sum_{i,j=0}^n w_{ij} x_ix_j\\[1mm] & = \sum_{i,j=0}^n w_{ij} x_i - \sum_{i=0}^n \sum_{j=0}^i 2w_{ij} x_ix_j \end{aligned}

Untuk memecahkan masalah ini dengan komputer kuantum, kita akan menyatakan fungsi biaya sebagai nilai ekspektasi dari sebuah observable. Namun, observable yang diterima Qiskit secara native terdiri dari operator Pauli, yang memiliki nilai eigen 11 dan 1-1 bukannya 00 dan 11. Itulah mengapa kita akan melakukan perubahan variabel berikut:

Di mana x=(x0,x1,,xn1)\vec{x}=(x_0,x_1,\cdots ,x_{n-1}). Kita bisa menggunakan matriks ketetanggaan ww untuk mengakses bobot semua sisi dengan nyaman. Ini akan digunakan untuk mendapatkan fungsi biaya kita:

zi=12xixi=1zi2z_i = 1-2x_i \rightarrow x_i = \frac{1-z_i}{2}

Ini berarti:

xi=0zi=1xi=1zi=1.\begin{array}{lcl} x_i=0 & \rightarrow & z_i=1 \\ x_i=1 & \rightarrow & z_i=-1.\end{array}

Jadi fungsi biaya baru yang ingin kita maksimalkan adalah:

C(z)=i,j=0nwij(1zi2)(11zj2)=i,j=0nwij4i,j=0nwij4zizj=i=0nj=0iwij2i=0nj=0iwij2zizj\begin{aligned} C(\vec{z}) & = \sum_{i,j=0}^n w_{ij} \bigg(\frac{1-z_i}{2}\bigg)\bigg(1-\frac{1-z_j}{2}\bigg)\\[1mm] & = \sum_{i,j=0}^n \frac{w_{ij}}{4} - \sum_{i,j=0}^n \frac{w_{ij}}{4} z_iz_j\\[1mm] & = \sum_{i=0}^n \sum_{j=0}^i \frac{w_{ij}}{2} - \sum_{i=0}^n \sum_{j=0}^i \frac{w_{ij}}{2} z_iz_j \end{aligned}

Selain itu, kecenderungan alami komputer kuantum adalah mencari minima (biasanya energi terendah) bukannya maksima, sehingga alih-alih memaksimalkan C(z)C(\vec{z}) kita akan meminimalkan:

C(z)=i=0nj=0iwij2zizji=0nj=0iwij2-C(\vec{z}) = \sum_{i=0}^n \sum_{j=0}^i \frac{w_{ij}}{2} z_iz_j - \sum_{i=0}^n \sum_{j=0}^i \frac{w_{ij}}{2}

Sekarang kita punya fungsi biaya untuk diminimalkan yang variabelnya bisa bernilai 1-1 dan 11, kita bisa membuat analogi berikut dengan Pauli ZZ:

ziZi=In1...Zi...I0z_i \equiv Z_i = \overbrace{I}^{n-1}\otimes ... \otimes \overbrace{Z}^{i} \otimes ... \otimes \overbrace{I}^{0}

Dengan kata lain, variabel ziz_i akan setara dengan Gate ZZ yang bekerja pada Qubit ii. Selain itu:

Zixn1x0=zixn1x0xn1x0Zixn1x0=ziZ_i|x_{n-1}\cdots x_0\rangle = z_i|x_{n-1}\cdots x_0\rangle \rightarrow \langle x_{n-1}\cdots x_0 |Z_i|x_{n-1}\cdots x_0\rangle = z_i

Maka observable yang akan kita pertimbangkan adalah:

H^=i=0nj=0iwij2ZiZj\hat{H} = \sum_{i=0}^n \sum_{j=0}^i \frac{w_{ij}}{2} Z_iZ_j

yang harus kita tambahkan suku konstanta setelahnya:

offset=i=0nj=0iwij2\texttt{offset} = - \sum_{i=0}^n \sum_{j=0}^i \frac{w_{ij}}{2}

Operatornya adalah kombinasi linear dari suku-suku dengan operator Z pada simpul yang terhubung oleh sisi (ingat bahwa Qubit ke-0 paling kanan): IIZZ+IZIZ+IZZI+ZIIZ+ZZIIIIZZ + IZIZ + IZZI + ZIIZ + ZZII. Setelah operator terbentuk, ansatz untuk algoritma QAOA bisa dengan mudah dibangun menggunakan Circuit QAOAAnsatz dari pustaka Circuit Qiskit.

from qiskit.circuit.library import QAOAAnsatz
from qiskit.quantum_info import SparsePauliOp

hamiltonian = SparsePauliOp.from_list(
[("IIZZ", 1), ("IZIZ", 1), ("IZZI", 1), ("ZIIZ", 1), ("ZZII", 1)]
)

ansatz = QAOAAnsatz(hamiltonian, reps=2)
# Draw
ansatz.decompose(reps=3).draw("mpl")

Output of the previous code cell

# Sum the weights, and divide by 2

offset = -sum(edge[2] for edge in edges) / 2
print(f"""Offset: {offset}""")
Offset: -2.5

Dengan Estimator Runtime yang langsung menerima Hamiltonian dan ansatz berparameter, serta mengembalikan energi yang diperlukan, fungsi biaya untuk instance QAOA cukup sederhana:

def cost_func(params, ansatz, hamiltonian, estimator):
"""Return estimate of energy from estimator

Parameters:
params (ndarray): Array of ansatz parameters
ansatz (QuantumCircuit): Parameterized ansatz circuit
hamiltonian (SparsePauliOp): Operator representation of Hamiltonian
estimator (Estimator): Estimator primitive instance

Returns:
float: Energy estimate
"""
pub = (ansatz, hamiltonian, params)
cost = estimator.run([pub]).result()[0].data.evs
# cost = estimator.run(ansatz, hamiltonian, parameter_values=params).result().values[0]
return cost
import numpy as np

x0 = 2 * np.pi * np.random.rand(ansatz.num_parameters)

estimator = StatevectorEstimator()
cost = cost_func_vqe(x0, ansatz, hamiltonian, estimator)
print(cost)
1.473098768180865
# Estimated usage: < 1 min, benchmarked at 6 seconds on ibm_osaka, 5-23-24
# Load some necessary packages:

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

# Select the least busy backend:

backend = service.least_busy(
operational=True, min_num_qubits=ansatz.num_qubits, simulator=False
)

# Or get a specific backend:
# backend = service.backend("ibm_brisbane")

# Use a pass manager to transpile the circuit and observable for the specific backend being used:

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_ansatz = pm.run(ansatz)
isa_hamiltonian = hamiltonian.apply_layout(layout=isa_ansatz.layout)

# Set estimator options
estimator_options = EstimatorOptions(resilience_level=1, default_shots=10_000)

# Open a Runtime session:

with Session(backend=backend) as session:
estimator = Estimator(mode=session, options=estimator_options)
cost = cost_func_vqe(x0, isa_ansatz, isa_hamiltonian, estimator)

# Close session after done
session.close()
print(cost)
1.1120776913677988

Kita akan kembali ke contoh ini di bagian Aplikasi untuk mengeksplorasi cara memanfaatkan optimizer untuk beriterasi melalui search space. Secara umum, ini meliputi:

  • Memanfaatkan optimizer untuk menemukan parameter optimal
  • Mengikat parameter optimal ke ansatz untuk menemukan nilai eigen
  • Menerjemahkan nilai eigen ke definisi masalah kita

Strategi pengukuran: kecepatan versus akurasi

Seperti yang disebutkan, kita menggunakan komputer kuantum yang bising sebagai black-box oracle, di mana noise bisa membuat nilai yang diambil menjadi non-deterministik, menyebabkan fluktuasi acak yang pada gilirannya akan menghambat — atau bahkan sepenuhnya mencegah — konvergensi optimizer tertentu ke solusi yang diusulkan. Ini adalah masalah umum yang harus kita tangani saat kita secara bertahap mengeksplorasi quantum utility dan berkembang menuju quantum advantage:

A graph showing how simulation cost varies with circuit complexity. Using a classical computer it grows exponentially. With quantum error mitigation, there should be a crossover at which that becomes advantageous. Quantum error correction allows for linear growth of the simulation cost and will certainly lead to advantage.

Kita bisa menggunakan opsi error suppression dan error mitigation dari Qiskit Runtime Primitive untuk mengatasi noise dan memaksimalkan utilitas komputer kuantum saat ini.

Error Suppression

Error suppression mengacu pada teknik yang digunakan untuk mengoptimalkan dan mentransformasi Circuit selama kompilasi agar kesalahan diminimalkan. Ini adalah teknik penanganan error dasar yang biasanya menghasilkan beberapa overhead pra-pemrosesan klasik pada keseluruhan runtime. Overhead tersebut mencakup transpilasi Circuit untuk dijalankan pada hardware kuantum dengan cara:

  • Mengekspresikan Circuit menggunakan gate native yang tersedia pada sistem kuantum
  • Memetakan virtual Qubit ke Qubit fisik
  • Menambahkan SWAP berdasarkan persyaratan konektivitas
  • Mengoptimalkan gate 1Q dan 2Q
  • Menambahkan dynamical decoupling pada Qubit yang idle untuk mencegah efek dekoherensi.

Primitives memungkinkan penggunaan teknik error suppression dengan mengatur opsi optimization_level dan memilih opsi transpilasi lanjutan. Di kursus berikutnya, kita akan mendalami berbagai metode konstruksi Circuit untuk meningkatkan hasil, tapi untuk kebanyakan kasus, kami menyarankan mengatur optimization_level=3.

Kita akan memvisualisasikan nilai dari peningkatan optimasi dalam proses transpilasi dengan melihat contoh Circuit dengan perilaku ideal yang sederhana.

from qiskit.circuit import Parameter, QuantumCircuit
from qiskit.quantum_info import SparsePauliOp

theta = Parameter("theta")

qc = QuantumCircuit(2)
qc.x(1)
qc.h(0)
qc.cp(theta, 0, 1)
qc.h(0)
observables = SparsePauliOp.from_list([("ZZ", 1)])

qc.draw("mpl")

Output of the previous code cell

Circuit di atas bisa menghasilkan nilai ekspektasi sinusoidal dari observable yang diberikan, asalkan kita memasukkan fase yang mencakup interval yang sesuai, seperti [0,2π][0,2\pi].

## Setup phases
import numpy as np

phases = np.linspace(0, 2 * np.pi, 50)

# phases need to be expressed as a list of lists in order to work
individual_phases = [[phase] for phase in phases]

Kita bisa menggunakan simulator untuk menunjukkan kegunaan transpilasi yang dioptimalkan. Kita akan kembali di bawah ini menggunakan hardware nyata untuk mendemonstrasikan kegunaan error mitigation. Kita akan menggunakan QiskitRuntimeService untuk mendapatkan Backend nyata (dalam hal ini, ibm_brisbane), dan menggunakan AerSimulator untuk menyimulasikan Backend tersebut, termasuk perilaku noise-nya.

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_aer import AerSimulator

# get a real backend from the runtime service
service = QiskitRuntimeService()
backend = service.backend("ibm_brisbane")

# generate a simulator that mimics the real quantum system with the latest calibration results
backend_sim = AerSimulator.from_backend(backend)

Kita sekarang bisa menggunakan pass manager untuk mentranspilasi Circuit ke dalam "instruction set architecture" atau ISA dari Backend. Ini adalah persyaratan baru di Qiskit Runtime: semua Circuit yang disubmit ke Backend harus sesuai dengan batasan target Backend, artinya harus ditulis dalam hal ISA Backend — yaitu, kumpulan instruksi yang bisa dipahami dan dieksekusi oleh perangkat. Batasan target ini ditentukan oleh faktor-faktor seperti gate basis native perangkat, konektivitas Qubit-nya, dan — bila relevan — spesifikasi timing pulse dan instruksi lainnya.

Perhatikan bahwa dalam kasus ini, kita akan melakukannya dua kali: sekali dengan optimization_level = 0, dan sekali dengan nilainya diatur ke 3. Setiap kali kita akan menggunakan primitif Estimator untuk memperkirakan nilai ekspektasi dari observable pada berbagai nilai fase.

# Import estimator and specify that we are using the simulated backend:

from qiskit_ibm_runtime import EstimatorV2 as Estimator

estimator = Estimator(mode=backend_sim)

circuit = qc
# Use a pass manager to transpile the circuit and observable for the backend being simulated.
# Start with no optimization:

from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

pm = generate_preset_pass_manager(backend=backend_sim, optimization_level=0)
isa_circuit = pm.run(circuit)
isa_observables = observables.apply_layout(layout=isa_circuit.layout)

noisy_exp_values = []
pub = (isa_circuit, isa_observables, [individual_phases])
cost = estimator.run([pub]).result()[0].data.evs
noisy_exp_values = cost[0]

# Repeat above steps, but now with optimization = 3:

exp_values_with_opt_es = []
pm = generate_preset_pass_manager(backend=backend_sim, optimization_level=3)
isa_circuit = pm.run(circuit)
isa_observables = observables.apply_layout(layout=isa_circuit.layout)

pub = (isa_circuit, isa_observables, [individual_phases])
cost = estimator.run([pub]).result()[0].data.evs
exp_values_with_opt_es = cost[0]

Akhirnya, kita bisa memplot hasilnya, dan kita lihat bahwa presisi perhitungan cukup baik bahkan tanpa optimasi, tapi jelas meningkat dengan meningkatkan optimasi ke level 3. Perhatikan bahwa pada Circuit yang lebih dalam dan lebih kompleks, perbedaan antara level optimasi 0 dan 3 kemungkinan akan lebih signifikan. Ini adalah Circuit yang sangat sederhana yang digunakan sebagai model mainan.

import matplotlib.pyplot as plt

plt.plot(phases, noisy_exp_values, "o", label="opt=0")
plt.plot(phases, exp_values_with_opt_es, "o", label="opt=3")
plt.plot(phases, 2 * np.sin(phases / 2) ** 2 - 1, label="ideal")
plt.ylabel("Expectation")
plt.legend()
plt.show()

Output of the previous code cell

Error Mitigation

Error mitigation mengacu pada teknik yang memungkinkan pengguna untuk mengurangi error Circuit dengan memodelkan noise perangkat pada saat eksekusi. Biasanya, ini menghasilkan overhead pra-pemrosesan kuantum yang berkaitan dengan pelatihan model dan overhead pasca-pemrosesan klasik untuk memitigasi error dalam hasil mentah menggunakan model yang dihasilkan.

Opsi resilience_level dari primitif Qiskit Runtime menentukan jumlah ketahanan yang dibangun terhadap error. Level yang lebih tinggi menghasilkan hasil yang lebih akurat dengan biaya waktu pemrosesan yang lebih lama akibat overhead sampling kuantum. Level resilience bisa digunakan untuk mengonfigurasi trade-off antara biaya dan akurasi saat menerapkan error mitigation pada query primitif kamu.

Saat mengimplementasikan teknik error mitigation apa pun, kita harapkan bias dalam hasil kita berkurang dibandingkan dengan bias sebelumnya yang tidak dimitigasi. Dalam beberapa kasus, bias bahkan bisa hilang. Namun, ini ada biayanya. Saat kita mengurangi bias dalam kuantitas yang kita estimasi, variabilitas statistik akan meningkat (yaitu, varians), yang bisa kita kompensasi dengan lebih meningkatkan jumlah shot per Circuit dalam proses sampling kita. Ini akan memperkenalkan overhead di luar yang diperlukan untuk mengurangi bias, jadi tidak dilakukan secara default. Kita bisa dengan mudah memilih perilaku ini dengan menyesuaikan jumlah shot per Circuit di options.executions.shots, seperti yang ditunjukkan dalam contoh di bawah ini.

A diagram showing broader or narrowing distributions as in the bias/variance tradeoff.

Untuk kursus ini, kita akan mengeksplorasi model error mitigation ini pada level tinggi untuk mengilustrasikan error mitigation yang bisa dilakukan oleh primitif Qiskit Runtime tanpa memerlukan detail implementasi penuh.

Twirled readout error extinction (T-REx)

Twirled readout error extinction (T-REx) menggunakan teknik yang dikenal sebagai Pauli twirling untuk mengurangi noise yang diperkenalkan selama proses pengukuran kuantum. Teknik ini tidak mengasumsikan bentuk noise tertentu, yang membuatnya sangat umum dan efektif.

Alur kerja keseluruhan:

  1. Ambil data untuk zero state dengan bit flip yang diacak (Pauli X sebelum pengukuran)
  2. Ambil data untuk state yang diinginkan (bising) dengan bit flip yang diacak (Pauli X sebelum pengukuran)
  3. Hitung fungsi khusus untuk setiap kumpulan data, dan bagi.

 

A diagram showing measurement and calibration circuits for T-REX.

Kita bisa mengaturnya dengan options.resilience_level = 1, yang didemonstrasikan dalam contoh di bawah ini.

Zero noise extrapolation

Zero noise extrapolation (ZNE) bekerja dengan pertama-tama mengamplifikasi noise dalam Circuit yang menyiapkan quantum state yang diinginkan, mendapatkan pengukuran untuk beberapa level noise yang berbeda, dan menggunakan pengukuran tersebut untuk menyimpulkan hasil tanpa noise.

Alur kerja keseluruhan:

  1. Amplifikasi noise Circuit untuk beberapa faktor noise
  2. Jalankan setiap Circuit yang diamplifikasi noise-nya
  3. Ekstrapolasi kembali ke batas zero noise

 

A diagram showing steps in ZNE. Noise is artificially amplified by different factors. Then the values are extrapolated to what they should be at zero noise.

Kita bisa mengaturnya dengan options.resilience_level = 2. Kita bisa mengoptimalkannya lebih lanjut dengan mengeksplorasi berbagai noise_factors, noise_amplifiers, dan extrapolators, tapi ini di luar cakupan kursus ini. Kami mendorong kamu untuk bereksperimen dengan opsi-opsi ini seperti yang dijelaskan di sini.

Setiap metode memiliki overhead terkait masing-masing: trade-off antara jumlah komputasi kuantum yang diperlukan (waktu) dan akurasi hasil kita:

MethodsR=1, T-RExR=2, ZNEAssumptionsNoneAbility to scale noiseQubit overhead11Sampling overhead2Nnoise-factorsBias0O(λNnoise-factors)\begin{array}{c|c|c|c} \text{Methods} & R=1 \text{, T-REx} & R=2 \text{, ZNE} \\[1mm] \hline \text{Assumptions} & \text{None} & \text{Ability to scale noise} \\[1mm] \text{Qubit overhead} & 1 & 1 \\[1mm] \text{Sampling overhead} & 2 & N_{\text{noise-factors}} \\[1mm] \text{Bias} & 0 & \mathcal{O}(\lambda^{N_{\text{noise-factors}}}) \\[1mm] \end{array}

Menggunakan opsi mitigasi dan supresi Qiskit Runtime

Berikut cara menghitung nilai ekspektasi sambil menggunakan error mitigation dan suppression di Qiskit Runtime. Kita bisa menggunakan Circuit dan observable yang persis sama seperti sebelumnya, tapi kali ini menjaga optimization level tetap di level 2, dan sekarang menyetel resilience atau teknik error mitigation yang digunakan. Proses error mitigation ini terjadi beberapa kali sepanjang loop optimasi.

Kita melakukan bagian ini pada hardware nyata, karena error mitigation tidak tersedia pada simulator.

# Estimated usage: 8 minutes, benchmarked on an Eagle processor, 5-23-24

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import (
Session,
EstimatorOptions,
EstimatorV2 as Estimator,
)

# We select the least busy backend

# Select the least busy backend
# backend = service.least_busy(
# operational=True, min_num_qubits=ansatz.num_qubits, simulator=False
# )

# Or use a specific backend
backend = service.backend("ibm_brisbane")

# Initialize some variables to save the results from different runs:

exp_values_with_em0_es = []
exp_values_with_em1_es = []
exp_values_with_em2_es = []

# Use a pass manager to optimize the circuit and observables for the backend chosen:

pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
isa_circuit = pm.run(circuit)
isa_observables = observables.apply_layout(layout=isa_circuit.layout)

# Open a session and run with no error mitigation:

estimator_options = EstimatorOptions(resilience_level=0, default_shots=10_000)

with Session(backend=backend) as session:
estimator = Estimator(mode=session, options=estimator_options)

pub = (isa_circuit, isa_observables, [individual_phases])
cost = estimator.run([pub]).result()[0].data.evs

session.close()

exp_values_with_em0_es = cost[0]

# Open a session and run with resilience = 1:

estimator_options = EstimatorOptions(resilience_level=1, default_shots=10_000)

with Session(backend=backend) as session:
estimator = Estimator(mode=session, options=estimator_options)

pub = (isa_circuit, isa_observables, [individual_phases])
cost = estimator.run([pub]).result()[0].data.evs

session.close()

exp_values_with_em1_es = cost[0]

# Open a session and run with resilience = 2:

estimator_options = EstimatorOptions(resilience_level=2, default_shots=10_000)

with Session(backend=backend) as session:
estimator = Estimator(mode=session, options=estimator_options)

pub = (isa_circuit, isa_observables, [individual_phases])
cost = estimator.run([pub]).result()[0].data.evs

session.close()

exp_values_with_em2_es = cost[0]

Seperti sebelumnya, kita bisa memplot nilai ekspektasi yang dihasilkan sebagai fungsi sudut fase untuk tiga level error mitigation yang digunakan. Dengan susah payah, seseorang bisa melihat bahwa error mitigation sedikit meningkatkan hasilnya. Sekali lagi, efek ini jauh lebih terlihat pada Circuit yang lebih dalam dan lebih kompleks.

import matplotlib.pyplot as plt

plt.plot(phases, exp_values_with_em0_es, "o", label="unmitigated")
plt.plot(phases, exp_values_with_em1_es, "o", label="resil = 1")
plt.plot(phases, exp_values_with_em2_es, "o", label="resil = 2")
plt.plot(phases, 2 * np.sin(phases / 2) ** 2 - 1, label="ideal")
plt.ylabel("Expectation")
plt.legend()
plt.show()

Output of the previous code cell

Ringkasan

Dengan pelajaran ini, kamu belajar cara membuat fungsi biaya:

  • Membuat fungsi biaya
  • Cara memanfaatkan primitif Qiskit Runtime untuk memitigasi dan menekan noise
  • Cara mendefinisikan strategi pengukuran untuk mengoptimalkan kecepatan vs akurasi

Berikut workload variasional kita secara high-level:

A diagram showing the quantum circuit with unitaries preparing the reference state and variational state, followed by measurements. These are used to evaluate the cost function.

Fungsi biaya kita berjalan selama setiap iterasi dari loop optimasi. Pelajaran berikutnya akan mengeksplorasi bagaimana optimizer klasik menggunakan evaluasi fungsi biaya kita untuk memilih parameter baru.

import qiskit
import qiskit_ibm_runtime

print(qiskit.version.get_version_info())
print(qiskit_ibm_runtime.version.get_version_info())
1.1.0
0.23.0
Source: IBM Quantum docs — updated 5 Mei 2026
English version on doQumentation — updated 7 Mei 2026
This translation based on the English version of approx. 27 Mar 2026