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?

Návrhové principy reprezentací operátorů

Tento průvodce vysvětluje společné návrhové principy a základní koncepty sdílené všemi reprezentacemi operátorů v modulu operators.

Přehled​

Reprezentace operátorů poskytované tímto modulem sdílejí několik základních návrhových principů:

  • Řídká datová struktura: Operátory kódují pouze neidentitní operace. Vnitřní uspořádání dat je obecně inspirováno formáty řídkých matic, což umožňuje efektivní uložení a výpočet pro systémy s mnoha módy, ale poměrně málo významnými příspěvky.

  • Iterace a rekonstrukce členů: Bez ohledu na vnitřní řídké uložení poskytují operátory konzistentní iterační rozhraní, takže můžeš členy prohlížet, filtrovat a transformovat, aniž bys rozuměl podkladové datové struktuře, a poté z upravených členů rekonstruovat nové operátory.

  • Indexování podle módů: Operátory používají abstraktní indexy módů k označení fermionických stupňů volnosti, což umožňuje flexibilní mapování z fyzikálních systémů na reprezentaci operátoru.

  • Seskupování členů a komutační vztahy: Operátory nativně podporují informace o seskupení, které přiřazují členům indexy skupin. To umožňuje optimalizace a zachování fyzikální struktury bez potřeby samostatných datových struktur. Praktické použití najdeš v průvodci seskupováním.

  • Aritmetické a matematické operace: Všechny operátory implementují konzistentní sadu aritmetických operací (jako sčítání, násobení a skládání) a matematických funkcí pomocí protokolu OperatorTrait, což umožňuje jednotný kód napříč různými typy operátorů.

  • Řazení členů operátoru a normální formy: Matematicky ekvivalentní operátory mohou mít v kvantových algoritmech velmi odlišné reprezentace a chování. Všechny reprezentace operátorů podporují různé normální formy (založené na komutačních vztazích specifických pro danou algebru), aby bylo dosaženo kanonických, předvídatelných a optimalizovatelných reprezentací operátorů.

Řídká datová struktura​

Všechny operátory používají řídkou reprezentaci, v níž se sledují pouze neidentitní operace. Každá neidentitní operace se skládá z koeficientu (komplexní číslo) a posloupnosti akcí na konkrétních módech.

Tento přístup dramaticky snižuje spotřebu paměti i dobu výpočtu, zejména u systémů s mnoha módy, ale poměrně málo významnými příspěvky. Kódováním pouze neidentitních operací se operace soustředí jen na to, na čem záleží, což umožňuje pracovat s velkými systémy, které by s hustými reprezentacemi byly neproveditelné. Operátory se navíc přirozeně škálují na libovolný počet módů; operátor působící na módy {0, 1} funguje beze změny v systémech s mnohem více módy, protože nedotčené módy jsou implicitně identita.

Shodné členy se při aritmetických operacích uchovávají samostatně a v případě potřeby je nutné je výslovně sloučit.

Vnitřní formát uložení​

Operátory jsou interně uloženy v polích inspirovaných formáty řídkých matic:

  • Pole koeficientů: Komplexní koeficient každého členu

  • Pole indexů módů: Fermionické módy, na které každá akce působí

  • Pole hranic: Indexy označující, kde v poli módů začínají a končí módy každého členu

Důležité

V závislosti na typu operátoru mohou být přítomna další pole. Například instance FermionOperator obsahují pole akcí booleovských hodnot, které určuje typ fermionické akce působící na příslušný index módu. Naproti tomu třída MajoranaOperator toto rozlišení nepotřebuje, protože tuto informaci kóduje v paritě indexu módu. Úplný formát uložení najdeš v dokumentaci API pro svůj typ operátoru.

Následující příklady ukazují, jak jsou tato pole organizována. Nejprve přímá konstrukce pomocí řídkých polí:

>>> from qiskit_fermions.operators import FermionOperator
>>>
>>> # Construct operators directly using sparse arrays
>>> # First operator: 1.0 * +0 -1
>>> op1 = FermionOperator(
... coeffs=[1.0],
... actions=[True, False],
... modes=[0, 1],
... boundaries=[0, 2],
... )
>>>
>>> # Second operator: 1.0 * +2 -3
>>> op2 = FermionOperator(
... coeffs=[1.0],
... actions=[True, False],
... modes=[2, 3],
... boundaries=[0, 2],
... )
>>>
>>> # Combine the sparse operators
>>> op1 += op2
>>> print(format(op1))
1.000000e0 +0.000000e0j * (+0 -1)
1.000000e0 +0.000000e0j * (+2 -3)

Praktické metody konstrukce​

Pro vývojáře v Pythonu je k dispozici několik praktických metod konstrukce, které skrývají detaily řídkého uložení. Usnadňují sestavování operátorů, aniž by ses musel starat o správu polí koeficientů, módů a hranic:

>>> from qiskit_fermions.operators import FermionOperator, cre, ann
>>>
>>> # Construct operators using operator algebra notation
>>> op1 = FermionOperator.from_dict({(cre(0), ann(1)): 1.0})
>>> op2 = FermionOperator.from_dict({(cre(2), ann(3)): 1.0})
>>>
>>> # The result is sparse even when combining them
>>> op1 += op2
>>> print(format(op1))
1.000000e0 +0.000000e0j * (+0 -1)
1.000000e0 +0.000000e0j * (+2 -3)
Tip

Jednotlivé implementace operátorů mohou podporovat další metody konstrukce přizpůsobené jejich konkrétnímu použití. Všechny dostupné možnosti konstrukce pro svůj typ operátoru najdeš v dokumentaci API.

Iterace přes členy a rekonstrukce​

Operátory poskytují jednotné iterační rozhraní pomocí OperatorTrait.iter_terms() bez ohledu na jejich vnitřní řídkou reprezentaci. Členy tak můžeš prohlížet, filtrovat nebo transformovat, aniž bys musel rozumět podkladové datové struktuře. Z transformovaných členů pak můžeš pomocí OperatorTrait.from_terms() znovu sestavit nový operátor.

>>> from qiskit_fermions.operators import FermionOperator, cre, ann
>>>
>>> # Construct an operator with terms of different orders
>>> op = FermionOperator.from_dict({
... (): 0.5, # constant term (order 0)
... (cre(0), ann(1)): 1.0, # two-body term (order 2)
... (cre(0), cre(1), ann(1), ann(0)): 0.25 # four-body term (order 4)
... })
>>>
>>> # Filter to keep only terms of order 2
>>> order_two_terms = [
... (term, coeff) for term, coeff in op.iter_terms()
... if len(term) == 2
... ]
>>>
>>> # Reconstruct operator from filtered terms
>>> filtered_op = FermionOperator.from_terms(order_two_terms)
>>> print(f"Original operator has {len(op)} terms")
Original operator has 3 terms
>>> print(f"Filtered operator has {len(filtered_op)} term")
Filtered operator has 1 term

Indexování podle módů​

Všechny reprezentace operátorů označují indexy, na které jejich členy působí, jako módy. Mód je prostě index identifikující fermionový stupeň volnosti ve tvém systému. Mapování fyzikálních stupňů volnosti (například prostorových orbitalů, spinových stavů nebo jiných kvantových čísel) na indexy módů je ponecháno na uživateli, což umožňuje maximální flexibilitu.

Tato abstrakce je přítomná také v modulu qiskit_fermions.circuit, kde FermionicCircuit pracuje s registrem fermionových módů. V reprezentaci operátorů i obvodů poskytují módy jednotný způsob nezávislý na algebře, jak určit, které stupně volnosti se dané operace účastní.

Důležité

