Lumaktaw sa pangunahing nilalaman

Mga cost function

Sa araling ito, matututunan natin kung paano suriin ang isang cost function:

  • Una, matututunan natin ang tungkol sa Qiskit Runtime primitives
  • I-define ang isang cost function na C(θ)C(\vec\theta). Ito ay isang function na naka-depende sa problema na nagtatakda ng layunin ng problema para mapakinabangan (o mapinalaki) ng optimizer
  • Pagtatakda ng estratehiya sa pagsukat gamit ang Qiskit Runtime primitives upang ma-optimize ang bilis kumpara sa katumpakan

 

Isang diagram na nagpapakita ng mga pangunahing bahagi ng isang cost function kabilang ang paggamit ng mga primitive tulad ng estimator at sampler.

Mga Primitive

Ang lahat ng pisikal na sistema, maging klasikal man o quantum, ay maaaring umiral sa iba't ibang estado. Halimbawa, ang isang sasakyan sa daan ay maaaring magkaroon ng tiyak na masa, posisyon, bilis, o akselerasyon na nagpapakilala ng kanyang estado. Katulad nito, ang mga quantum na sistema ay maaari ring magkaroon ng iba't ibang konpigurasyon o estado, ngunit naiiba ang mga ito sa mga klasikal na sistema pagdating sa pagsukat at ebolusyon ng estado. Nagbubunga ito ng natatanging katangian tulad ng superposition at entanglement na eksklusibo sa quantum mechanics. Tulad ng paglalarawan sa estado ng sasakyan gamit ang mga pisikal na katangian tulad ng bilis o akselerasyon, maaari rin nating ilarawan ang estado ng isang quantum na sistema gamit ang mga observable, na mga bagay na matematikal.

Sa quantum mechanics, ang mga estado ay kinakatawan ng mga normalized na kumplikadong column vector, o mga ket (ψ|\psi\rangle), at ang mga observable ay mga Hermitian linear operator (H^=H^\hat{H}=\hat{H}^{\dagger}) na kumikilos sa mga ket. Ang isang eigenvector (λ|\lambda\rangle) ng isang observable ay kilala bilang eigenstate. Ang pagsukat ng isang observable para sa isa sa mga eigenstate nito (λ|\lambda\rangle) ay magbibigay sa atin ng kaukulang eigenvalue (λ\lambda) bilang resulta.

Kung nag-iisip ka kung paano sukatin ang isang quantum na sistema at kung ano ang maaaring sukatin, nag-aalok ang Qiskit ng dalawang primitive na makakatulong:

  • Sampler: Binibigyan ng isang quantum na estado na ψ|\psi\rangle, kinukuha ng primitive na ito ang posibilidad ng bawat posibleng computational basis state.
  • Estimator: Binibigyan ng isang quantum observable na H^\hat{H} at isang estado na ψ|\psi\rangle, kinakalkula ng primitive na ito ang inaasahang halaga ng H^\hat{H}.

Ang Sampler primitive

Ang Sampler primitive ay kinakalkula ang posibilidad ng pagkuha ng bawat posibleng estado na k|k\rangle mula sa computational basis, binibigyan ng quantum circuit na naghahanda ng estado na ψ|\psi\rangle. Kinakalkula nito ang

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\},

Kung saan ang nn ay ang bilang ng mga qubit, at ang kk ang integer na representasyon ng anumang posibleng output binary string na {0,1}n\{0,1\}^n (iyon ay, mga integer base 22).

Ang Qiskit Runtime Sampler ay nagpapatakbo ng circuit nang maraming beses sa isang quantum device, nagsasagawa ng mga pagsukat sa bawat takbo, at nagtatayo muli ng probability distribution mula sa mga nakuhang bitstring. Habang mas maraming takbo (o shot) ang isinasagawa nito, mas tumpak ang mga resulta, ngunit nangangailangan ito ng mas maraming oras at quantum na mapagkukunan.

