Přeskočit na hlavní obsah

Fáze Transpileru

Verze balíčků

Kód na této stránce byl vyvinut s použitím následujících požadavků. Doporučujeme používat tyto nebo novější verze.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1

Tato stránka popisuje fáze předpřipraveného transpilačního pipeline v Qiskit SDK. Existuje šest fází:

  1. init
  2. layout
  3. routing
  4. translation
  5. optimization
  6. scheduling

Funkce generate_preset_pass_manager vytvoří přednastavený staged pass manager složený z těchto fází. Konkrétní průchody tvořící každou fázi závisí na argumentech předaných funkci generate_preset_pass_manager. Argument optimization_level je poziční a musí být zadán; jde o celé číslo, které může být 0, 1, 2 nebo 3. Vyšší hodnoty znamenají intenzivnější, ale nákladnější optimalizaci (viz Výchozí nastavení Transpileru a možnosti konfigurace).

Doporučený způsob transpilace obvodu je vytvoření předpřipraveného staged pass manageru a jeho následné spuštění na obvodu, jak je popsáno v části Transpilace s pass managery. Jednodušší, ale méně přizpůsobitelnou alternativou je použití funkce transpile. Tato funkce přijímá obvod přímo jako argument. Stejně jako u generate_preset_pass_manager závisí použité průchody Transpileru na argumentech, jako je optimization_level, předaných funkci transpile. Interně funkce transpile volá generate_preset_pass_manager, aby vytvořila předpřipravený staged pass manager, a spustí ho na obvodu.

Fáze Init

Tato první fáze toho ve výchozím nastavení příliš nedělá a je primárně užitečná, pokud chceš zahrnout vlastní počáteční optimalizace. Protože většina algoritmů pro layout a routing je navržena tak, aby pracovala pouze s jednoQubitovými a dvouQubitovými Gate, tato fáze se také používá k překladu Gate operujících na více než dvou Qubitech na Gate operující pouze na jednom nebo dvou Qubitech.

Další informace o implementaci vlastních počátečních optimalizací pro tuto fázi najdeš v části o pluginech a přizpůsobení pass managerů.

Fáze Layout

Další fáze se týká layoutu nebo konektivity Backend, na který bude Circuit odeslán. Obecně jsou kvantové Circuit abstraktní entity, jejichž Qubity jsou „virtuální" nebo „logické" reprezentace skutečných Qubitů používaných při výpočtech. Pro provedení posloupnosti Gate je nutné mapování 1:1 z „virtuálních" Qubitů na „fyzické" Qubity ve skutečném kvantovém zařízení. Toto mapování je uloženo jako objekt Layout a je součástí omezení definovaných v instrukční sadě architektury (ISA) Backend.

Tento obrázek ilustruje mapování Qubitů z reprezentace drátů do diagramu, který znázorňuje způsob propojení Qubitů na QPU.

Volba mapování je nesmírně důležitá pro minimalizaci počtu operací SWAP potřebných k mapování vstupního Circuit na topologii zařízení a pro zajištění použití nejlépe kalibrovaných Qubitů. Vzhledem k důležitosti této fáze přednastavené pass managery zkouší několik různých metod, jak najít nejlepší layout. Obvykle to zahrnuje dva kroky: nejprve se pokusí najít „perfektní" layout (layout, který nevyžaduje žádné operace SWAP), a poté heuristický průchod, který se pokusí najít nejlepší layout, pokud perfektní layout nelze nalézt. Pro tento první krok se typicky používají dva průchody Passes:

  • TrivialLayout: Naivně mapuje každý virtuální Qubit na fyzický Qubit se stejným číslem na zařízení (tj. [0,1,1,3] -> [0,1,1,3]). Jde o historické chování používané pouze při optimzation_level=1 k pokusu o nalezení perfektního layoutu. Pokud se to nepodaří, zkusí se jako další VF2Layout.
  • VF2Layout: Jde o AnalysisPass, který vybírá ideální layout tím, že tuto fázi chápe jako problém izomorfismu podgrafů řešeného algoritmem VF2++. Pokud je nalezeno více než jedno mapování, spustí se hodnoticí heuristika, která vybere mapování s nejnižší průměrnou chybovostí.

