Přeskočit na hlavní obsah

Experiment v měřítku užitečnosti I

poznámka

Tamiya Onodera (5. července 2024)

Stáhni si pdf původní přednášky. Upozorňujeme, že některé úryvky kódu mohou být zastaralé, protože jde o statické obrázky.

Přibližný čas QPU pro spuštění tohoto experimentu je 45 sekund.

1. Úvod do článku o užitečnosti

V této lekci spustíme Circuit v měřítku užitečnosti, který se objevuje v publikaci, již neformálně nazýváme „článek o užitečnosti“, zveřejněné v Nature Vol 618, 15. června 2023. Článek se zabývá časovým vývojem 2D Isingova modelu s příčným polem. Konkrétně se zaměřuje na časovou dynamiku Hamiltoniánu,

H=HZZ+HX=J(i,j)ZiZj+hiXiH = H_{ZZ} + H_X = - J \sum_{(i,j)} Z_i Z_j + h \sum_{i} X_i

kde J>0J > 0 je vazba mezi nejbližšími sousedními spiny s i<ji < j a hh je globální příčné pole. Simulují spinovou dynamiku z počátečního stavu pomocí Trotterova rozkladu prvního řádu operátoru časového vývoje,

exp(iHZZδt)=(i,j)exp(iJδtZiZj)=(i,j)RZiZj(2Jδt)exp(iHXδt)=iexp(ihδtXi)=iRXi(2hδt)\begin{aligned} \exp(-i H_{ZZ} \delta t) &= \prod_{(i,j)} \exp (i J \delta t Z_i Z_j) = \prod_{(i,j)} \mathrm{R}_{Z_i Z_j} ( - 2 J \delta t) \\ \exp(-i H_X \delta t) &= \prod_{i} \exp (-i h \delta t X_i ) = \prod_{i} \mathrm{R}_{X_i} ( 2 h \delta t) \end{aligned}

kde je doba vývoje TT diskretizována do T/δtT / \delta t Trotterových kroků a RZiZj(θJ)\mathrm{R}_{Z_i Z_j}(\theta_J) a RXi(θh)\mathrm{R}_{X_i}(\theta_h) jsou rotační Gates ZZZZ a XX.

Experimenty prováděli na procesoru IBM Quantum® Eagle, což je 127qubitové zařízení s heavy-hex konektivitou, přičemž aplikovali interakce XX na všechny Qubits a interakce ZZZZ na všechny hrany mapy vazeb. Všimni si, že všechny interakce ZZZZ nelze aplikovat současně kvůli „datové závislosti“. Proto obarví mapu vazeb, aby je seskupili do vrstev. Ty ve stejné vrstvě dostanou stejnou barvu a lze je aplikovat paralelně.

Navíc se pro zjednodušení experimentu zaměřili na případ θJ=π/2\theta_J=-\pi /2.

Novým přínosem článku je, že sestavili kvantové Circuits v měřítku přesahujícím simulaci stavovým vektorem, spustili je na zašuměných kvantových počítačích a podařilo se jim získat spolehlivé výsledky. Prokázali tak užitečnost zašuměných kvantových počítačů. Při tom použili extrapolaci nulového šumu (ZNE) s pravděpodobnostním zesílením chyb (PEA) ke zmírnění chyb ze zašuměných zařízení.

Od té doby nazýváme takové experimenty a Circuits „v měřítku užitečnosti“.

1.1 Váš cíl

Tvým cílem v této lekci je sestavit Circuit v měřítku užitečnosti a spustit jej na procesoru Eagle. Získávání spolehlivých výsledků přesahuje rámec tohoto notebooku, částečně proto, že PEA je v současnosti experimentální funkcí Qiskitu, a částečně proto, že aplikace ZNE s PEA zabere poměrně dost času.

Konkrétně máš za úkol sestavit a spustit Circuit odpovídající obrázku 4b v článku a vykreslit „nezmírněné“ body vlastní. Jak vidíš, jedná se o Circuit s 127 Qubits ×\times 60 vrstev (20 Trotterových kroků) s pozorovatelnou veličinou Z62\langle Z_{62} \rangle. image.png Zní to jako velká věc?   Žádný strach. Poslední tři lekce tohoto kurzu ti poskytnou odrazové můstky. Pro začátek si předvedeme experiment menšího rozsahu, ve kterém sestavíme a spustíme na falešném zařízení Circuit s 27 Qubits ×\times 6 vrstev (2 Trotterovy kroky) s pozorovatelnou veličinou Z13\langle Z_{13} \rangle.