Gayunpaman, dahil ang bilang ng mga posibleng output ay lumalaki nang eksponensyal sa bilang ng mga qubit na nn (iyon ay, 2n2^n), ang bilang ng mga shot ay kailangang lumaki rin nang eksponensyal upang makuha ang isang siksik na probability distribution. Samakatuwid, ang Sampler ay epektibo lamang para sa mga maluwag na probability distribution; kung saan ang target na estado na ψ|\psi\rangle ay dapat maipahayag bilang isang linear na kombinasyon ng mga computational basis state, na ang bilang ng mga termino ay lumalaki nang hindi hihigit sa polynomial sa bilang ng mga qubit:

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

Ang Sampler ay maaari ring i-configure upang makuha ang mga posibilidad mula sa isang subseksyon ng circuit, na kumakatawan sa isang subset ng kabuuang posibleng mga estado.

Ang Estimator primitive

Ang Estimator primitive ay kinakalkula ang inaasahang halaga ng isang observable na H^\hat{H} para sa isang quantum na estado na ψ|\psi\rangle; kung saan ang mga posibilidad ng observable ay maaaring ipahayag bilang pλ=λψ2p_\lambda = |\langle\lambda|\psi\rangle|^2, na ang λ|\lambda\rangle ay mga eigenstate ng observable na H^\hat{H}. Ang inaasahang halaga ay tinukoy bilang average ng lahat ng posibleng kinalabasan na λ\lambda (iyon ay, ang mga eigenvalue ng observable) ng isang pagsukat ng estado na ψ|\psi\rangle, na may timbang ayon sa kaukulang mga posibilidad:

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

Gayunpaman, ang pagkalkula ng inaasahang halaga ng isang observable ay hindi palaging posible, dahil madalas ay hindi natin alam ang eigenbasis nito. Ang Qiskit Runtime Estimator ay gumagamit ng isang kumplikadong algebraic na proseso upang tantiyahin ang inaasahang halaga sa isang tunay na quantum device sa pamamagitan ng pagbasag ng observable sa kombinasyon ng iba pang mga observable na alam na natin ang eigenbasis.

Sa mas simpleng salita, binabasag ng Estimator ang anumang observable na hindi nito alam kung paano sukatin sa mas simpleng, masusukat na mga observable na tinatawag na Pauli operator. Ang anumang operator ay maaaring ipahayag bilang kombinasyon ng 4n4^n na Pauli operator.

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\}, \\

kung kaya

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

kung saan ang nn ay ang bilang ng mga qubit, ang kkn1k0k \equiv k_{n-1} \cdots k_0 para sa klZ4{0,1,2,3}k_l \in \mathbb{Z}_4 \equiv \{0, 1, 2, 3\} (iyon ay, mga integer base 44), at (σ0,σ1,σ2,σ3):=(I,X,Y,Z)(\sigma_0, \sigma_1, \sigma_2, \sigma_3) := (I, X, Y, Z).

Pagkatapos isagawa ang decomposition na ito, nagmumula ang Estimator ng bagong circuit na VkψV_k|\psi\rangle para sa bawat observable na P^k\hat{P}_k (mula sa orihinal na circuit), upang epektibong i-diagonalize ang Pauli observable sa computational basis at sukatin ito. Madali nating masusukat ang mga Pauli observable dahil alam natin ang VkV_k nang maaga, na hindi palaging ganoon para sa ibang mga observable.

Para sa bawat P^k\hat{P}_{k}, ang Estimator ay nagpapatakbo ng kaukulang circuit sa isang quantum device nang maraming beses, sinusukat ang output state sa computational basis, at kinakalkula ang posibilidad na pkjp_{kj} ng pagkuha ng bawat posibleng output na jj. Pagkatapos, hinahanap nito ang eigenvalue na λkj\lambda_{kj} ng PkP_k na kaukulang sa bawat output na jj, pinarami ng wkw_k, at isinasama ang lahat ng resulta upang makuha ang inaasahang halaga ng observable na H^\hat{H} para sa ibinigay na estado na ψ|\psi\rangle.

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},

Dahil ang pagkalkula ng inaasahang halaga ng 4n4^n na Pauli ay hindi praktikal (iyon ay, lumalaki nang eksponensyal), ang Estimator ay epektibo lamang kapag malaki ang bilang ng mga wkw_k na zero (iyon ay, maluwag na Pauli decomposition sa halip na siksik). Pormal na sinasabi natin na, para sa komputing na ito ay mahusay na masolvable, ang bilang ng mga non-zero na termino ay dapat lumaki nang hindi hihigit sa polynomial sa bilang ng mga qubit na nn: H^=kPoly(n)wkP^k.\hat{H} = \sum^{\text{Poly}(n)}_k w_k \hat{P}_k.