Současné implementace používají módy bez spinu: Všechny reprezentace operátorů, které tento modul aktuálně nabízí, zacházejí s módy jako s fermionovými stupni volnosti bez spinu. To znamená, že pokud tvůj systém obsahuje elektrony nebo fermiony se spinem nahoru i dolů, musíš je explicitně namapovat na různé módy (například módy 0-3 pro spin nahoru čtyř prostorových orbitalů, módy 4-7 pro spin dolů, nebo jakoukoli jinou konvenci, kterou zvolíš).

Tento návrh udržuje základní reprezentace jednoduché a obecné a zároveň nevnucuje konkrétní konvenci řazení spinů. Pomocné moduly jako qiskit_fermions.operators.library nabízejí praktické funkce (například FCIDump.from_file()), které při načítání dat o elektronové struktuře taková mapování vyřeší za tebe.

Tip

S vývojem balíčku mohou přibýt reprezentace operátorů se spinem, které budou spinové stupně volnosti podporovat přímo ve svém datovém modelu. Budou jasně odlišeny od současných implementací bez spinu a budou s nimi v modulu koexistovat.

Seskupování členů a komutační vztahy​

Stejně jako koeficienty a indexy módů mohou operátory volitelně ukládat pole skupin, které každému členu přiřazuje index skupiny. Tím, že je seskupování zabudováno přímo do reprezentace operátoru jako součást řídké datové struktury, putuje informace o seskupení přirozeně s operátorem při transformacích. To umožňuje systematické využití struktury, ať už pochází z fyzikálních vlastností, algebraických vztahů nebo symetrií specifických pro daný problém. Strukturované informace pak lze využít v navazujících operacích, jako je syntéza a dekompozice obvodů, pomocí metod jako OperatorTrait.split_out_groups().

Podrobný návod, jak ve svých pracovních postupech seskupovat členy operátorů, najdeš v průvodci seskupování.

>>> from qiskit_fermions.operators import MajoranaOperator, gamma
>>> op = MajoranaOperator.from_dict({
... (gamma(0, False),): 1.0,
... (gamma(1, False),): 1.0,
... (gamma(2, False), gamma(3, False)): 1.0
... })
>>> # Assign group indices to terms
>>> op.groups = [0, 0, 1]
>>> # Partition operator by groups
>>> grouped_ops = op.split_out_groups()

Aritmetické a matematické operace​

Všechny operátory implementují protokol OperatorTrait, který poskytuje jednotnou sadu operací napříč různými typy operátorů. Díky tomu kód napsaný pro jednu reprezentaci operátoru funguje stejně i s ostatními. Jak tento protokol souvisí s dalšími protokoly definovanými v tomto balíčku, se dozvíš v qiskit_fermions.protocols.

Protokol zahrnuje aritmetické operace (například sčítání, násobení a skládání), strukturální operace (iterace přes členy, analýza podpory módů, přeznačování), matematické funkce (normální uspořádání, zjednodušování, kontrola ekvivalence) a další. Úplný přehled všech dostupných operací najdeš v dokumentaci OperatorTrait.

>>> from qiskit_fermions.operators import FermionOperator, cre, ann
>>>
>>> # Construct a Hermitian operator: H = +0 -1 + +1 -0
>>> op = FermionOperator.from_dict({
... (cre(0), ann(1)): 1.0,
... (cre(1), ann(0)): 1.0
... })
>>>
>>> # Check if the operator is Hermitian
>>> print(f"Operator is Hermitian: {op.is_hermitian(atol=1e-10)}")
Operator is Hermitian: True
Poznámka

is_hermitian() ověřuje, že H−H†=0H - H^\dagger = 0, tak, že tento rozdíl převede na normální tvar a porovná ho s zero(). Rozepsáno pomocí ostatních operací protokolu provádí následující:

>>> difference = (op - op.adjoint()).normal_ordered().simplify(atol=1e-10)
>>> difference.equiv(FermionOperator.zero(), atol=1e-10)
True

