Přeskočit na hlavní obsah
Tento projekt je v betě. Našel jsi chybu? Otevřít issue na GitHubu. Máš otázku nebo nápad? Zeptej se v Discussions — rádi si poslechneme tvou zpětnou vazbu!
Jaká je kvalita překladu na této stránce?

Optimalizace přípravy Slaterova determinantu

Důležité

Koncepty v tomto průvodci jsou v současnosti dostupné pouze v Python API. Ekvivalentní funkcionalita bude v C API zpřístupněna v budoucím vydání.

Hradlo InitializeModes deklaruje referenční obsazení módů; hradlo OrbitalRotation rotuje jednočásticovou bázi. Umístěna těsně za sebou, obě hradla připraví Slaterův determinant; módy začínají v referenčním determinantu a pak se rotují do cílových orbitalů. Hradlo PrepareSlaterDeterminant toto složení zachycuje a protože nese referenční obsazení, může jeho transpilace použít obdélníkovou givens_decomposition_slater(), která realizuje pouze obsazené orbitaly, místo úplného čtvercového rozkladu, který vyžaduje samotné OrbitalRotation. To je striktně levnější syntéza (viz přínos na konci tohoto průvodce).

Transpiler pass MergeSlaterDeterminantPreparation ve FermionicCircuit rozpozná vzor InitializeModes následovaný OrbitalRotation a přepíše ho na hradla PrepareSlaterDeterminant, takže pozdější fáze syntézy automaticky využije redukovaný rozklad. Při simulaci je přepis na stavu bez účinku: PrepareSlaterDeterminant je validate-then-rotate (ověří referenční obsazení a poté aplikuje rotaci, stejně jako dvě samostatná hradla), takže sloučení odemkne levnější syntézu, aniž by změnilo připravený stav.

V celém tomto průvodci je každý obvod nakreslen před průchodem passu a po něm, vedle sebe: vstup vlevo a výsledek běhu passu na něm vpravo. Dělají to dvě pomocné funkce. merge spustí pass na kopii obvodu (přes jeho FermionicDAGCircuit) a převede výsledek zpět na vykreslitelný FermionicCircuit a draw_merge vykreslí obě poloviny do jediného obrázku před/po:

>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>>
>>> from qiskit_fermions.circuit import FermionicCircuit
>>> from qiskit_fermions.circuit.library import InitializeModes, OrbitalRotation
>>> from qiskit_fermions.transpiler import FermionicCircuitToDAG
>>> from qiskit_fermions.transpiler.converters import FermionicDAGToCircuit
>>> from qiskit_fermions.transpiler.passes import MergeSlaterDeterminantPreparation
>>>
>>> def merge(circuit):
... dag = FermionicCircuitToDAG().run(circuit)
... merged = MergeSlaterDeterminantPreparation().run(dag)
... return FermionicDAGToCircuit().run(merged)
>>>
>>> def natural_size(circuit):
... # the figure size Qiskit picks for a circuit on its own (discard the probe figure)
... probe = circuit.draw("mpl")
... size = probe.get_size_inches()
... plt.close(probe)
... return size
>>>
>>> def draw_merge(circuit):
... merged = merge(circuit)
... # let Qiskit size each half naturally, then lay them out side by side
... (bw, bh), (aw, ah) = natural_size(circuit), natural_size(merged)
... fig, (before, after) = plt.subplots(1, 2, figsize=(bw + aw, max(bh, ah)))
... circuit.draw("mpl", ax=before)
... merged.draw("mpl", ax=after)
... before.set_title("before")
... after.set_title("after")
... return fig

Co se sloučí​

Pass rozpoznává tři vzory, všechny vycházející z konvence módů po blocích podle spinu, kterou používají InitializeModes a OrbitalRotation. Pro systém s norb prostorovými orbitaly jsou módy 0..norb sektorem spinu alfa a norb..2*norb sektorem spinu beta. Příprava Slaterova determinantu se provádí pro každý spinový sektor zvlášť, protože každý sektor má svůj vlastní tvar (elektrony, orbitaly).

Celý registr (nebo bez spinu)​

Nejjednodušší vzor je použití InitializeModes, po němž bezprostředně následuje OrbitalRotation na stejných módech. To je případ bez spinu (jeden determinant přes všechny módy) a také spinový obvod, jehož rotace zasahuje oba sektory najednou. Obě hradla se spojí do jediného PrepareSlaterDeterminant:

>>> circuit = FermionicCircuit(4)
>>> circuit.append(InitializeModes([1, 1, 0, 0]), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(4)), circuit.modes)
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

A mode initialization and rotation fusing into one Slater determinant preparation.

Podle spinového sektoru​