Maaaring mapansin ng mambabasa ang implicit na palagay na ang posibilidad na sampling ay kailangan ding maging mahusay tulad ng ipinaliwanag para sa Sampler, na ibig sabihin ay

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}.

Patnubay na halimbawa para sa pagkalkula ng mga inaasahang halaga

Ipagpalagay nating ang single-qubit state na +:=H0=12(0+1)|+\rangle := H|0\rangle = \frac{1}{\sqrt{2}}(|0\rangle + |1\rangle), at observable

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

na may sumusunod na teoretikal na inaasahang halaga na H^+=+H^+=2.\langle\hat{H}\rangle_+ = \langle+|\hat{H}|+\rangle = 2.

Dahil hindi natin alam kung paano sukatin ang observable na ito, hindi natin direktang makalkula ang inaasahang halaga nito, at kailangan nating muling ipahayag ito bilang H^+=2X+Z+\langle\hat{H}\rangle_+ = 2\langle X \rangle_+ - \langle Z \rangle_+ . Maaaring ipakita na ito ay nagbibigay ng parehong resulta sa pamamagitan ng paggawa ng katotohanan na +X+=1\langle+|X|+\rangle = 1, at +Z+=0\langle+|Z|+\rangle = 0.

Tingnan natin kung paano direktang kalkulahin ang X+\langle X \rangle_+ at Z+\langle Z \rangle_+. Dahil ang XX at ZZ ay hindi nagkokomyut (iyon ay, hindi sila nagbabahagi ng parehong eigenbasis), hindi sila maaaring sukatin nang sabay-sabay, kaya kailangan natin ang mga auxiliary circuit:

# 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

Maaari na nating isagawa ang komputasyon nang manu-mano gamit ang Sampler at suriin ang mga resulta sa 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

Matematikal na katatagan (opsyonal)

Ipinahayag ang ψ|\psi\rangle kaugnay ng basis ng mga eigenstate ng H^\hat{H}, ang ψ=λaλλ|\psi\rangle = \sum_\lambda a_\lambda |\lambda\rangle, sumusunod na:

ψ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}

Dahil hindi natin alam ang mga eigenvalue o eigenstate ng target observable na H^\hat{H}, kailangan muna nating isaalang-alang ang diagonalization nito. Dahil ang H^\hat{H} ay Hermitian, mayroon isang unitary transformation na VV kung kaya H^=VΛV,\hat{H}=V^\dagger \Lambda V, kung saan ang Λ\Lambda ay ang diagonal eigenvalue matrix, kaya jΛk=0\langle j | \Lambda | k \rangle = 0 kung jkj\neq k, at jΛj=λj\langle j | \Lambda | j \rangle = \lambda_j.

Ipinapahiwatig nito na ang inaasahang halaga ay maaaring muling isulat bilang:

ψ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}

Dahil kung ang isang sistema ay nasa estado na ϕ=Vψ|\phi\rangle = V |\psi\rangle ang posibilidad ng pagsukat ng j| j\rangle ay pj=jϕ2p_j = |\langle j|\phi \rangle|^2, ang inaasahang halaga sa itaas ay maaaring ipahayag bilang:

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

Napakahalaga na tandaan na ang mga posibilidad ay kinukuha mula sa estado na VψV |\psi\rangle sa halip na ψ|\psi\rangle. Ito ang dahilan kung bakit ganap na kinakailangan ang matrix na VV. Maaari kang magtaka kung paano makuha ang matrix na VV at ang mga eigenvalue na Λ\Lambda. Kung mayroon ka na sa mga eigenvalue, wala nang pangangailangan na gumamit ng quantum computer dahil ang layunin ng mga variational algorithm ay ang paghanap ng mga eigenvalue na ito ng H^\hat{H}.

Sa kabutihang palad, may paraan para makalusot dito: ang anumang 2n×2n2^n \times 2^n na matrix ay maaaring isulat bilang isang linear na kombinasyon ng 4n4^n tensor product ng nn na Pauli matrix at mga identity, lahat ay parehong hermitian at unitary na may kilalang VV at Λ\Lambda. Ito ang ginagawa ng Runtime's Estimator sa loob nito sa pamamagitan ng pagbasag ng anumang Operator object sa isang SparsePauliOp.