Síla této kontroly závisí na typu operátoru, protože závisí na tom, jak úplně normální tvar daného typu kontrahuje sousední generátory. Výsledek True je vždy spolehlivý, ale u některých typů je výsledek False konzervativní: podrobnosti viz is_hermitian().

Důležité

Příklad používá atol=1e-10. Parametr atol (absolutní tolerance) určuje práh. Koeficienty s velikostí menší než atol se považují za nulové a zahazují se. To je zásadní pro numerickou stabilitu při porovnávání operátorů, protože aritmetika s pohyblivou řádovou čárkou může způsobit drobné zaokrouhlovací chyby, které by jinak bránily rozpoznání ekvivalentních operátorů.

Tip

Protokol OperatorTrait poskytuje společné rozhraní, ale jednotlivé implementace operátorů mohou nabízet další praktické metody, které součástí protokolu nejsou. Například FermionOperator a MajoranaOperator nabízejí metodu max_rank(), která vrací délku nejdelšího členu. Všechny dostupné funkce pro svůj typ operátoru zjistíš v dokumentaci API.

Uspořádání členů operátorů a normální tvary​

Základní výzvou v algebře kvantových operátorů je, že matematicky ekvivalentní operátory lze reprezentovat mnoha různými způsoby, z nichž každý má jiné důsledky pro kvantové algoritmy. Stejný operátor lze zapsat v algebraicky ekvivalentních tvarech (například a†ba^\dagger b lze vyjádřit jako ba†+[a†,b]ba^\dagger + [a^\dagger,b]), a přesto tyto reprezentace vedou k odlišnému chování při syntéze obvodů, zjednodušování a v numerických algoritmech.

Reprezentace operátorů podporují operace normálního uspořádání, které pomocí komutačních relací transformují operátory do kanonických tvarů specifických pro danou algebru. To umožňuje spolehlivé porovnávání (dva ekvivalentní operátory mají shodné normálně uspořádané tvary), odhaluje zjednodušení (komutační relace způsobují, že se členy vyruší nebo sloučí) a podporuje algoritmy, které pro správnost a efektivitu vyžadují konkrétní tvary operátorů.

Důležité

Různé reprezentace operátorů mohou implementovat normální uspořádání na základě různých komutačních relací odpovídajících jejich algebře. Například fermionové normální uspořádání využívá antikomutační relace ({ci,cj†}=δij\{c_i, c_j^\dagger\} = \delta_{ij}), zatímco majoranovské normální uspořádání používá jiné algebraické konvence ({γi,γj}=2δij\{\gamma_i, \gamma_j\} = 2\delta_{ij}). Jak je normální uspořádání implementováno, zjistíš v dokumentaci pro svůj typ operátoru.

>>> from qiskit_fermions.operators import FermionOperator, cre, ann
>>>
>>> # Two different representations of the same operator
>>> op1 = FermionOperator.from_dict({(ann(0), cre(0)): 1.0})
>>> op2 = FermionOperator.from_dict({(): 1.0, (cre(0), ann(0)): -1.0})
>>>
>>> # Direct comparison fails due to different forms
>>> op1.equiv(op2, atol=1e-10)
False
>>>
>>> # Normal-order both and compare again
>>> op1_normal = op1.normal_ordered()
>>> op2_normal = op2.normal_ordered()
>>> op1_normal.equiv(op2_normal, atol=1e-10)
True
Tip

Metoda protokolu OperatorTrait.normal_ordered() záměrně nespecifikuje své poziční a pojmenované argumenty, takže konkrétní implementace operátorů mohou definovat nastavitelné parametry, které řídí přesný vytvářený kanonický tvar. To umožňuje optimalizace specifické pro operátor a varianty normálních tvarů přizpůsobené jejich algebře nebo případu užití. Dostupné parametry zjistíš v dokumentaci API pro svůj typ operátoru.