Poté se pro heuristickou fázi ve výchozím nastavení používají dva průchody:

  • DenseLayout: Najde podgraf zařízení s největší konektivitou, který má stejný počet Qubitů jako Circuit (používá se pro optimalizační úroveň 1, pokud jsou v Circuit přítomny operace řídicího toku, jako je IfElseOp).
  • SabreLayout: Tento průchod vybírá layout tak, že začíná od počátečního náhodného layoutu a opakovaně spouští algoritmus SabreSwap. Tento průchod se používá pouze na optimalizačních úrovních 1, 2 a 3, pokud není pomocí průchodu VF2Layout nalezen perfektní layout. Další podrobnosti o tomto algoritmu najdeš v článku arXiv:1809.02573.

Fáze Routing

Aby bylo možné provést dvouQubitovou Gate mezi Qubity, které nejsou přímo propojeny na kvantovém zařízení, je nutné do Circuit vložit jednu nebo více Gate SWAP, které přesunou stavy Qubitů tak, aby byly sousední na mapě Gate zařízení. Každá Gate SWAP představuje nákladnou a hlučnou operaci k provedení. Nalezení minimálního počtu Gate SWAP potřebných k mapování Circuit na dané zařízení je tedy důležitým krokem v procesu transpilace. Z důvodu efektivity se tato fáze ve výchozím nastavení typicky počítá společně s fází Layout, ale jsou od sebe logicky odděleny. Fáze Layout vybírá fyzické Qubity, které se mají použít, zatímco fáze Routing vkládá odpovídající počet Gate SWAP, aby bylo možné Circuit provést s použitím vybraného layoutu.

Nalezení optimálního mapování SWAP je však obtížné. Ve skutečnosti jde o NP-těžký problém, a proto je jeho výpočet pro všechna kromě nejmenších kvantových zařízení a vstupních Circuit prohibitivně nákladný. Aby Qiskit toto obešel, používá stochastický heuristický algoritmus SabreSwap pro výpočet dobrého, avšak ne nutně optimálního, mapování SWAP. Použití stochastické metody znamená, že generované Circuit nejsou zaručeně stejné při opakovaném spuštění. Opakované spuštění stejného Circuit skutečně vede k distribuci hloubek Circuit a počtů Gate na výstupu. Právě z tohoto důvodu mnoho uživatelů volí spuštění funkce routing (nebo celého StagedPassManager) mnohokrát a výběr Circuit s nejmenší hloubkou z distribuce výstupů.

Vezměme si například 15-Qubitový GHZ Circuit spuštěný 100krát s „špatným" (odpojeným) initial_layout.

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib qiskit qiskit-ibm-runtime
import matplotlib.pyplot as plt
from qiskit import QuantumCircuit
from qiskit_ibm_runtime.fake_provider import FakeAuckland, FakeWashingtonV2
from qiskit.transpiler import generate_preset_pass_manager

backend = FakeAuckland()

ghz = QuantumCircuit(15)
ghz.h(0)
ghz.cx(0, range(1, 15))

depths = []
for seed in range(100):
pass_manager = generate_preset_pass_manager(
optimization_level=1,
backend=backend,
layout_method="trivial", # Fixed layout mapped in circuit order
seed_transpiler=seed, # For reproducible results
)
depths.append(pass_manager.run(ghz).depth())

plt.figure(figsize=(8, 6))
plt.hist(depths, align="left", color="#AC557C")
plt.xlabel("Depth", fontsize=14)
plt.ylabel("Counts", fontsize=14)
Text(0, 0.5, 'Counts')

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

Toto široké rozdělení ukazuje, jak obtížné je pro mapper SWAP vypočítat nejlepší mapování. Abychom získali určitý náhled, podívejme se jak na Circuit, který se provádí, tak na Qubity vybrané na hardwaru.