Narito ang mga Operator na maaaring gamitin:

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}

Kaya muling isulat natin ang H^\hat{H} kaugnay ng mga Pauli at identity:

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,

kung saan ang k=l=0n14lklkn1...k0k = \sum_{l=0}^{n-1} 4^l k_l \equiv k_{n-1}...k_0 para sa kn1,...,k0{0,1,2,3}k_{n-1},...,k_0\in \{0,1,2,3\} (iyon ay, base 44), at 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}

kung saan ang Vk:=Vkn1...Vk0V_k := V_{k_{n-1}}\otimes ... \otimes V_{k_0} at Λk:=Λkn1...Λk0\Lambda_k := \Lambda_{k_{n-1}}\otimes ... \otimes \Lambda_{k_0}, kung kaya: Pk^=VkΛkVk.\hat{P_k}=V_k^\dagger \Lambda_k V_k.

Mga cost function

Sa pangkalahatan, ginagamit ang mga cost function upang ilarawan ang layunin ng isang problema at kung gaano kahusay ang pagganap ng isang trial state kaugnay ng layuning iyon. Ang kahulugang ito ay maaaring ilapat sa iba't ibang halimbawa sa chemistry, machine learning, finance, optimization, at iba pa.

Isaalang-alang natin ang isang simpleng halimbawa ng paghahanap ng ground state ng isang sistema. Ang aming layunin ay i-minimize ang expectation value ng observable na kumakatawan sa enerhiya (Hamiltonian H^\hat{\mathcal{H}}):

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

Maaari nating gamitin ang Estimator upang suriin ang expectation value at ipasa ang halagang ito sa isang optimizer upang i-minimize. Kung matagumpay ang pag-optimize, magbabalik ito ng hanay ng mga pinakamainam na parameter value na θ\vec\theta^*, mula sa kung saan makakagawa tayo ng iminumungkahing solution state na ψ(θ)|\psi(\vec\theta^*)\rangle at masusukat ang expectation value bilang C(θ)C(\vec\theta^*).

Pansinin na ikukuha lamang natin ang kakayahang i-minimize ang cost function para sa limitadong hanay ng mga state na ating isinasaalang-alang. Nagdadala ito sa atin sa dalawang magkahiwalay na posibilidad:

  • Hindi tinukoy ng ating ansatz ang solution state sa buong search space: Kung ganito ang kaso, hindi kailanman makikita ng ating optimizer ang solusyon, at kailangan nating mag-eksperimento sa ibang mga ansatz na maaaring mas tumpay na kumakatawan sa ating search space.
  • Hindi makita ng ating optimizer ang valid na solusyong ito: Ang optimization ay maaaring tukuyin nang globally at locally. Susuriin natin ang ibig sabihin nito sa susunod na seksyon.

Sa kabuuan, magsasagawa tayo ng classical optimization loop ngunit maaasahan ang pagsusuri ng cost function sa isang quantum computer. Mula sa perspektibong ito, maaaring isipin ng isa ang optimization bilang isang purong klasikal na gawain kung saan tinatawagan natin ang ilang black-box quantum oracle sa tuwing kailangang suriin ng optimizer ang cost function.

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

Una, isasagawa natin ito gamit ang isang simulator: ang StatevectorEstimator. Karaniwang ipinapayo ito para sa pag-debug, ngunit agad susundan natin ang debugging run ng isang kalkulasyon sa tunay na quantum hardware. Lalong hindi na makakatawid sa classical simulation ang mga problemang may interes nang walang pinakabagong supercomputing facilities.

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