To je vše k úvodu. Pojďme se vydat na dobrodružství v měřítku užitečnosti!

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

qiskit.__version__
'2.0.2'
#!pip install qiskit_ibm_runtime
#!pip install qiskit_aer
import matplotlib.pyplot as plt
import numpy as np
import rustworkx as rx

from qiskit import QuantumCircuit, transpile
from qiskit.circuit import Parameter
from qiskit.circuit.library import YGate
from qiskit.quantum_info import SparsePauliOp
from qiskit_ibm_runtime import (
QiskitRuntimeService,
fake_provider,
EstimatorV2 as Estimator,
)
from qiskit_aer import AerSimulator
service = QiskitRuntimeService()

2. Příprava

2.1 Konstrukce RZZ(-π\pi / 2)

Nejprve si všimni, že RZZ Gate obecně vyžaduje dva CXCX Gates.

from qiskit.circuit.library import RZZGate

θ_h = Parameter("$\\theta_h$")
qc1 = QuantumCircuit(2)
qc1.append(RZZGate(θ_h), [0, 1])
qc1.decompose(reps=1).draw("mpl")

Output of the previous code cell

Jak je uvedeno výše, v tomto experimentu se zaměřujeme na RZZ Gate s konkrétním úhlem -π\pi / 2. Jak ukazuje článek, lze jej realizovat pouze s jedním CXCX Gate.

qc2 = QuantumCircuit(2)

qc2.sdg([0, 1])
qc2.append(YGate().power(1 / 2), [1])
qc2.cx(0, 1)
qc2.append(YGate().power(1 / 2).adjoint(), [1])

qc2.draw("mpl")

Output of the previous code cell

Pro další použití definujeme Gate na základě tohoto Circuit.

rzz = qc2.to_gate(label="RZZ")

Pojďme nově definované rzz náhodně použít.

qc3 = QuantumCircuit(3)
qc3.append(rzz, [0, 1])
qc3.append(rzz, [0, 2])
display(qc3.draw("mpl"))
# display(qc.decompose(reps=1).draw("mpl"))

Output of the previous code cell

Než budeme pokračovat, ověřme logickou ekvivalenci mezi qc1 (RZZ Gate) pro -pi/2 a naším nově definovaným rzz, tedy qc2 Gate:

from qiskit.quantum_info import Operator

op1 = Operator(qc1.assign_parameters([-np.pi / 2]))
op2 = Operator(qc2)

op1.equiv(op2)
True

2.2 Obarvení mapy vazeb

Podívejme se, jak se obarvuje mapa vazeb (coupling map) Backendu. To je potřeba k seskupení ZZZZ interakcí do vrstev.

Nejprve si vizualizujme mapu vazeb Backendu. Všimni si, že u všech současných zařízení IBM Quantum jsou mapy vazeb typu heavy-hex.

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

backend.coupling_map.draw()

Output of the previous code cell

K obarvení mapy vazeb použijeme rustworkx, což je Python balíček pro práci s grafy a komplexními sítěmi. Poskytuje několik obarvovacích algoritmů, které jsou všechny heuristické, a proto není zaručeno, že najdou minimální obarvení.

Nicméně vzhledem k tomu, že grafy heavy-hex jsou bipartitní, zvolíme graph_bipartite_edge_color, který by pro tyto grafy měl najít minimální obarvení.

def color_coupling_map(backend):
graph = backend.coupling_map.graph
undirected_graph = graph.to_undirected(multigraph=False)
edge_color_map = rx.graph_bipartite_edge_color(undirected_graph)
if edge_color_map is None:
edge_color_map = rx.graph_greedy_edge_color(undirected_graph)
# build a map from color to a list of edges
edge_index_map = undirected_graph.edge_index_map()
color_edges_map = {color: [] for color in edge_color_map.values()}
for edge_index, color in edge_color_map.items():
color_edges_map[color].append(
(edge_index_map[edge_index][0], edge_index_map[edge_index][1])
)
return edge_color_map, color_edges_map

