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 . 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
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 (), at ang mga observable ay mga Hermitian linear operator () na kumikilos sa mga ket. Ang isang eigenvector () ng isang observable ay kilala bilang eigenstate. Ang pagsukat ng isang observable para sa isa sa mga eigenstate nito () ay magbibigay sa atin ng kaukulang eigenvalue () 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 , kinukuha ng primitive na ito ang posibilidad ng bawat posibleng computational basis state.Estimator: Binibigyan ng isang quantum observable na at isang estado na , kinakalkula ng primitive na ito ang inaasahang halaga ng .
Ang Sampler primitive
Ang Sampler primitive ay kinakalkula ang posibilidad ng pagkuha ng bawat posibleng estado na mula sa computational basis, binibigyan ng quantum circuit na naghahanda ng estado na . Kinakalkula nito ang
Kung saan ang ay ang bilang ng mga qubit, at ang ang integer na representasyon ng anumang posibleng output binary string na (iyon ay, mga integer base ).
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 (iyon ay, ), 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 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:
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 para sa isang quantum na estado na ; kung saan ang mga posibilidad ng observable ay maaaring ipahayag bilang , na ang ay mga eigenstate ng observable na . Ang inaasahang halaga ay tinukoy bilang average ng lahat ng posibleng kinalabasan na (iyon ay, ang mga eigenvalue ng observable) ng isang pagsukat ng estado na , na may timbang ayon sa kaukulang mga posibilidad:
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 na Pauli operator.
kung kaya
kung saan ang ay ang bilang ng mga qubit, ang para sa (iyon ay, mga integer base ), at .
Pagkatapos isagawa ang decomposition na ito, nagmumula ang Estimator ng bagong circuit na para sa bawat observable na (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 nang maaga, na hindi palaging ganoon para sa ibang mga observable.
Para sa bawat , 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 ng pagkuha ng bawat posibleng output na . Pagkatapos, hinahanap nito ang eigenvalue na ng na kaukulang sa bawat output na , pinarami ng , at isinasama ang lahat ng resulta upang makuha ang inaasahang halaga ng observable na para sa ibinigay na estado na .
Dahil ang pagkalkula ng inaasahang halaga ng na Pauli ay hindi praktikal (iyon ay, lumalaki nang eksponensyal), ang Estimator ay epektibo lamang kapag malaki ang bilang ng mga 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 :
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
Patnubay na halimbawa para sa pagkalkula ng mga inaasahang halaga
Ipagpalagay nating ang single-qubit state na , at observable
na may sumusunod na teoretikal na inaasahang halaga na
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 . Maaaring ipakita na ito ay nagbibigay ng parehong resulta sa pamamagitan ng paggawa ng katotohanan na , at .
Tingnan natin kung paano direktang kalkulahin ang at . Dahil ang at 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")
# Auxiliary circuit for X
aux_circuits[0].draw("mpl")
# Auxiliary circuit for Z
aux_circuits[1].draw("mpl")
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 kaugnay ng basis ng mga eigenstate ng , ang , sumusunod na:
Dahil hindi natin alam ang mga eigenvalue o eigenstate ng target observable na , kailangan muna nating isaalang-alang ang diagonalization nito. Dahil ang ay Hermitian, mayroon isang unitary transformation na kung kaya kung saan ang ay ang diagonal eigenvalue matrix, kaya kung , at .
Ipinapahiwatig nito na ang inaasahang halaga ay maaaring muling isulat bilang:
Dahil kung ang isang sistema ay nasa estado na ang posibilidad ng pagsukat ng ay , ang inaasahang halaga sa itaas ay maaaring ipahayag bilang:
Napakahalaga na tandaan na ang mga posibilidad ay kinukuha mula sa estado na sa halip na . Ito ang dahilan kung bakit ganap na kinakailangan ang matrix na . Maaari kang magtaka kung paano makuha ang matrix na at ang mga eigenvalue na . 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 .
Sa kabutihang palad, may paraan para makalusot dito: ang anumang na matrix ay maaaring isulat bilang isang linear na kombinasyon ng tensor product ng na Pauli matrix at mga identity, lahat ay parehong hermitian at unitary na may kilalang at . 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:
Kaya muling isulat natin ang kaugnay ng mga Pauli at identity:
kung saan ang para sa (iyon ay, base ), at :
kung saan ang at , kung kaya:
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 ):
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 , mula sa kung saan makakagawa tayo ng iminumungkahing solution state na at masusukat ang expectation value bilang .
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")
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 , kung saan ang ay ang hanay ng mga vertex at ang ay ang hanay ng mga gilid, hinahangad ng Max-Cut problem na hatiin ang mga vertex sa dalawang disjoint na subset, at , upang mapakinabangan ang bilang ng mga gilid na may isang endpoint sa at ang isa pa sa .
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"
)
Ang problemang ito ay maaaring ipahayag bilang isang binary optimization problem. Para sa bawat node na , kung saan ang ay ang bilang ng mga node ng graph (sa kasong ito ay ), isasaalang-alang natin ang binary variable na . Ang variable na ito ay magkakaroon ng halagang kung ang node ay nasa isa sa mga grupo na ating ita-label na at kung ito ay nasa kabilang grupo, na ita-label natin bilang . Itatatanda rin natin bilang (elemento ng adjacency matrix na ) ang timbang ng gilid na mula sa node patungo sa node . Dahil ang graph ay undirected, . Pagkatapos ay maaari nating buuin ang ating problema bilang pag-maximize ng sumusunod na cost function:
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 at sa halip na at . Kaya naman gagawa tayo ng sumusunod na pagbabago ng variable:
Kung saan ang . Maaari nating gamitin ang adjacency matrix na upang komportableng ma-access ang mga timbang ng lahat ng gilid. Gagamitin ito upang makuha ang ating cost function:
Nangangahulugan ito na:
Kaya ang bagong cost function na gustong i-maximize natin ay:
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 ay i-minimize natin ang:
Ngayon na mayroon na tayong cost function na i-minimize na ang mga variable ay maaaring magkaroon ng mga halagang at , maaari tayong gumawa ng sumusunod na pagkakatulad sa Pauli :
Sa ibang salita, ang variable na ay katumbas ng isang gate na kumikilos sa qubit . Bukod dito:
Ang observable na isasaalang-alang natin ay:
na dapat nating idagdag ang independent term pagkatapos:
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): . 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")
# 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:
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")
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 .
## 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()
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.
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:
- Makakuha ng data para sa zero state na may mga randomized na bit flip (Pauli X bago ang pagsukat)
- Makakuha ng data para sa nais na (maingay) state na may mga randomized na bit flip (Pauli X bago ang pagsukat)
- Kalkulahin ang espesyal na function para sa bawat hanay ng data, at hatiin.
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:
- Palakasin ang ingay ng circuit para sa ilang noise factor
- Patakbuhin ang bawat circuit na may pinalaking ingay
- I-extrapolate pabalik sa zero noise limit
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:
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()
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:
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