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?

Seskupování členů operátoru: využij strukturu operátoru

Seskupování je jednou z operací v qiskit_fermions.operators.terms, kde jsou shromážděny rutiny, které rozdělují členy operátoru podle jeho struktury.

Jak je popsáno v průvodci operátory, operátory mohou ukládat pole skupin, volitelnou součást řídké datové struktury, která každému členu přiřazuje index skupiny. Členy se stejným indexem skupiny tvoří skupinu. Díky tomu můžeš strukturu systematicky využívat při dalším zpracování.

Seskupením souvisejících členů (ať podle fyzikálních vlastností, algebraických vztahů, nebo symetrií specifických pro daný problém) získáš několik výhod:

  • Optimalizovaná syntéza obvodů: Seskupené členy přenášejí informace specifické pro problém celým zásobníkem, což umožňuje jednodušší optimalizaci syntetizovaných obvodů. Výsledkem je menší hloubka obvodu a menší počet hradel.

  • Zachování fyzikální struktury: Skupiny kódují smysluplnou strukturu původního problému (například vzory interakcí nebo symetrie), kterou lze využít pro fyzikálně smysluplnější dekompozice.

  • Výběr množin toku: Seskupování může přirozeně kódovat množiny toku a další grafově-teoretické struktury důležité pro kvantovou simulaci, jak ukazuje [1].

  • Lepší algoritmická stabilita: Jak ukazuje rutina SqDRIFT, některé algoritmy mohou těžit z lepší stability nebo výkonu díky zachování fyzikálních vlastností.

  • Faktory součinového vzorce: Fermionový součinový vzorec rozdělí operátor na jeho skupiny, takže seskupení určuje cenu i Trotterovu chybu časového vývoje. Viz Syntéza hradla Evolution ve fermionovém prostoru.

Použití​

Nejjednodušší způsob, jak definovat seskupení operátoru, je nastavit atribut groups cílového operátoru. Níže je příklad seskupení členů podle množin toku po vedení, jak je vidět na obrázku 1c v [1].

Tip

Modul qiskit_fermions.operators.terms.grouping poskytuje pohodlné metody pro automatické rozpoznání struktury v operátoru a odpovídající přiřazení indexů skupin.