Grafy typu heavy-hex by měly být obarveny třemi barvami. Ověřme si to pro výše uvedenou mapu vazeb.

edge_color_map, color_edges_map = color_coupling_map(backend)
print(
f"{backend.name}, {backend.num_qubits}-qubit device, {len(color_edges_map.keys())} colors assigned."
)
ibm_strasbourg, 127-qubit device, 3 colors assigned.

Ano, je to tak!

Pro zábavu si namalujme mapu vazeb získaným obarvením, s využitím vizualizační funkce rustworks.

color_str_map = {0: "green", 1: "red", 2: "blue"}

undirected_graph = backend.coupling_map.graph.to_undirected(multigraph=False)
for i in undirected_graph.edge_indices():
undirected_graph.get_edge_data_by_index(i)["color"] = color_str_map[
edge_color_map[i]
]

rx.visualization.graphviz_draw(
undirected_graph, method="neato", edge_attr_fn=lambda edge: {"color": edge["color"]}
)

Output of the previous code cell

3. Vyřešení Trotterizované časové evoluce 2D Isingova modelu.

Pojďme definovat rutinu pro sestavení obvodu z utility paperu pro časovou evoluci 2D Isingova modelu. Rutina přijímá tři parametry: Backend, celé číslo udávající počet Trotterových kroků a booleovskou hodnotu řídící vkládání bariér.

def get_utility_circuit(backend, num_steps: int, barrier: bool = False):
num_qubits = backend.num_qubits
_, color_edges_map = color_coupling_map(backend)
θ_h = Parameter("$\\theta_h$")
qc = QuantumCircuit(num_qubits)

for i in range(num_steps):
qc.rx(θ_h, range(num_qubits))

for _, edge_list in color_edges_map.items():
for edge in edge_list:
qc.append(rzz, edge)

if barrier:
qc.barrier()
return qc

Všimni si, že jsme pro sestavený Circuit již ručně provedli mapování a routing Qubitů. Proto, když Circuit později transpilujeme, nepožadujeme (neměli bychom požadovat) po Transpileru, aby prováděl mapování a routing Qubitů. Jak za chvíli uvidíš, voláme ho s úrovní optimalizace 1 a metodou layoutu „trivial“.

Dále definujeme jednoduchou rutinu pro získání informací o sestaveném obvodu pro rychlou kontrolu.

def get_circuit_info(qc: QuantumCircuit, reps: int = 0):
qc0 = qc.decompose(reps=reps)
return (
f"{qc0.num_qubits} qubits × {qc0.depth(lambda x: x.operation.num_qubits == 2)} layers ({qc0.depth()}-depth)"
+ ", "
+ f"""Gate breakdown: {", ".join([f"{k.upper()} {v}" for k, v in qc0.count_ops().items()])}"""
)

Vyzkoušejme tyto rutiny. Měl bys vidět Circuit o 27 Qubitech ×\times 15 vrstev (5 Trotterových kroků). Protože falešné zařízení má 28 hran, mělo by být 28*5 provazujících Gates.

backend = fake_provider.FakeTorontoV2()
num_steps = 5
qc = get_utility_circuit(backend, num_steps, True)

display(qc.draw(output="mpl", fold=-1))
print(get_circuit_info(qc, reps=0))
print(get_circuit_info(qc, reps=1))

Výstup předchozí buňky s kódem

27 qubits × 15 layers (20-depth),  Gate breakdown: CIRCUIT-165 140, RX 135, BARRIER 5
27 qubits × 15 layers (60-depth), Gate breakdown: SDG 280, UNITARY 280, CX 140, R 135, BARRIER 5

4. Vyřešení 27qubitové verze problému.

Nyní předvedeme menší verzi utility experimentu. Sestavíme Circuit o 27 Qubitech ×\times 6 vrstev (2 Trotterovy kroky) s Z13\langle Z_{13} \rangle jako observable a spustíme jej jak na AerSimulatoru, tak na falešném zařízení.