Magpapatuloy na tayo sa pagpapatakbo sa tunay na quantum computer. Pansinin ang mga pagbabago sa syntax. Ang mga hakbang na kinasasangkutan ng pass_manager ay tatalakayin pa sa susunod na halimbawa. Ang isang hakbang na partikular na mahalaga sa mga variational algorithm ay ang paggamit ng Qiskit Runtime session. Ang pagsisimula ng isang session ay nagbibigay-daan sa iyo na magpatakbo ng maraming iteration ng isang variational algorithm nang hindi naghihintay sa bagong pila sa bawat oras na ma-update ang mga parameter. Mahalaga ito kung mahaba ang mga oras ng pila at/o kailangan ng maraming iteration. Tanging ang mga kasosyo sa IBM Quantum® Network lamang ang makakagamit ng Runtime sessions. Kung wala kang access sa mga session, maaari mong bawasan ang bilang ng mga iteration na isusumite mo sa isang pagkakataon, at i-save ang pinakabagong mga parameter para gamitin sa mga susunod na run. Kung magsumite ka ng masyadong maraming iteration o makatagpo ng masyadong mahabang oras ng pila, maaari kang makatagpo ng error code 1217, na tumutukoy sa matagal na pagkaantala sa pagitan ng mga pagsusumite ng 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)

Pansinin na ang mga halagang nakuha mula sa dalawang kalkulasyon sa itaas ay halos magkapareho. Ang mga teknik para mapabuti ang mga resulta ay tatalakayin pa sa ibaba.

Halimbawa ng mapping sa mga hindi pisikal na sistema

Ang maximum cut (Max-Cut) na problema ay isang combinatorial optimization problem na kinasasangkutan ng paghati ng mga vertex ng isang graph sa dalawang disjoint na hanay upang mapakinabangan ang bilang ng mga gilid sa pagitan ng dalawang hanay. Mas pormal, sa isang undirected graph na G=(V,E)G=(V,E), kung saan ang VV ay ang hanay ng mga vertex at ang EE ay ang hanay ng mga gilid, hinahangad ng Max-Cut problem na hatiin ang mga vertex sa dalawang disjoint na subset, SS at TT, upang mapakinabangan ang bilang ng mga gilid na may isang endpoint sa SS at ang isa pa sa TT.

Maaari nating ilapat ang Max-Cut upang malutas ang iba't ibang problema kabilang ang: clustering, network design, phase transitions, at iba pa. Magsisimula tayo sa pamamagitan ng paglikha ng isang problem graph:

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

Ang problemang ito ay maaaring ipahayag bilang isang binary optimization problem. Para sa bawat node na 0i<n0 \leq i < n, kung saan ang nn ay ang bilang ng mga node ng graph (sa kasong ito ay n=4n=4), isasaalang-alang natin ang binary variable na xix_i. Ang variable na ito ay magkakaroon ng halagang 11 kung ang node ii ay nasa isa sa mga grupo na ating ita-label na 11 at 00 kung ito ay nasa kabilang grupo, na ita-label natin bilang 00. Itatatanda rin natin bilang wijw_{ij} (elemento (i,j)(i,j) ng adjacency matrix na ww) ang timbang ng gilid na mula sa node ii patungo sa node jj. Dahil ang graph ay undirected, wij=wjiw_{ij}=w_{ji}. Pagkatapos ay maaari nating buuin ang ating problema bilang pag-maximize ng sumusunod na cost function:

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}

Upang malutas ang problemang ito gamit ang isang quantum computer, ipapahayag natin ang cost function bilang expected value ng isang observable. Gayunpaman, ang mga observable na tinatanggap ng Qiskit nang native ay binubuo ng mga Pauli operator, na may eigenvalue na 11 at 1-1 sa halip na 00 at 11. Kaya naman gagawa tayo ng sumusunod na pagbabago ng variable:

Kung saan ang x=(x0,x1,,xn1)\vec{x}=(x_0,x_1,\cdots ,x_{n-1}). Maaari nating gamitin ang adjacency matrix na ww upang komportableng ma-access ang mga timbang ng lahat ng gilid. Gagamitin ito upang makuha ang ating cost function:

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

Nangangahulugan ito na:

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

Kaya ang bagong cost function na gustong i-maximize natin ay:

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}

Bukod dito, ang natural na ugali ng isang quantum computer ay ang paghahanap ng minima (karaniwang ang pinakamababang enerhiya) sa halip na maxima, kaya sa halip na i-maximize ang C(z)C(\vec{z}) ay i-minimize natin ang:

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}

Ngayon na mayroon na tayong cost function na i-minimize na ang mga variable ay maaaring magkaroon ng mga halagang 1-1 at 11, maaari tayong gumawa ng sumusunod na pagkakatulad sa Pauli ZZ:

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

