Přeskočit na hlavní obsah

Bringing it all together with Qiskit Runtime

Shrnutí

Victoria Lipinska podává závěrečné shrnutí toho, co jsme se dosud naučili.

Reference

Následující články jsou zmíněny ve výše uvedeném videu.

VQE s Qiskit patterns

Máme všechny potřebné komponenty pro výpočet VQE:

  • Hamiltonián
  • Ansatz
  • Klasický optimalizátor

Teď je stačí propojit v rámci frameworku Qiskit patterns.

Krok 1: Převod klasických vstupů na kvantový problém

Jak bylo uvedeno dříve, budeme zde předpokládat, že vhodně naformátovaný hamiltonián zájmu byl již vygenerován. Pokud máš k tomu otázky, podívej se na lekci o hamiltonánech. Níže uvedený blok kódu nastavuje komponenty vysvětlené v předchozích lekcích. Zde jsme se rozhodli modelovat H2, protože jeho hamiltonián je dostatečně kompaktní, aby se dal celý vypsat.

# 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

Vybereme okruh efficient_su2 a optimalizátor COBYLA jako výchozí bod.

# 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

Output of the previous code cell

Nyní sestavíme naši účelovou funkci. Ta přímo souvisí s hamiltoniánem, liší se však v tom, že hamiltonián je operátor, a my chceme funkci, která vrací střední hodnotu tohoto operátoru pomocí Estimatoru. Samozřejmě k tomu využívá ansatz a variační parametry, takže ty všechny figurují jako argumenty. Níže definujeme mírně odlišné verze pro použití na skutečném hardwaru nebo simulátorech.

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

Krok 2: Optimalizuj problém pro kvantové spuštění

Chceme, aby náš kód běžel na používaném hardwaru co nejefektivněji. Proto musíme vybrat backend, abychom mohli zahájit krok optimalizace. Níže uvedený kód vybere nejméně vytížený backend, který máš k dispozici.

# 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

Optimalizace obvodu pro spuštění na reálném backendu je bohaté a zásadní téma. Není však specifické pro VQE. Prozatím ti pouze připomeneme dva důležité pojmy:

  • optimization_level: Popisuje, jak dobře je obvod přizpůsoben rozložení vybraného backendu. Nejnižší úroveň optimalizace provede jen nezbytné minimum potřebné ke spuštění obvodu na zařízení – namapuje qubity obvodu na qubity zařízení a přidá swap hradla, aby bylo možné provést všechny dvoukubitové operace. Nejvyšší úroveň optimalizace je mnohem chytřejší a využívá celou řadu triků ke snížení celkového počtu hradel. Protože vícekvbitová hradla mají vysokou míru chyb a qubity se v průběhu času dekoherují, kratší obvody by měly přinášet lepší výsledky.
  • Dynamical Decoupling: Na nečinné qubity lze aplikovat posloupnost hradel. Tím se potlačí některé nežádoucí interakce s prostředím. Další informace o optimalizaci obvodů najdeš v odkazované dokumentaci. Níže uvedený kód generuje správce průchodů pomocí přednastavených správců průchodů z 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")

Output of the previous code cell

Stejným způsobem musíme aplikovat charakteristiky rozložení zařízení na hamiltonián.

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

Krok 3: Spuštění pomocí Qiskit Primitives

Než spustíme výpočet na vybraném hardwaru, je vhodné použít simulátor pro rychlé ladění a někdy také pro odhad chyb. Z těchto důvodů stručně ukážeme, jak spustit VQE na simulátoru. Je však zásadní poznamenat, že žádný klasický počítač, simulátor ani GPU nedokáže přesně simulovat plnou funkčnost vysoce provázaného 127-qubitového kvantového počítače. V současné éře kvantové užitečnosti budou mít simulátory omezené využití.

Připomeň si, že pro každou volbu parametrů ve variačním obvodu je nutné vypočítat střední hodnotu (protože právě ta se minimalizuje). Jak jsi možná již uhodl/a, nejefektivnějším způsobem, jak toho dosáhnout, je použití Qiskit primitive Estimator. Začneme lokálním simulátorem, který vyžaduje použití lokální verze Estimatoru nazývané BackendEstimator.

Ponecháme skutečný backend, který jsme použili pro optimalizaci, a můžeme importovat model chování šumu tohoto zařízení a použít ho s lokálním simulátorem dle vlastního výběru. Zde použijeme 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)

Konečně nastal čas implementovat VQE — minimalizovat účelovou funkci pomocí vybraného hamiltoniánu, ansatzu, klasického optimalizátoru a našeho BackendEstimatoru, který vychází ze skutečného backendu zvoleného pro následné použití. Všimni si, že jsme zde zvolili poměrně nízký maximální počet iterací. Je to proto, že simulátor používáme pouze k ladění. Optimalizační kroky VQE často vyžadují stovky iterací, než konvergují.

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