Samozřejmě sledujeme náš čtyřkrokový pracovní postup, „Qiskit pattern“, který se skládá z kroků Map, Optimize, Execute a Post-Process. Konkrétněji:

  • Mapování klasických vstupů na kvantový výpočet.
  • Optimalizace obvodů pro kvantový výpočet.
  • Spuštění obvodů pomocí primitiv.
  • Post-processing a vrácení výsledků v klasickém formátu.

V následujícím máme krok Map pro vytvoření obvodu pro menší experiment. Poté máme jednu sadu kroků Optimize a Execute pro AerSimulator a další pro falešné zařízení. Nakonec máme krok Post-Process pro vykreslení výsledků.

4.1 Krok 1: Mapování

backend = fake_provider.FakeTorontoV2()  # a 27 qubit fake device.
num_steps = 2
qc = get_utility_circuit(backend, num_steps)
obs = SparsePauliOp.from_sparse_list(
[("Z", [13], 1)], num_qubits=backend.num_qubits
) # Falcon
angles = [
0,
0.1,
0.2,
0.3,
0.4,
0.5,
0.6,
0.7,
0.8,
1.0,
np.pi / 2,
] # We try 11 angles for theta_h.

4.2 Kroky 2 a 3: Optimalizace a spuštění (simulátor)

backend_sim = AerSimulator()
transpiled_qc_sim = transpile(
qc, backend_sim, optimization_level=1, layout_method="trivial"
)
transpiled_obs_sim = obs.apply_layout(layout=transpiled_qc_sim.layout)

print(get_circuit_info(qc, reps=1))
print(get_circuit_info(transpiled_qc_sim, reps=1))
27 qubits × 6 layers (23-depth),  Gate breakdown: SDG 112, UNITARY 112, CX 56, R 54
27 qubits × 6 layers (16-depth), Gate breakdown: U3 80, CX 56, R 54, U1 32, U 28

Jeden uživatel spustil následující buňku na MacBooku Pro s čtyřjádrovým procesorem Intel Core i7 2,3 GHz a 32 GB 3LPDDR4X RAM s operačním systémem macOS 14.5. Trvalo to 161 ms reálného času. Každý notebook bude mírně odlišný.

%%time
params = [[p] for p in angles]
estimator = Estimator(mode=backend_sim)
pub = (transpiled_qc_sim, transpiled_obs_sim, params)
result_sim = estimator.run([pub]).result()
CPU times: user 231 ms, sys: 186 ms, total: 417 ms
Wall time: 111 ms

4.3 Kroky 2 a 3: Optimalizace a spuštění (falešné zařízení)

backend_fake = fake_provider.FakeTorontoV2()
transpiled_qc_fake = transpile(
qc, backend_fake, optimization_level=1, layout_method="trivial"
)
transpiled_obs_fake = obs.apply_layout(layout=transpiled_qc_fake.layout)

print(get_circuit_info(qc, reps=1))
print(get_circuit_info(transpiled_qc_fake, reps=1))
27 qubits × 6 layers (23-depth),  Gate breakdown: SDG 112, UNITARY 112, CX 56, R 54
27 qubits × 6 layers (49-depth), Gate breakdown: SDG 324, U1 274, H 162, CX 56, U3 14

Když stejný uživatel spustil následující buňku ve stejném prostředí jako výše, trvalo to 2 min 19 s reálného času. Spuštění Circuitu na falešném zařízení vyvolá zašuměnou simulaci, která trvá mnohem déle než přesná simulace. Doporučujeme nespouštět na falešném zařízení větší Circuit (například 27 Qubitů ×\times 9 vrstev se 3 Trotterovými kroky).

%%time
params = [[p] for p in angles]
estimator = Estimator(mode=backend_fake)
pub = (transpiled_qc_fake, transpiled_obs_fake, params)
result_fake = estimator.run([pub]).result()
CPU times: user 4min 42s, sys: 9.35 s, total: 4min 51s
Wall time: 38.3 s

4.4 Krok 4: Zpracování výsledků

Vykreslíme výsledky z přesné a zašuměné simulace. Uvidíš závažné účinky šumu na FakeToronto.