Sa ibang salita, ang variable na ziz_i ay katumbas ng isang ZZ gate na kumikilos sa qubit ii. Bukod dito:

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

Ang observable na isasaalang-alang natin ay:

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

na dapat nating idagdag ang independent term pagkatapos:

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

Ang operator ay isang linear combination ng mga term na may Z operator sa mga node na konektado ng isang gilid (alalahanin na ang 0th qubit ay pinakamalayo sa kanan): IIZZ+IZIZ+IZZI+ZIIZ+ZZIIIIZZ + IZIZ + IZZI + ZIIZ + ZZII. Kapag nabuuo na ang operator, ang ansatz para sa QAOA algorithm ay madaling maitayo sa pamamagitan ng paggamit ng QAOAAnsatz circuit mula sa Qiskit circuit library.

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

Dahil direktang tumatanggap ang Runtime Estimator ng Hamiltonian at parameterized ansatz at nagbabalik ng kinakailangang enerhiya, ang cost function para sa isang QAOA instance ay medyo simple:

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

Babalik tayo sa halimbawang ito sa Applications upang tuklasin kung paano gamitin ang isang optimizer upang mag-iterate sa search space. Sa pangkalahatan, kinabibilangan ito ng:

  • Paggamit ng optimizer upang mahanap ang mga pinakamainam na parameter
  • Pag-bind ng mga pinakamainam na parameter sa ansatz upang mahanap ang mga eigenvalue
  • Pagsasalin ng mga eigenvalue sa ating kahulugan ng problema

Estratehiya sa pagsukat: bilis kumpara sa katumpakan

Tulad ng nabanggit, gumagamit tayo ng maingay na quantum computer bilang isang black-box oracle, kung saan ang ingay ay maaaring gawing hindi-deterministiko ang mga nakuhang halaga, na humahantong sa mga random na pagbabago na, sa turn, ay makapipinsala — o kahit ganap na makakapigil — sa convergence ng ilang mga optimizer patungo sa isang iminumungkahing solusyon. Ito ay isang pangkalahatang problema na dapat nating tugunan habang unti-unting tinutuklas natin ang quantum utility at umuusad patungo sa 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.

Maaari nating gamitin ang mga opsyon ng error suppression at error mitigation ng Qiskit Runtime Primitive upang tugunan ang ingay at mapakinabangan ang utility ng mga quantum computer ngayon.

Error Suppression

Ang Error suppression ay tumutukoy sa mga teknik na ginagamit upang i-optimize at baguhin ang isang Circuit sa panahon ng compilation upang mabawasan ang mga error. Ito ay isang pangunahing teknik sa pangangasiwa ng error na karaniwang nagdudulot ng ilang klasikal na pre-processing na overhead sa kabuuang runtime. Ang overhead ay kinabibilangan ng pag-transpile ng mga Circuit upang mapatakbo sa quantum hardware sa pamamagitan ng:

  • Pagpapahayag ng Circuit gamit ang mga native gate na available sa isang quantum system
  • Pag-map ng mga virtual qubit sa mga pisikal na qubit
  • Pagdaragdag ng mga SWAP batay sa mga kinakailangan ng konektibidad
  • Pag-optimize ng mga 1Q at 2Q gate
  • Pagdaragdag ng dynamical decoupling sa mga idle qubit upang maiwasan ang mga epekto ng decoherence.

Pinapayagan ng mga Primitive ang paggamit ng mga teknik ng error suppression sa pamamagitan ng pagtatakda ng opsyong optimization_level at pagpili ng mga advanced na opsyon sa transpilation. Sa isang susunod na kurso, susuriin natin ang iba't ibang paraan ng paggawa ng Circuit upang mapabuti ang mga resulta, ngunit para sa karamihan ng mga kaso, inirerekumenda nating itakda ang optimization_level=3.

Ivi-visualize natin ang halaga ng pagdaragdag ng optimization sa proseso ng transpilation sa pamamagitan ng pagtingin sa isang halimbawang Circuit na may simpleng ideal na pag-uugali.

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

Ang Circuit sa itaas ay maaaring magbunga ng sinusoidal na expectation value ng ibinigay na observable, sa kondisyon na maglagay tayo ng mga phase na sumasaklaw sa naaangkop na interval, tulad ng [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]

