Przejdź do głównej treści
Ten projekt jest w wersji beta. Znalazłeś błąd? Otwórz zgłoszenie na GitHub. Masz pytanie lub pomysł? Zapytaj w Discussions — chętnie poznamy Twoją opinię!
Jak oceniasz jakość tłumaczenia tej strony?

Grupowanie wyrazów operatora: wykorzystaj strukturę operatora

Grupowanie jest jedną z operacji w qiskit_fermions.operators.terms, który zbiera procedury dzielące wyrazy operatora na podstawie ich struktury.

Jak opisano w przewodniku o operatorach, operatory mogą przechowywać tablicę grup, opcjonalną część rzadkiej struktury danych, która przypisuje każdemu wyrazowi indeks grupy. Wyrazy o tym samym indeksie grupy tworzą grupę. Dzięki temu możesz systematycznie wykorzystywać tę strukturę w dalszym przetwarzaniu.

Grupując powiązane wyrazy (według własności fizycznych, zależności algebraicznych lub symetrii specyficznych dla problemu), możesz uzyskać kilka korzyści:

  • Zoptymalizowana synteza obwodów: zgrupowane wyrazy niosą informacje specyficzne dla problemu przez cały stos, co pozwala łatwiej optymalizować syntezowane obwody. Skutkuje to mniejszą głębokością obwodu i liczbą bramek.

  • Zachowanie struktury fizycznej: grupy kodują znaczącą strukturę wyjściowego problemu (na przykład wzorce oddziaływań lub symetrie), którą można wykorzystać do bardziej fizycznie sensownych dekompozycji.

  • Wybór zbiorów przepływu: grupowanie może w naturalny sposób kodować zbiory przepływu i inne struktury grafowe istotne dla symulacji kwantowej, jak pokazano w [1].

  • Lepsza stabilność algorytmiczna: jak pokazano w procedurze SqDRIFT, niektóre algorytmy mogą zyskać na stabilności lub wydajności dzięki zachowaniu własności fizycznych.

  • Czynniki wzoru produktowego: fermionowy wzór produktowy dzieli operator na jego grupy, więc grupowanie określa zarówno koszt, jak i błąd Trottera ewolucji w czasie. Zobacz Synteza bramki Evolution w przestrzeni fermionowej.

Użycie​

Najprostszym sposobem zdefiniowania grupowania operatora jest ustawienie atrybutu groups operatora docelowego. Poniżej znajduje się przykład grupowania wyrazów według zbiorów przepływu linii, jak pokazano na rysunku 1c w [1].

Wskazówka

The qiskit_fermions.operators.terms.grouping module provides convenience methods for automatically identifying structure in an operator and assigning group indices accordingly.

>>> 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.

To prosta siatka dwa na trzy modów Majorany połączonych krawędziami skierowanymi. Każda krawędź reprezentuje wyraz operatora działający na dwa połączone mody. Celem jest pogrupowanie tych wyrazów według etykiet krawędzi (pokazanych na grafie). Wyrazy połączone krawędziami o tej samej etykiecie trafiają do tej samej grupy. Poniższy kod pokazuje, jak to zrobić:

>>> 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))]

Analiza grupowania​

Grupy nie narzucają własnego znaczenia: tablica grup zapisuje, które wyrazy należą do siebie, ale nigdy dlaczego. Żadna procedura w tej bibliotece nie raportuje, czy grupowanie jest „poprawne”, ponieważ nie ma jednego pojęcia poprawności, względem którego można by to oceniać. Dalsze etapy przetwarzania zakładają jednak pewne własności grupowania, a funkcje z tej sekcji pozwalają sprawdzić takie założenie z góry, zamiast płacić za nie przy każdym wywołaniu.

Najczęstsza konwencja zakłada, że każda grupa jest osobno hermitowska. To właśnie umożliwia fermionowe losowe wzory produktowe: pojedynczy ap†aqa^\dagger_p a_q z p≠qp \neq q nie jest hermitowski, więc jego ewolucja w czasie nie jest unitarna i nie można go próbkować osobno. Sparowanie go z aq†apa^\dagger_q a_p daje grupę, którą można próbkować.

>>> 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]

Wymuszany jest jeden indeks grupy na wyraz​

Tablica grup przechowuje jeden indeks na wyraz, a przypisanie tablicy o innej długości zgłasza błąd. Żaden dalszy etap nie sprawdza ponownie tego niezmiennika, więc jest on wymuszany tam, gdzie indeksy trafiają do operatora: zbyt krótka tablica po cichu pomijałaby końcowe wyrazy, a zbyt długa wymyślałaby grupy, których nie niesie żaden wyraz.

>>> 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

Czyszczenie indeksów grup przez przypisanie None jest zawsze dozwolone.

Te dwa sprawdzenia są niezależne​

groups_have_uniform_coeffs() raportuje, czy współczynniki grupy są zgodne, co jest założeniem, które przyjmuje group_coeff_means(), uśredniając moduły. Nie jest to słabsza forma sprawdzenia hermitowskości: żadne z tych sprawdzeń nie implikuje drugiego, w żadnym kierunku.

>>> 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])

Domyślnie porównywane są moduły, ponieważ właśnie je uśrednia group_coeff_means(). Hermitowsko sprzężona para o współczynnikach zespolonych ma równe moduły, ale nierówne współczynniki, więc spełnia tylko formę domyślną.

>>> 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]
Uwaga

groups_are_hermitian() dziedziczy jednostronną gwarancję is_hermitian(): wpis True jest zawsze wiarygodny, natomiast wpis False jest wiarygodny tylko dla typów operatorów, których postać normalna jest prawdziwą postacią kanoniczną. Sprawdź dokumentację konkretnego typu operatora, aby dowiedzieć się, który przypadek ma zastosowanie.

[1] (1,2)

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