plt.plot(angles, result_fake[0].data.evs, "o", label="Fake Device")
plt.plot(angles, result_sim[0].data.evs, "o", label="AerSimulator")
plt.xlabel("$\\mathrm{R_x}$ angle $\\theta_h$")
plt.title("$\\langle Z_{13} \\rangle$")
plt.legend()
plt.show()

Výstup předchozí buňky kódu

5. Vyřešení 127qubitové verze problému

Tvým cílem je spustit experiment v měřítku utility, jak bylo zmíněno na začátku. Vytvoříš a provedeš Circuit se 127 qubity a 60 vrstvami (20 Trotterových kroků) s observablem Z62\langle Z_{62} \rangle. Doporučujeme ti zkusit to udělat sám s využitím kódu pro 27qubitovou verzi, kde to dává smysl. Řešení je však uvedeno zde.

Řešení:

5.1 Krok 1: Mapování

# backend_map = service.backend("ibm_brisbane")
backend_map = service.least_busy(operational=True, simulator=False)

num_steps = 20
qc = get_utility_circuit(backend_map, num_steps)
obs = SparsePauliOp.from_sparse_list(
[("Z", [62], 1)], num_qubits=backend_map.num_qubits
) # Eagle
angles = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 1.0, np.pi / 2]

5.2 Kroky 2 a 3: Optimalizace a spuštění

Poznamenejme, že coupling map procesoru Eagle má 144 hran.

# backend = service.backend("ibm_brisbane")
backend = backend_map

transpiled_qc = transpile(qc, backend, optimization_level=1, layout_method="trivial")
transpiled_obs = obs.apply_layout(layout=transpiled_qc.layout)

print(get_circuit_info(qc, reps=1))
print(get_circuit_info(transpiled_qc))
156 qubits × 60 layers (221-depth),  Gate breakdown: SDG 7040, UNITARY 7040, CX 3520, R 3120
156 qubits × 60 layers (201-depth), Gate breakdown: RZ 11933, SX 6240, CZ 3520
params = [[p] for p in angles]
estimator = Estimator(mode=backend)
pub = (transpiled_qc, transpiled_obs, params)
job = estimator.run([pub])

job_id = job.job_id()
print(f"job id={job_id}")
job id=d1479n6qf56g0081sxa0

5.3 Zpracování výsledků

Uvádíme hodnoty pro „mitigované“ body na obrázku 4b v článku o utility. Vynes je společně se svými výsledky.

result_paper = [
1.0171,
1.0044,
0.9563,
0.9602,
0.8394,
0.8120,
0.5466,
0.4556,
0.1953,
0.0141,
0.0117,
]

# REPLACE WITH YOUR OWN JOB ID
job = service.job(job_id)

plt.plot(angles, job.result()[0].data.evs, "o", label=f"{job.backend().name}")
plt.plot(angles, result_paper, "o", label="Utility Paper")
plt.xlabel("$\\mathrm{R_x}$ angle $\\theta_h$")
plt.title("$\\langle Z_{62} \\rangle$")
plt.legend()
plt.show()

Output of the previous code cell

Jsou tvé výsledky podobné „nemitigovaným“ hodnotám na obrázku 4b?   Mohou se velmi lišit v závislosti na zařízení a jeho stavu v době experimentu. Neznepokojuj se samotnými výsledky. Co budeme kontrolovat, je, zda jsi kódování provedl správně. Pokud ano, gratulujeme, dosáhl jsi startovní čáry éry utility.

Stejně jako v článku o utility vědci z celého světa vynaložili ohromnou vynalézavost k získání smysluplných výsledků i v přítomnosti šumu. Konečným cílem tohoto společného úsilí je kvantová výhoda: stav, v němž kvantové počítače dokážou řešit některé problémy využitelné v průmyslu rychleji, s vyšší věrností nebo levněji než klasické počítače. Nepůjde pravděpodobně o jednu událost, ale spíše o éru, v níž klasická reprodukce kvantových výsledků bude trvat stále déle, až se v určitém okamžiku tento kvantový náskok stane kriticky důležitým. Jedno je o kvantové výhodě jasné: dostaneme se k ní pouze prostřednictvím experimentů v měřítku utility. Pokud tento kurz povede k tvému zapojení do této výpravy, která je plná výzev a zábavy, budeme více než rádi.

Reference