Tento kód proběhl správně, i když nekonvergoval, což jsme očekávali. Přistoupíme ke spuštění výpočtu na skutečném hardwaru a poté probereme výstupy. Pro skutečné backendy použijeme Qiskit Runtime Estimator. Výpočet budeme chtít spustit uvnitř relace Qiskit Runtime a zpravidla budeme chtít pro tuto relaci nastavit příslušné možnosti.

from qiskit_ibm_runtime import QiskitRuntimeService, Session
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime.options import EstimatorOptions

Použití relace mimo jiné znamená, že naše úloha bude čekat ve frontě pouze jednou — na začátku. Další iterace klasického optimalizátoru nebudou zařazeny do fronty. V relaci můžeme nastavit úrovně odolnosti a optimalizace. Tyto nástroje jsou natolik důležité, že uvádíme stručný přehled každého z nich a jeho význam pro VQE, spolu s odkazy pro další studium:

  • Relace Runtime: VQE je ze své podstaty iterativní — klasický optimalizátor vybírá nové variační parametry, a tedy i nová hradla, v každém dalším pokusu. Bez použití relací by to mohlo vést k dalšímu čekání ve frontě mezi každým pokusným obvodem. Zapouzdřením výpočtu VQE do relace dochází pouze k jednomu počátečnímu čekání ve frontě před spuštěním úlohy, ale bez dalšího čekání mezi variačními kroky. Tato strategie již byla použita v příkladu z předchozí lekce, ale může hrát ještě důležitější roli při proměnné geometrii. Více o relacích najdeš v dokumentaci režimů spouštění.
  • Vestavěná optimalizace Estimatoru: V Estimatoru jsou vestavěné možnosti pro optimalizaci výpočtu. V mnoha kontextech (včetně Estimatoru) jsou nastavení omezena na 0 a 1, přičemž 0 znamená žádnou optimalizaci a 1 (výchozí hodnota) znamená určitou optimalizaci tvého obvodu pro vybraný hardware. Některé jiné kontexty umožňují nastavení 0, 1, 2 nebo 3. Více o konkrétních metodách používaných v různých nastaveních najdeš v dokumentaci. Zde ve skutečnosti nastavíme optimalizaci na 0 a použijeme skip\_transpilation = true, protože jsme náš obvod již transpilovali pomocí správce průchodů výše, v sekci optimalizace.
  • Vestavěná odolnost Estimatoru: Stejně jako u optimalizace má Estimator vestavěná nastavení pro odolnost vůči chybám, která odpovídají různým přístupům ke zmírnění chyb. Informace o nastavení úrovně odolnosti najdeš v dokumentaci.

Stojí za zmínku, že zmírňování chyb hraje v konvergenci výpočtu VQE jemnou roli. Klasický optimalizátor prohledává prostor parametrů a hledá ty, které minimalizují energii. Pokud jsi daleko od optimálních parametrů, může být strmý gradient patrný klasickému optimalizátoru i v přítomnosti chyb. Jak však výpočet konverguje a blížíš se optimálním hodnotám, gradient se zmenšuje a snáze jej chyby „smyjí". Kolik zmírňování chyb chceš použít? V jakých bodech konvergence? Tato rozhodnutí musíš učinit pro svůj konkrétní případ použití.

Pro toto první spuštění na hardwaru jsme nastavili odolnost na 0, abychom umožnili relativně rychlý běh. Pro jakékoli závažné použití budeš chtít zmírňování chyb použít. Všimni si, že v níže uvedené buňce jsou dvě sady možností: (1) možnosti pro relaci Runtime, které jsme pojmenovali „session_options", a (2) možnosti pro klasický optimalizátor, zde jednoduše nazvané „options".

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]

Průběh své úlohy můžeš sledovat na platformě IBM Quantum® Platform v části 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

Krok 4: Post-processing, vrácení výsledku v klasickém formátu

Věnujme chvíli tomu, abychom se ujistili, že těmto výstupům rozumíme. Výstup „fun" je minimální hodnota, kterou jsme získali pro účelovou funkci (ne nutně poslední vypočítaná hodnota). Jedná se o celkovou energii včetně kladné jaderné repulze, a proto jsme také definovali electron_energy.

V případě výše máme zprávu, že byl překročen maximální počet vyhodnocení funkce a že počet vyhodnocení funkce (nfev) byl 10. To jednoduše znamená, že ostatní kritéria konvergence optimalizace nebyla splněna; jinými slovy, není důvod se domnívat, že jsme nalezli energii základního stavu. To je také smysl hodnoty success rovné „False".

A konečně máme x. Je to vektor variačních parametrů. Jsou to parametry použité při výpočtu, který vedl k minimální účelové funkci (střední hodnota energie). Těchto osm hodnot odpovídá osmi rotačním úhlům v těch hradlech ansatzu, která přijímají proměnné rotační úhly.

Gratulujeme! Spustil/a jsi výpočet VQE na QPU od IBM Quantum!

V další lekci uvidíš, jak upravit tento pracovní postup tak, aby zahrnoval proměnné v Hamiltoniánu. V kontextu problémů kvantové chemie to může znamenat variaci geometrie za účelem určení tvaru molekul nebo vazebných míst.

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