Maaari tayong gumamit ng simulator upang ipakita ang pagiging kapaki-pakinabang ng isang na-optimize na transpilation. Babalik tayo sa ibaba upang gumamit ng tunay na hardware upang ipakita ang pagiging kapaki-pakinabang ng error mitigation. Gagamitin natin ang QiskitRuntimeService upang makakuha ng tunay na backend (sa kasong ito, ibm_brisbane), at gamitin ang AerSimulator upang i-simulate ang backend na iyon, kasama ang gawi nito sa ingay.

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)

Maaari na tayong gumamit ng pass manager upang i-transpile ang Circuit sa "instruction set architecture" o ISA ng backend. Ito ay isang bagong kinakailangan sa Qiskit Runtime: ang lahat ng Circuit na isusumite sa isang backend ay dapat sumunod sa mga limitasyon ng target ng backend, ibig sabihin ay dapat itong isulat sa mga tuntunin ng ISA ng backend — iyon ay, ang hanay ng mga instruksyon na kayang intindihin at isagawa ng device. Ang mga target na limitasyong ito ay tinukoy ng mga salik tulad ng mga native na basis gate ng device, ang konektibidad ng qubit nito, at — kung naaangkop — ang mga detalye ng timing ng pulse at iba pang instruksyon nito.

Pansinin na sa kasalukuyang kaso, gagawin natin ito nang dalawang beses: minsan na may optimization_level = 0, at minsan na nakatakda ito sa 3. Sa bawat oras ay gagamitin natin ang Estimator primitive upang tantiyahin ang mga expectation value ng observable sa iba't ibang halaga ng phase.

# 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]

Sa wakas, maaari na nating i-plot ang mga resulta, at makikita natin na ang katumpakan ng kalkulasyon ay medyo mabuti kahit walang optimization, ngunit tiyak na napabuti ito sa pamamagitan ng pagdaragdag ng optimization sa level 3. Pansinin na sa mas malalim at mas kumplikadong Circuit, ang pagkakaiba sa pagitan ng mga optimization level na 0 at 3 ay malamang na mas kapansin-pansin. Ito ay isang napaka-simpleng Circuit na ginagamit bilang isang toy model.

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

Ang Error mitigation ay tumutukoy sa mga teknik na nagbibigay-daan sa mga gumagamit na bawasan ang mga error sa Circuit sa pamamagitan ng pag-modelo ng ingay ng device sa oras ng pagpapatakbo. Karaniwang nagdudulot ito ng quantum pre-processing overhead na may kaugnayan sa pagsasanay ng modelo at classical post-processing overhead upang mabawasan ang mga error sa mga raw na resulta sa pamamagitan ng paggamit ng nabuong modelo.

Ang opsyong resilience_level ng Qiskit Runtime primitive ay tumutukoy sa dami ng katatagan na itatatayo laban sa mga error. Ang mas mataas na antas ay nagbubunga ng mas tumpak na mga resulta sa gastos ng mas matagal na oras ng pagproseso dahil sa overhead ng quantum sampling. Ang mga antas ng katatagan ay maaaring gamitin upang i-configure ang trade-off sa pagitan ng gastos at katumpakan kapag inilalapat ang error mitigation sa iyong query sa primitive.

Kapag nagpapatupad ng anumang teknik ng error mitigation, inaasahan nating mababawasan ang bias sa ating mga resulta kaugnay ng nakaraan, walang mitigation na bias. Sa ilang mga kaso, maaari pang mawala ang bias. Gayunpaman, may kasamang gastos ito. Habang binabawasan natin ang bias sa ating mga tinantiyahing dami, tataas ang statistical variability (iyon ay, variance), na maaari nating tugunan sa pamamagitan ng karagdagang pagdaragdag ng bilang ng mga shot bawat Circuit sa ating proseso ng sampling. Magdudulot ito ng overhead na higit sa kailangan upang bawasan ang bias, kaya hindi ito ginagawa bilang default. Madali nating maaaring piliin ang gawi na ito sa pamamagitan ng pag-aayos ng bilang ng mga shot bawat Circuit sa options.executions.shots, tulad ng ipinakita sa halimbawa sa ibaba.

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

