Pagsasama-sama gamit ang Qiskit Runtime
Buod​
Ibinibigay ni Victoria Lipinska ang huling recap ng ating natutunan hanggang ngayon.
Mga sanggunian​
Ang mga sumusunod na artikulo ay sinianggunian sa video sa itaas.
- Quantum Chemistry in the Age of Quantum Computing, Cao, et al.
- Quantum computational chemistry, McArdle, et al.
VQE gamit ang mga Qiskit pattern​
Mayroon na tayong lahat ng kinakailangang bahagi para sa isang VQE na kalkulasyon:
- Hamiltonian
- Ansatz
- Classical optimizer
Ngayon ay kailangan na lang nating isama ang lahat sa Qiskit patterns framework.
Hakbang 1: I-map ang mga classical input sa quantum problem​
Tulad ng nabanggit dati, ipapalagay natin dito na ang isang angkop na Hamiltonian ng interes ay nagawa na. Kung mayroon kang mga katanungan tungkol doon, tingnan ang aralin tungkol sa mga Hamiltonian para sa gabay. Ang code block sa ibaba ay nag-set up ng mga bahagi na ipinaliwanag sa mga naunang aralin. Pinili nating i-model ang H2 dito dahil ang Hamiltonian nito ay sapat na compact para isulat.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-aer qiskit-ibm-runtime scipy
# General imports
import numpy as np
from qiskit.quantum_info import SparsePauliOp
# Hamiltonian obtained from a previous lesson
H = SparsePauliOp(
[
"IIII",
"IIIZ",
"IZII",
"IIZI",
"ZIII",
"IZIZ",
"IIZZ",
"ZIIZ",
"IZZI",
"ZZII",
"ZIZI",
"YYYY",
"XXYY",
"YYXX",
"XXXX",
],
coeffs=[
-0.09820182 + 0.0j,
-0.1740751 + 0.0j,
-0.1740751 + 0.0j,
0.2242933 + 0.0j,
0.2242933 + 0.0j,
0.16891402 + 0.0j,
0.1210099 + 0.0j,
0.16631441 + 0.0j,
0.16631441 + 0.0j,
0.1210099 + 0.0j,
0.17504456 + 0.0j,
0.04530451 + 0.0j,
0.04530451 + 0.0j,
0.04530451 + 0.0j,
0.04530451 + 0.0j,
],
)
nuclear_repulsion = 0.7199689944489797
Pipiliin natin ang isang efficient_su2 circuit, at ang optimizer na COBYLA, para magsimula.
# Pre-defined ansatz circuit
from qiskit.circuit.library import efficient_su2
# SciPy minimizer routine
from scipy.optimize import minimize
# Plotting functions
# Random initial state and efficient_su2 ansatz
ansatz = efficient_su2(H.num_qubits, su2_gates=["rx"], entanglement="linear", reps=1)
x0 = 2 * np.pi * np.random.random(ansatz.num_parameters)
print(ansatz.decompose().depth())
ansatz.decompose().draw("mpl")
5
Ngayon ay bubuuin natin ang ating cost function. Malinaw na ito ay may kaugnayan sa Hamiltonian, ngunit naiiba dahil ang Hamiltonian ay isang operator, at gusto natin ng isang function na nagbabalik ng expectation value ng operator na iyon, gamit ang Estimator. Siyempre, nagagawa ito sa pamamagitan ng paggamit ng ansatz at ng mga variational parameter, kaya lahat ng ito ay lumalabas bilang mga argumento. Sa ibaba, nagtatakda tayo ng bahagyang iba't ibang bersyon para gamitin sa tunay na hardware o mga simulator.
def cost_func(params, ansatz, H, estimator):
pub = (ansatz, [H], [params])
result = estimator.run(pubs=[pub]).result()
energy = result[0].data.evs[0]
return energy
# def cost_func_sim(params, ansatz, H, estimator):
# energy = estimator.run(ansatz, H, parameter_values=params).result().values[0]
# return energy
Hakbang 2: I-optimize ang problem para sa quantum execution.​
Gusto nating tumakbo ang ating code nang pinaka-mahusay hangga't maaari sa hardware na aming ginagamit. Kaya dapat pumili tayo ng backend para magsimula ng hakbang ng optimization. Ang code sa ibaba ay pumipili ng pinaka-hindi abala na backend na available sa iyo.
# To run on hardware, select the backend with the fewest number of jobs in the queue
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService(channel="ibm_quantum_platform")
backend = service.least_busy(operational=True, simulator=False)
backend.name
Ang pag-optimize ng circuit para tumakbo sa isang tunay na backend ay isang mayaman at kritikal na paksa. Ngunit hindi ito partikular sa VQE. Sa ngayon, ipaalala lang natin sa iyo ang dalawang mahalagang termino:
- optimization_level: Inilalarawan nito kung gaano kahusay ang circuit na iniakma sa layout ng napiling backend. Ang pinakamababang optimization level ay gumagawa lamang ng pinakamÃnimong kailangan para maipatakbo ang circuit sa device; inikot nito ang mga qubit ng circuit sa mga qubit ng device at nagdaragdag ng mga swap gate para payagan ang lahat ng two-qubit na operasyon. Ang pinakamataas na optimization level ay mas matalino at gumagamit ng maraming trick para mabawasan ang kabuuang bilang ng gate. Dahil ang mga multi-qubit gate ay may mataas na error rate at ang mga qubit ay nagde-decohere sa paglipas ng panahon, ang mas maikling mga circuit ay dapat magbigay ng mas magandang resulta.
- Dynamical Decoupling: Maaari tayong mag-apply ng sequence ng mga gate sa mga idling qubit. Kinakansela nito ang ilang hindi gustong pakikipag-ugnayan sa kapaligiran.
Kumonsulta sa naka-link na dokumentasyon para sa karagdagang impormasyon sa pag-optimize ng mga circuit. Ang code sa ibaba ay bumubuo ng mass manager gamit ang mga preset pass manager mula sa
qiskit.transpiler.
from qiskit.transpiler import PassManager
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.transpiler.passes import (
ALAPScheduleAnalysis,
PadDynamicalDecoupling,
ConstrainedReschedule,
)
from qiskit.circuit.library import XGate
target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
pm.scheduling = PassManager(
[
ALAPScheduleAnalysis(target=target),
ConstrainedReschedule(
acquire_alignment=target.acquire_alignment,
pulse_alignment=target.pulse_alignment,
target=target,
),
PadDynamicalDecoupling(
target=target,
dd_sequence=[XGate(), XGate()],
pulse_alignment=target.pulse_alignment,
),
]
)
# Use the pass manager and draw the resulting circuit
ansatz_isa = pm.run(ansatz)
ansatz_isa.draw(output="mpl", idle_wires=False, style="iqp")
Kailangan din nating ilapat ang mga katangian ng device layout sa Hamiltonian.
hamiltonian_isa = H.apply_layout(ansatz_isa.layout)
hamiltonian_isa
SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIZII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIZII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYYYII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYXXII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXYYII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXXXII'],
coeffs=[-0.09820182+0.j, -0.1740751 +0.j, -0.1740751 +0.j, 0.2242933 +0.j,
0.2242933 +0.j, 0.16891402+0.j, 0.1210099 +0.j, 0.16631441+0.j,
0.16631441+0.j, 0.1210099 +0.j, 0.17504456+0.j, 0.04530451+0.j,
0.04530451+0.j, 0.04530451+0.j, 0.04530451+0.j])
Hakbang 3: I-execute gamit ang Qiskit Primitives.​
Bago tayo mag-execute sa napiling hardware, magandang ideya na gumamit ng simulator para sa mabilis na pag-debug, at minsan para sa mga pagtatantya ng error. Dahil sa mga dahilang iyon, ipapakita natin nang maikli kung paano patakbuhin ang VQE sa isang simulator. Ngunit kritikal na tandaan na walang classical computer, simulator, o GPU ang makakapag-simulate nang tumpak ng buong functionality ng isang highly-entangled na 127-qubit quantum computer. Sa kasalukuyang panahon ng quantum utility, ang mga simulator ay magkakaroon ng limitadong gamit.
Tandaan na para sa bawat pagpili ng mga parameter sa variational circuit, kailangan kalkulahin ang isang expectation value (dahil iyon ang value na imi-minimize). Tulad ng maaaring hinalaan mo na, ang pinaka-mahusay na paraan para gawin iyon ay ang paggamit ng Qiskit primitive na Estimator. Magsisimula tayo sa paggamit ng lokal na simulator, na mangangailangan ng paggamit ng lokal na bersyon ng Estimator na tinatawag na BackendEstimator.
Pinapanatili ang tunay na backend na ginamit natin para sa optimization, maaari tayong mag-import ng modelo ng noise behavior ng device na iyon para gamitin kasama ang lokal na simulator na aming pinili. Dito, gagamitin natin ang aer_simulator_statevector.
# We will start by using a local simulator
from qiskit_aer import AerSimulator
# Import an estimator, this time from qiskit (we will import from Runtime for real hardware)
from qiskit.primitives import BackendEstimatorV2
# generate a simulator that mimics the real quantum system
backend_sim = AerSimulator.from_backend(backend)
estimator = BackendEstimatorV2(backend=backend_sim)
Handa na tayong ipatupad ang VQE, imi-minimize ang cost function gamit ang napiling Hamiltonian, ansatz, classical optimizer, at ating BackendEstimator, batay sa tunay na backend na pinili natin para sa susunod na paggamit. Tandaan na dito ay pumili tayo ng medyo maliit na bilang para sa maximum na iterations. Ito ay dahil ginagamit lang natin ang simulator para mag-debug. Ang mga hakbang ng VQE optimization ay madalas na nangangailangan ng daan-daang iterations para mag-converge.
res = minimize(
cost_func,
x0,
args=(ansatz_isa, hamiltonian_isa, estimator),
method="cobyla",
options={"maxiter": 10, "disp": True},
)
print(getattr(res, "fun") - nuclear_repulsion)
print(res)
Return from COBYLA because the objective function has been evaluated MAXFUN times.
Number of function values = 10 Least value of F = -0.11556938907226563
The corresponding X is:
[4.11796514 4.52126324 0.69570423 4.12781503 6.55507846 1.80713073
0.9645473 6.23812214]
-0.8355383835212453
message: Return from COBYLA because the objective function has been evaluated MAXFUN times.
success: False
status: 3
fun: -0.11556938907226563
x: [ 4.118e+00 4.521e+00 6.957e-01 4.128e+00 6.555e+00
1.807e+00 9.645e-01 6.238e+00]
nfev: 10
maxcv: 0.0
Ang code na ito ay nag-evaluate nang tama, kahit hindi ito nag-converge, na inaasahan natin. Magpapatuloy tayo sa pagpapatakbo ng kalkulasyon sa tunay na hardware, at pagkatapos ay tatalakayin ang mga output. Para sa mga tunay na backend, gagamitin natin ang Qiskit Runtime Estimator. Gusto nating i-execute ito sa loob ng isang Qiskit Runtime session at karaniwang gusto nating tukuyin ang mga opsyon para sa session na iyon.
from qiskit_ibm_runtime import QiskitRuntimeService, Session
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime.options import EstimatorOptions
Sa iba pang mga bagay, ang paggamit ng session ay nangangahulugang ang ating job ay maghihintay sa queue nang isang beses lamang, para magsimula. Ang mga kasunod na iteration ng classical optimizer ay hindi mai-queue. Sa session, maaari tayong magtakda ng resilience at optimization levels. Ang mga tool na ito ay sapat na mahalaga kaya isasama natin ang isang maikling pagsusuri ng bawat isa at ang kanilang kahalagahan sa VQE, na may mga link para matuto pa:
- Mga Runtime session: Ang VQE ay likas na iterative, kung saan ang classical optimizer ay pumipili ng mga bagong variational parameter, at kaya't mga bagong gate ang ginagamit, sa bawat kasunod na pagsubok. Kung walang mga session, maaari itong magresulta sa karagdagang oras sa queue sa pagitan ng bawat trial circuit. Ang pag-encapsulate ng VQE na kalkulasyon sa loob ng isang session ay nagresulta sa isang paunang queue lamang bago magsimula ang job, ngunit walang karagdagang oras sa queue sa pagitan ng mga variational step. Ang estratehiyang ito ay ginamit na sa halimbawa ng nakaraang aralin, ngunit maaaring gumanap ng mas mahalagang papel kapag nagbabago-bago ang geometry. Para sa karagdagan tungkol sa mga session, suriin ang dokumentasyon ng mga execution mode.
- Ang built-in na optimization ng Estimator: Sa Estimator ay may mga built-in na opsyon para sa pag-optimize ng isang kalkulasyon. Sa maraming konteksto (kasama ang Estimator), ang mga setting ay limitado sa 0 at 1, kung saan ang 0 ay nagpapahiwatig ng walang optimization, at ang 1 (ang default) ay nagpapahiwatig ng ilang optimization ng iyong circuit sa napiling hardware. Ang ilang ibang konteksto ay nagpapahintulot ng mga setting na 0, 1, 2, o 3. Para sa karagdagan tungkol sa mga partikular na pamamaraan na ginamit sa iba't ibang setting, tingnan ang dokumentasyon. Dito, itatatakda natin ang optimization sa 0, at gagamitin ang
skip\_transpilation = true, dahil na-transpile na natin ang ating circuit gamit ang pass manager sa itaas, sa seksyon ng optimization. - Ang built-in na resilience ng Estimator: Tulad ng optimization, ang Estimator ay may mga built-in na setting para sa resilience laban sa mga error, na naaayon sa iba't ibang approach sa error mitigation. Para matuto tungkol sa mga resilience level setting, sumangguni sa dokumentasyon.
Kapaki-pakinabang na tandaan na ang error mitigation ay gumaganap ng nuanced na papel sa convergence ng isang VQE na kalkulasyon. Ang classical optimizer ay naghahanap ng parameter space para sa mga parameter na nagmi-minimize ng energy. Kapag malayo ka sa optimal na mga parameter, ang isang matarik na gradient ay maaaring makita ng classical optimizer kahit may mga error. Ngunit habang nag-co-converge ang kalkulasyon at papalapit ka sa mga optimal na value, ang gradient ay nagiging mas maliit, at mas madaling matanggal ng mga error. Gaano karaming error mitigation ang gusto mong gamitin? Sa anong mga punto sa convergence? Ito ang mga pagpipiliang kailangan mong gawin para sa iyong partikular na use case.
Para sa unang hardware run na ito, itinakda natin ang resilience sa 0 para mapabilis ang pagpapatakbo. Para sa anumang seryosong aplikasyon, gusto mong gumamit ng error mitigation. Tandaan na sa cell sa ibaba ay may dalawang set ng opsyon: (1) mga opsyon para sa Runtime session, na pinangalanan nating "session_options", at (2) mga opsyon para sa classical optimizer, simpleng tinatawag na "options" dito.
estimator_options = EstimatorOptions(resilience_level=0, default_shots=2000)
with Session(backend=backend) as session:
estimator = Estimator(mode=session, options=estimator_options)
res = minimize(
cost_func,
x0,
args=(ansatz_isa, hamiltonian_isa, estimator),
method="cobyla",
options={"maxiter": 10, "disp": True},
)
Return from COBYLA because the objective function has been evaluated MAXFUN times.
Number of function values = 10 Least value of F = -0.11691688904
The corresponding X is:
[5.11796514 5.52126324 0.69570423 5.12781503 6.55507846 1.80713073
1.9645473 6.23812214]
Maaari mong tingnan ang progreso ng iyong job sa IBM Quantum® Platform sa ilalim ng Workloads.
print(getattr(res, "fun") - nuclear_repulsion)
print(res)
-0.8368858834889796
message: Return from COBYLA because the objective function has been evaluated MAXFUN times.
success: False
status: 3
fun: -0.11691688904
x: [ 5.118e+00 5.521e+00 6.957e-01 5.128e+00 6.555e+00
1.807e+00 1.965e+00 6.238e+00]
nfev: 10
maxcv: 0.0
Hakbang 4: Post-process, ibalik ang resulta sa classical na format.​
Sandali tayong tiyaking naiintindihan natin ang mga output na ito. Ang "fun" na output ay ang pinakamababa na value na nakuha natin para sa cost function (hindi kinakailangan ang huling value na kinakalkula). Ito ang kabuuang energy, kasama ang positibong nuclear repulsion, kaya naman na-define din natin ang electron_energy.
Sa kaso sa itaas, mayroon tayong mensahe na nalampasan ang maximum na bilang ng function evaluation, at ang bilang ng function evaluation (nfev) ay 10. Ang ibig sabihin nito ay ang ibang pamantayan para sa convergence ng optimization ay hindi natugunan; sa madaling salita, walang dahilan para isipin na nahanap natin ang ground state energy. Ito rin ang kahulugan ng success na "False".
Sa wakas, mayroon tayong x. Ito ang vector ng mga variational parameter. Ang mga ito ang mga parameter na ginamit sa kalkulasyon na nagbigay ng pinakamababa na cost function (energy expectation value). Ang walong value na ito ay naaayon sa walong rotation angle sa mga gate na iyon sa ansatz na tumatanggap ng mga variable na rotation angle.
Congrats! Nagpatakbo ka na ng isang VQE na kalkulasyon sa isang IBM Quantum QPU!
Sa susunod na aralin, makikita natin kung paano i-adjust ang workstream na ito para magsama ng mga variable sa iyong Hamiltonian. Sa konteksto ng mga problema sa quantum chemistry, maaaring mangahulugan ito ng pagbabago ng geometry para matukoy ang mga hugis ng mga molekula o mga binding site.
import qiskit
import qiskit_ibm_runtime
print(qiskit.version.get_version_info())
print(qiskit_ibm_runtime.version.get_version_info())
2.1.0
0.40.1