ghz.draw("mpl", idle_wires=False)

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

from qiskit.visualization import plot_circuit_layout

# Plot the hardware graph and indicate which hardware qubits were chosen to run the circuit
transpiled_circ = pass_manager.run(ghz)
plot_circuit_layout(transpiled_circ, backend)

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

Jak vidíš, tento Circuit musí provést dvouQubitovou Gate mezi Qubity 0 a 14, které jsou na grafu konektivity velmi daleko od sebe. Spuštění tohoto Circuit tedy vyžaduje vložení Gate SWAP k provedení všech dvouQubitových Gate pomocí průchodu SabreSwap.

Všimni si také, že algoritmus SabreSwap se liší od větší metody SabreLayout z předchozí fáze. Ve výchozím nastavení SabreLayout spouští jak layout, tak routing a vrací transformovaný Circuit. To se dělá z několika konkrétních technických důvodů uvedených na referenční stránce API průchodu.

Fáze překladu

Při psaní kvantového obvodu můžeš volně používat libovolnou kvantovou bránu (unitární operaci), spolu s kolekcí nebránových operací, jako jsou instrukce pro měření qubitů nebo jejich resetování. Většina kvantových zařízení však nativně podporuje jen hrstku kvantových bránových a nebránových operací. Tyto nativní brány jsou součástí definice ISA cíle a tato fáze přednastavených PassManagerů překládá (neboli rozbaluje) brány zadané v obvodu do nativní sady bází bran příslušného Backend. Jde o důležitý krok, protože umožňuje Backend obvod spustit, ale zpravidla vede ke zvýšení hloubky a počtu bran.

Zvláště důležité jsou dva speciální případy, které dobře ilustrují, co tato fáze dělá.

  1. Pokud brána SWAP není nativní branou cílového Backend, jsou k její realizaci potřeba tři brány CNOT:
print("native gates:" + str(sorted(backend.operation_names)))
qc = QuantumCircuit(2)
qc.swap(0, 1)
qc.decompose().draw("mpl")
native gates:['cx', 'delay', 'for_loop', 'id', 'if_else', 'measure', 'reset', 'rz', 'switch_case', 'sx', 'x']

Output of the previous code cell

Jako součin tří bran CNOT je SWAP nákladnou operací na šumových kvantových zařízeních. Takovéto operace jsou však obvykle nezbytné pro vložení obvodu do omezené bránové konektivity mnoha zařízení. Minimalizace počtu bran SWAP v obvodu je proto primárním cílem transpilačního procesu.

  1. Toffoliho brána neboli brána řízená-řízená-not (ccx) je tříqubitová brána. Protože naše sada bází obsahuje pouze jedno- a dvouqubitové brány, musí být tato operace rozložena. Je to však poměrně nákladné:
qc = QuantumCircuit(3)
qc.ccx(0, 1, 2)
qc.decompose().draw("mpl")

Output of the previous code cell

Za každou Toffoliho bránu v kvantovém obvodu může hardware provést až šest bran CNOT a několik jednoqubitových bran. Tento příklad ukazuje, že každý algoritmus využívající více Toffoliho bran skončí jako obvod s velkou hloubkou, a tedy bude výrazně ovlivněn šumem.

Fáze optimalizace

Tato fáze se soustředí na rozklad kvantových obvodů do sady bází bran cílového zařízení a musí čelit zvýšené hloubce způsobené fázemi layoutu a routování. Naštěstí existuje mnoho rutin pro optimalizaci obvodů kombinováním nebo eliminováním bran. V některých případech jsou tyto metody tak účinné, že výstupní obvody mají menší hloubku než vstupní, a to i po mapování layoutu a routování na hardwarovou topologii. V jiných případech toho moc udělat nelze a výpočet může být na šumových zařízeních obtížně proveditelný. Právě v této fázi se začínají lišit jednotlivé úrovně optimalizace.