Para sa kursong ito, susuriin natin ang mga modelong ito ng error mitigation sa mataas na antas upang ilarawan ang error mitigation na maaaring isagawa ng Qiskit Runtime primitives nang hindi nangangailangan ng buong detalye ng implementasyon.

Twirled readout error extinction (T-REx)

Ginagamit ng Twirled readout error extinction (T-REx) ang isang teknik na kilala bilang Pauli twirling upang mabawasan ang ingay na ipinakilala sa panahon ng proseso ng quantum measurement. Ipinapalagay ng teknik na ito na walang partikular na anyo ng ingay, na ginagawa itong napaka-pangkalahatan at epektibo.

Pangkalahatang workflow:

  1. Makakuha ng data para sa zero state na may mga randomized na bit flip (Pauli X bago ang pagsukat)
  2. Makakuha ng data para sa nais na (maingay) state na may mga randomized na bit flip (Pauli X bago ang pagsukat)
  3. Kalkulahin ang espesyal na function para sa bawat hanay ng data, at hatiin.

 

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

Maaari nating itakda ito gamit ang options.resilience_level = 1, tulad ng ipinapakita sa halimbawa sa ibaba.

Zero noise extrapolation

Gumagana ang Zero noise extrapolation (ZNE) sa pamamagitan ng unang pagpapalakas ng ingay sa Circuit na naghahanda ng nais na quantum state, pagkuha ng mga sukat para sa ilang iba't ibang antas ng ingay, at paggamit ng mga sukat na iyon upang matukoy ang walang-ingay na resulta.

Pangkalahatang workflow:

  1. Palakasin ang ingay ng Circuit para sa ilang noise factor
  2. Patakbuhin ang bawat Circuit na may pinalaking ingay
  3. I-extrapolate pabalik sa zero noise limit

 

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.

Maaari nating itakda ito gamit ang options.resilience_level = 2. Maaari nating i-optimize ito pa sa pamamagitan ng pagtuklas ng iba't ibang noise_factors, noise_amplifiers, at extrapolators, ngunit ito ay nasa labas ng saklaw ng kursong ito. Hinihikayat ka naming mag-eksperimento sa mga opsyong ito tulad ng inilarawan dito.

Ang bawat pamamaraan ay may sariling nauugnay na overhead: isang trade-off sa pagitan ng bilang ng mga quantum computation na kailangan (oras) at ang katumpakan ng ating mga resulta:

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}

Paggamit ng mga opsyon ng mitigation at suppression ng Qiskit Runtime

Narito kung paano kalkulahin ang isang expectation value habang ginagamit ang error mitigation at suppression sa Qiskit Runtime. Maaari tayong gumamit ng eksaktong parehong Circuit at observable tulad ng dati, ngunit sa pagkakataong ito ay pinanatili ang optimization level sa level 2, at ngayon ay ina-tune ang resilience o ang mga teknik ng error mitigation na ginagamit. Ang prosesong ito ng error mitigation ay nangyayari nang maraming beses sa buong optimization loop.

Isasagawa natin ang bahaging ito sa tunay na hardware, dahil ang error mitigation ay hindi available sa mga 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]

Tulad ng dati, maaari nating i-plot ang mga nagresultang expectation value bilang function ng phase angle para sa tatlong antas ng error mitigation na ginamit. Sa kahirapan, makikita ng isa na ang error mitigation ay bahagyang nagpapabuti ng mga resulta. Muli, ang epektong ito ay mas kapansin-pansin sa mas malalim at mas kumplikadong Circuit.

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

Buod

Sa araling ito, natutunan mo kung paano gumawa ng cost function:

  • Gumawa ng cost function
  • Kung paano gamitin ang Qiskit Runtime primitives upang mabawasan at mapigilan ang ingay
  • Kung paano tukuyin ang isang estratehiya sa pagsukat upang i-optimize ang bilis kumpara sa katumpakan

Narito ang ating mataas na antas na variational workload:

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.

Ang ating cost function ay tumatakbo sa bawat iteration ng optimization loop. Ang susunod na aralin ay susuriin kung paano ginagamit ng classical optimizer ang ating pagsusuri ng cost function upang pumili ng mga bagong parameter.

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