Je to stejný tvar, ale omezený na jednu spinovou polovinu. Zde se dva nezávislé páry inicializace a rotace, jeden na sektor, každý samostatně spojí do PrepareSlaterDeterminant:

>>> circuit = FermionicCircuit(6) # norb = 3: alpha modes 0..3, beta modes 3..6
>>> circuit.append(InitializeModes([1, 1, 0]), circuit.modes[:3])
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[:3])
>>> circuit.append(InitializeModes([1, 0, 0]), circuit.modes[3:])
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[3:])
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

Two per-sector initialize-and-rotate pairs, each fusing into its own preparation.

Globální inicializace, rotace podle spinu​

Jde o jediné InitializeModes přes celý registr (2 * norb), po němž následují dvě akce OrbitalRotation, jedna na každé souvislé spinové polovině. Pass rozdělí globální obsazení na hranici sektorů a vydá dvě hradla PrepareSlaterDeterminant. Obě rotace se mohou objevit v libovolném pořadí.

Tento tvar vzniká v pracovním postupu lokálního unitárního klastrového Jastrowa (LUCJ) (viz průvodce LUCJ); uživatel umístí InitializeModes (typicky Hartreeho-Fockovu referenci pomocí from_hartree_fock()) na začátek obvodu a připojí ansatz UCJ, jehož první dvě orbitalové rotace podle spinu po rozložení ansatzu bezprostředně navazují na inicializaci.

>>> circuit = FermionicCircuit(6) # norb = 3, nelec = (2, 1)
>>> circuit.append(InitializeModes.from_hartree_fock(3, (2, 1)), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[:3])
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[3:])
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

A global initialization with two per-spin rotations fusing into two preparations.

Tento vzor se uplatní i tehdy, když je hradlem bezprostředně za inicializací rotována jen jedna spinová polovina, což je tvar vznikající, když na začátku obvodu náhodou stojí orbitalová rotace jen jednoho spinového sektoru. Rotovaná polovina se spolu se svou rotací stane PrepareSlaterDeterminant a druhá polovina se připraví s identitní rotací. Identitní příprava se syntetizuje na referenční hradla X, která by pro tuto polovinu stejně vydalo InitializeModes, takže doplnění nestojí žádná další hradla a přitom odemkne redukovanou Slaterovu syntézu na rotované polovině:

>>> circuit = FermionicCircuit(6)
>>> circuit.append(InitializeModes.from_hartree_fock(3, (2, 1)), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[:3])
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

A global initialization with a single per-spin rotation fusing into two preparations.

Co zůstane nedotčeno​

Pass je záměrně konzervativní. Spojuje pouze tehdy, když se obě hradla skutečně skládají do přípravy Slaterova determinantu, a vše ostatní zkopíruje beze změny. „Bezprostředně následuje“ se chápe nad grafem toku dat obvodu. OrbitalRotation se spojí jen tehdy, když je InitializeModes jeho jediným předchůdcem napříč všemi jeho módy. Na obrázcích níže jsou stavy před a po shodné: nic se nespojilo.

Operace mezi nimi​

Pokud se mezi inicializací a rotací dotkne módů jakékoli hradlo, už se neskládají do holé přípravy, takže se nic nesloučí:

>>> from qiskit_fermions.circuit.library import Evolution
>>> from qiskit_fermions.operators import FermionOperator, ann, cre
>>>
>>> circuit = FermionicCircuit(4)
>>> circuit.append(InitializeModes([1, 1, 0, 0]), circuit.modes)
>>> number_op = FermionOperator.from_dict({(cre(0), ann(0)): 1.0})
>>> circuit.append(Evolution(4, number_op, time=0.5), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(4)), circuit.modes)
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

An operation between the initialization and rotation blocks the merge.

Částečná rotace, která není spinovou polovinou​

Rotace pokrývající přesně jednu souvislou spinovou polovinu inicializace celého registru se spojí (viz předchozí část). Rotace na jakékoli jiné částečné množině módů (méně módů než inicializace a ne jedna z jejích dvou spinových polovin, například podrozsah přesahující hranici sektorů) však není přípravou po sektorech. Jeho sloučení by potichu zahodilo roli inicializace na módech, které vynechává, a proto pass nechá obě hradla na místě:

>>> circuit = FermionicCircuit(4) # norb = 2: the only spin halves are modes [0, 1] and [2, 3]
>>> circuit.append(InitializeModes([1, 1, 0, 0]), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[:3])
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

A partial rotation that is not a spin half is left unmerged.

Proč na tom záleží: přínos syntézy​