>>> import rustworkx as rx
>>> graph = rx.PyDiGraph()
>>> _ = graph.add_nodes_from(range(6))
>>> edges = [(0, 1), (1, 2), (3, 0), (1, 4), (5, 2), (5, 4), (4, 3)]
>>> groups = [0, 0, 1, 2, 1, 3, 3]
>>> edges_with_group_as_payload = [(i, j, g) for (i, j), g in zip(edges, groups)]
>>> _ = graph.add_edges_from(edges_with_group_as_payload)
>>> from rustworkx.visualization import mpl_draw
>>> mpl_draw(
... graph,
... pos={i: (i % 3, i // 3) for i in range(6)},
... edge_labels=str,
... with_labels=True,
... )
<Figure size ... with 1 Axes>

A simple directed graph on which to define a MajoranaOperator.

Jde o jednoduchou mřížku dva krát tři tvořenou Majoranovými módy spojenými orientovanými hranami. Každá hrana představuje člen operátoru působící na dva spojené módy. Cílem je seskupit tyto členy podle popisků hran (zobrazených v grafu). Členy spojené hranami se stejným popiskem se zařadí do stejné skupiny. Následující kód ukazuje, jak to provést:

>>> from qiskit_fermions.operators import MajoranaOperator
>>>
>>> # Construct operator directly using sparse arrays
>>> # 7 terms, each with coefficient 1.0, modes from edge endpoints
>>> op = MajoranaOperator(
... coeffs=[1.0] * 7,
... modes=[node for edge in edges for node in edge],
... boundaries=[0, 2, 4, 6, 8, 10, 12, 14],
... )
>>>
>>> # Assign group indices to terms (groups is part of the sparse data structure)
>>> op.groups = groups
>>>
>>> # Partition operator by groups
>>> grouped_ops = op.split_out_groups()
>>>
>>> # Inspect each group
>>> for i, g in enumerate(grouped_ops):
... print(f"Group {i}: {list(sorted(g.iter_terms()))}")
Group 0: [([0, 1], (1+0j)), ([1, 2], (1+0j))]
Group 1: [([3, 0], (1+0j)), ([5, 2], (1+0j))]
Group 2: [([1, 4], (1+0j))]
Group 3: [([4, 3], (1+0j)), ([5, 4], (1+0j))]

Analýza seskupení​

Skupiny samy o sobě nemají žádný význam: pole skupin zaznamenává, které členy patří k sobě, nikdy však proč. Žádná rutina v této knihovně nehlásí, zda je seskupení „správné“, protože neexistuje jediný pojem správnosti, vůči kterému by to šlo posoudit. Následné konzumenty však určité vlastnosti seskupení předpokládají, a funkce v této části ti umožňují takový předpoklad ověřit předem, místo aby ses za něj platil při každém volání.

Nejběžnější konvencí je, že každá skupina je samostatně hermitovská. Právě to vůbec umožňuje fermionové náhodné součinové vzorce: samotný člen ap†aqa^\dagger_p a_q s p≠qp \neq q není hermitovský, takže jeho časový vývoj není unitární a nelze jej samostatně vzorkovat. Spárováním s aq†apa^\dagger_q a_p vznikne skupina, kterou lze vzorkovat.

>>> from qiskit_fermions.operators import FermionOperator
>>> from qiskit_fermions.operators.terms.grouping import (
... group_coeff_means,
... groups_are_hermitian,
... groups_have_uniform_coeffs,
... )
>>>
>>> # a conjugate pair, plus one unpaired term. The operator is built from the sparse
>>> # arrays rather than from a dictionary, because only the former fixes the term order
>>> # that the group indices below are paired with.
>>> pair_and_single = FermionOperator(
... [1.0, 1.0, 1.0],
... [True, False, True, False, True, False],
... [0, 1, 1, 0, 2, 3],
... [0, 2, 4, 6],
... )
>>> pair_and_single.groups = [0, 0, 1]
>>>
>>> groups_are_hermitian(pair_and_single) # group 1 is unpaired
[True, False]
>>> group_coeff_means(pair_and_single)
[1.0, 1.0]

Pro každý člen se vynucuje jeden index skupiny​

Pole skupin obsahuje jeden index na člen a přiřazení pole jiné délky vyvolá chybu. Nic dále po proudu tuto invarianci znovu nekontroluje, proto se vynucuje tam, kde indexy vstupují do operátoru: příliš krátké pole by tiše zahodilo koncové členy a příliš dlouhé by vymyslelo skupiny, které žádný člen nenese.

>>> pair_and_single.groups = [0, 0] # one index short, on a three-term operator
Traceback (most recent call last):
ValueError: expected one group index per term, but got 2 for 3 terms

Vymazání indexů skupin přiřazením None je vždy povoleno.

Obě kontroly jsou nezávislé​

groups_have_uniform_coeffs() hlásí, zda se koeficienty skupiny shodují, což je předpoklad, který group_coeff_means() činí při průměrování velikostí. Není to slabší forma kontroly hermitovskosti: ani jedna z kontrol nevyplývá z druhé, v žádném směru.

>>> from qiskit_fermions.operators.terms.grouping import (
... groups_are_hermitian,
... groups_have_uniform_coeffs,
... )
>>> # uniform coefficients, but not Hermitian: the terms are not each other's adjoint
>>> uniform = FermionOperator(
... [1.0, 1.0],
... [True, False, True, False],
... [0, 1, 2, 3],
... [0, 2, 4],
... )
>>> uniform.groups = [0, 0]
>>> groups_have_uniform_coeffs(uniform), groups_are_hermitian(uniform)
([True], [False])
>>> # Hermitian, but with mixed magnitudes: two conjugate pairs at different scales
>>> mixed = FermionOperator(
... [1.0, 1.0, 5.0, 5.0],
... [True, False, True, False, True, False, True, False],
... [0, 1, 1, 0, 2, 3, 3, 2],
... [0, 2, 4, 6, 8],
... )
>>> mixed.groups = [0, 0, 0, 0]
>>> groups_are_hermitian(mixed), groups_have_uniform_coeffs(mixed)
([True], [False])

Ve výchozím nastavení se porovnávají velikosti, protože právě ty group_coeff_means() průměruje. Hermitovsky sdružená dvojice s komplexními koeficienty má stejné velikosti, ale nestejné koeficienty, takže splňuje jen výchozí formu.

>>> conjugate = FermionOperator(
... [1.0j, -1.0j],
... [True, False, True, False],
... [0, 1, 1, 0],
... [0, 2, 4],
... )
>>> conjugate.groups = [0, 0]
>>> groups_have_uniform_coeffs(conjugate)
[True]
>>> groups_have_uniform_coeffs(conjugate, abs=False)
[False]
Poznámka

groups_are_hermitian() dědí jednostrannou záruku z is_hermitian(): položka True je vždy spolehlivá, zatímco položka False je spolehlivá jen u typů operátorů, jejichž normální forma je skutečně kanonická. Který případ platí, zjistíš u konkrétního typu operátoru.

[1] (1,2)

A. Gandon et al., Stabilizer-based quantum simulation of fermion dynamics with local qubit encodings, arXiv:2512.11418v2.