Kromě toho tato fáze také provádí několik závěrečných kontrol, aby se ujistila, že všechny instrukce v obvodu jsou složeny z bází bran dostupných na cílovém Backend.

Níže uvedený příklad se stavem GHZ demonstruje vliv různých nastavení úrovně optimalizace na hloubku obvodu a počet bran.

poznámka

Výstup transpilace se liší kvůli stochastickému SWAP mapperu. Čísla níže se proto pravděpodobně změní při každém spuštění kódu.

15-qubit GHZ state

Následující kód sestaví 15-qubitový stav GHZ a porovnává optimization_levels transpilace z hlediska výsledné hloubky obvodu, počtu bran a počtu vícequbitových bran.

ghz = QuantumCircuit(15)
ghz.h(0)
ghz.cx(0, range(1, 15))

depths = []
gate_counts = []
multiqubit_gate_counts = []
levels = [str(x) for x in range(4)]
for level in range(4):
pass_manager = generate_preset_pass_manager(
optimization_level=level,
backend=backend,
seed_transpiler=1234,
)
circ = pass_manager.run(ghz)
depths.append(circ.depth())
gate_counts.append(sum(circ.count_ops().values()))
multiqubit_gate_counts.append(circ.count_ops()["cx"])

fig, (ax1, ax2) = plt.subplots(2, 1)
ax1.bar(levels, depths, label="Depth")
ax1.set_xlabel("Optimization Level")
ax1.set_ylabel("Depth")
ax1.set_title("Output Circuit Depth")
ax2.bar(levels, gate_counts, label="Number of Circuit Operations")
ax2.bar(levels, multiqubit_gate_counts, label="Number of CX gates")
ax2.set_xlabel("Optimization Level")
ax2.set_ylabel("Number of gates")
ax2.legend()
ax2.set_title("Number of output circuit gates")
fig.tight_layout()
plt.show()

Output of the previous code cell

Plánování

Tato poslední fáze se spouští, pouze pokud je o ni explicitně požádáno (podobně jako fáze Init) a ve výchozím nastavení se nespouští (přestože metodu lze zadat nastavením argumentu scheduling_method při volání generate_preset_pass_manager). Fáze plánování se typicky používá poté, co byl obvod přeložen do cílové sady bází, namapován na zařízení a optimalizován. Tyto průchody se zaměřují na zahrnutí veškeré doby nečinnosti v obvodu. Na vysoké úrovni lze průchod plánování chápat jako explicitní vkládání instrukcí zpoždění, které zajišťují dobu nečinnosti mezi spuštěními bran a umožňují zjistit, jak dlouho bude obvod na Backend spuštěn.

Zde je příklad:

ghz = QuantumCircuit(5)
ghz.h(0)
ghz.cx(0, range(1, 5))

# Use fake backend
backend = FakeWashingtonV2()

# Run with optimization level 3 and 'asap' scheduling pass
pass_manager = generate_preset_pass_manager(
optimization_level=3,
backend=backend,
scheduling_method="asap",
seed_transpiler=1234,
)

circ = pass_manager.run(ghz)
circ.draw(output="mpl", idle_wires=False)

Output of the previous code cell

Circuit with delay instructions

Transpiler vložil instrukce Delay, aby zohlednil dobu nečinnosti na každém qubitu. Abychom lépe pochopili načasování obvodu, můžeme se na něj podívat také pomocí funkce timeline.draw():

timeline.draw() view of the same circuit

Plánování obvodu se skládá ze dvou částí: analýzy a mapování omezení, následovaných průchodem doplňování. První část vyžaduje spuštění průchodu analýzy plánování (ve výchozím nastavení je to ALAPSchedulingAnalysis), který analyzuje obvod a zaznamenává čas zahájení každé instrukce v obvodu do harmonogramu. Jakmile má obvod počáteční harmonogram, lze spustit další průchody, které zohledňují případná časová omezení cílového Backend. Nakonec lze provést průchod doplňování, například PadDelay nebo PadDynamicalDecoupling.

Další kroky

Doporučení