Sloučení se vyplatí, protože PrepareSlaterDeterminant se syntetizuje na mnohem méně hradel než OrbitalRotation, které pohlcuje. Determinant s mm elektrony nad nn orbitaly potřebuje nejvýše m(n−m)m(n-m) dvouqubitových rotací a žádná diagonální fázová hradla, oproti n(n−1)/2n(n-1)/2 rotacím plus nn fázím úplné čtvercové orbitalové rotace.

Předvolený pass manager pro Jordanovu-Wignerovu transformaci spouští MergeSlaterDeterminantPreparation ve své optimalizační fázi, takže k této redukci dochází automaticky. Transpiluj přípravu dvou elektronů v šesti orbitalech až na QuantumCircuit a spočítej jeho hradla:

Poznámka

Předvolba spouští MergeOrbitalRotations bezprostředně před MergeSlaterDeterminantPreparation. To je důležité, když za inicializací následuje řada po sobě jdoucích hradel OrbitalRotation. Dřívější pass nejprve sloučí řadu do jediné rotace, kterou pak MergeSlaterDeterminantPreparation smrští s inicializací do jediného PrepareSlaterDeterminant. Bez tohoto pořadí by se pohltila jen první rotace řady a zbytek by se syntetizoval úplným čtvercovým rozkladem.

>>> from qiskit_fermions.transpiler.presets import generate_preset_jw_pass_manager
>>>
>>> # a reproducible random 6 x 6 orbital rotation
>>> rng = np.random.default_rng(1)
>>> z = rng.standard_normal((6, 6)) + 1j * rng.standard_normal((6, 6))
>>> q, r = np.linalg.qr(z)
>>> rotation = q * (np.diagonal(r) / np.abs(np.diagonal(r)))
>>>
>>> circuit = FermionicCircuit(6)
>>> circuit.append(InitializeModes([1, 1, 0, 0, 0, 0]), circuit.modes)
>>> circuit.append(OrbitalRotation(rotation), circuit.modes)
>>>
>>> pm = generate_preset_jw_pass_manager()
>>> prepared = pm.run(circuit)
>>> print(dict(sorted(prepared.count_ops().items())))
{'x': 2, 'xx_plus_yy': 8}
>>> prepared.draw("mpl", fold=-1)
<Figure size ... with 1 Axes>

The synthesized merged Slater determinant preparation.

Porovnej to se syntézou téže orbitalové rotace samotné. Bez předcházející inicializace není co sloučit, takže předvolba sáhne po úplném čtvercovém rozkladu, který je hlubší a nese diagonální fázová hradla:

>>> reference = FermionicCircuit(6)
>>> reference.append(OrbitalRotation(rotation), reference.modes)
>>> reference_prepared = pm.run(reference)
>>> print(dict(sorted(reference_prepared.count_ops().items())))
{'rz': 6, 'xx_plus_yy': 15}
>>> reference_prepared.draw("mpl", fold=-1)
<Figure size ... with 1 Axes>

The synthesized bare orbital rotation, with its extra phase gates.

Redukovaný rozklad vypouští všech šest fázových hradel a téměř o polovinu snižuje počet dvouqubitových rotací. Připravený determinant je správný až na globální fázi (fyzikálně pro přípravu stavu nepodstatnou), kterou by fázová hradla opravila.

Dva způsoby, jak hradlo rozložit​

PrepareSlaterDeterminant lze rozložit dvěma různými způsoby a nejsou zaměnitelné:

decompose()​

Rozloží hradlo na jeho definici: InitializeModes reference, za nímž následuje úplné OrbitalRotation. To je ekvivalentní hradlu jako operátoru, pro libovolný vstupní stav.

PrepareSlaterDeterminant.synthesis, aplikovaná pomocí F2FSynthesis​

Nahradí hradlo redukovaným rozkladem z tohoto průvodce. To je ekvivalentní hradlu pouze jako příprava stavu: vytvoří stejný determinant, až na globální fázi, když módy začínají ve vakuu. Je levnější právě proto, že to předpokládá.

Syntézu použij, když hradlo připravuje stav na vakuu, k čemuž hradlo slouží a co za tebe dělá transpiler. decompose() použij, když hradlo musí nadále působit jako operátor, například proto, že před ním na stejných módech stojí jiná hradla, nebo když chceš prostá hradla prozkoumat či simulovat. Pass F2FSynthesis nemůže vědět, o který případ jde, takže je na obvodu, který mu předáš, aby determinant připravil z vakua.

Simulace obvodu pomocí ffsim nepotřebuje ani jedno: každé hradlo aplikuje celý svůj unitární operátor, takže obvod obsahující PrepareSlaterDeterminant se simuluje tak, jak je, a zůstává na rychlé cestě pro okolní hradla OrbitalRotation. Rozložení pouze Slaterova hradla pomocí circuit.decompose("PrepareSlaterDeterminant") také ponechává tyto rotace celé.

Viz také