Relacja tego pakietu do ffsim
Koncepcje opisane w tym przewodniku są dostępne tylko w API Pythona.
ffsim i ten pakiet dzielą się pracą, zamiast się na siebie nakładać, wzdłuż jednej linii:
| ffsim odpowiada za | qiskit-fermions odpowiada za |
|---|---|
Wysokopoziomowe operatory ansatzów (UCJOpSpinBalanced, UCCSDOpRestrictedReal i ich pokrewne), budowane z amplitud sprzężonych klastrów lub wektora parametrów | Obwód fermionowy (FermionicCircuit), którym stają się te operatory, oraz jego transpilację do obwodu kubitowego w dowolnym kodowaniu fermion–kubit |
| Szybka symulacja w zwartej przestrzeni stałej liczby cząstek | Framework mapperów i struktury danych operatorów, z których zbudowany jest obwód |
Praktyczna konsekwencja działa w obie strony. Matematyka ansatzów mieszka w ffsim, więc UCJ i UCC przyjmują operator ffsim bezpośrednio jako dane wejściowe. Natomiast własne bramki Qiskit z ffsim są specyficzne dla transformacji Jordana-Wignera, więc sprowadzenie jednego z jego operatorów ansatzów przez inne kodowanie to właśnie to, co zyskujesz, przenosząc go tutaj.
Symulacja to miejsce, w którym obie strony się spotykają, i ten przewodnik wyjaśnia, jak. ffsim jest backendem symulacyjnym tego pakietu, osiąganym przez jego własne protokoły, a nie przez opakowanie: przewodnik SKQD wywołuje ffsim.apply_unitary(), ffsim.linear_operator() i ffsim.sample_state_vector() bezpośrednio na obiektach FermionicCircuiti obiektach FermionOperatorbez kroku konwersji.
Podłączenie do interfejsu symulacji ffsim
ffsim to wydajny symulator obwodów kwantowych fermionów, który wykorzystuje zachowanie liczby cząstek i spinu Z, aby reprezentować wektory stanu bardziej zwięźle niż ogólny -wymiarowy wektor stanu kubitów. Definiuje protokoły, które może zaimplementować dowolny obiekt, aby uczestniczyć w jego mechanizmie symulacji (zobacz qiskit_fermions.protocols dla własnych protokołów tego pakietu, które mają ten sam projekt):
-
ffsim.SupportsApplyUnitary, przez metodę_apply_unitary_(vec, norb, nelec, copy), stosującą obiekt jako unitarny do wektora stanu o stałej liczbie cząstek -
ffsim.SupportsLinearOperator, przez metodę_linear_operator_(norb, nelec), zwracającą widok obiektu jakoscipy.sparse.linalg.LinearOperatorw tym samym sektorze -
ffsim.SupportsTrace, przez metodę_trace_(norb, nelec), zwracającą ślad obiektu w tym sektorze.
Ten pakiet implementuje ten interfejs: każda bramka fermionowa w qiskit_fermions.circuit.library udostępnia _apply_unitary_, a FermionOperator udostępnia _linear_operator_ i _trace_. Dzięki temu operatory i obwody tego pakietu są zgodne z ffsim, więc jego narzędzia działają na nich natywnie, bez kroku konwersji. ffsim.apply_unitary() potrafi zasymulować FermionicCircuit od początku do końca, ffsim.linear_operator() potrafi zdiagonalizować FermionOperator, a narzędzia do próbkowania, takie jak ffsim.sample_state_vector() (użyte w przewodniku SKQD do zamiany symulowanego wektora stanu na zliczenia pomiarów), działają bez modyfikacji.
To, co zwraca _linear_operator_, to zwykły LinearOperator z SciPy, więc scipy.sparse.linalg.eigsh() i podobne funkcje działają na nim również.
>>> import ffsim
>>> import numpy as np
>>>
>>> from qiskit_fermions.circuit import FermionicCircuit
>>> from qiskit_fermions.circuit.library import Evolution
>>> from qiskit_fermions.operators import FermionOperator, ann, cre
>>>
>>> norb, nelec = 2, (1, 1)
>>> hamiltonian = FermionOperator.from_terms([
... ([cre(0), ann(1)], 0.5),
... ([cre(1), ann(0)], 0.5),
... ])
>>>
>>> circuit = FermionicCircuit(2 * norb)
>>> circuit.append(Evolution(2 * norb, hamiltonian, time=1.0), circuit.modes)
>>>
>>> reference = ffsim.hartree_fock_state(norb, nelec)
>>> state = ffsim.apply_unitary(reference, circuit, norb=norb, nelec=nelec) # native ffsim call
Sprzężenie przez protokoły ffsim daje to, że jego aktywnie rozwijany ekosystem narzędzi do symulacji i próbkowania stosuje się do obwodów i operatorów tego pakietu bez zmian, a użytkownicy, którzy już pracują z ffsim, mogą dołączać bramki i operatory tego pakietu bez uczenia się kolejnego API symulacji.
ffsim jest opcjonalną zależnością
ffsim jest zadeklarowany jako opcjonalny dodatek (pip install "qiskit-fermions[ffsim]" lub pośrednio przez [all]), zabezpieczony w czasie wykonania przez HAS_FFSIM (jak pokazano powyżej). Jego instalacja odblokowuje symulację: ffsim.SupportsLinearOperator i ffsim.SupportsTrace są zaimplementowane przez konwersję FermionOperator tego pakietu na ffsim.FermionOperator, więc bez ffsim zgłaszają MissingOptionalLibraryError. Wszystko, co nie symuluje (budowanie operatorów, ich mapowanie i transpilacja powstałych obwodów), nie potrzebuje niczego z tego.
Symulacja fermionowa odbywa się w sektorze stałej liczby cząstek
Zarówno _apply_unitary_, jak i _linear_operator_ przyjmują argument nelec i reprezentują wektor stanu w bazie wyznaczników o stałej liczbie cząstek dla danego sektora (norb, nelec), odzwierciedlając ustawienie przestrzeni FCI w ffsim (a pośrednio w PySCF), zamiast pełnej przestrzeni o wymiarze , której użyłby ogólny symulator kubitów. Jest to znacznie mniejsza przestrzeń (jej rozmiar jest iloczynem współczynników dwumianowych, a nie potęgą dwójki), ale wiąże się z twardym ograniczeniem. Można w niej reprezentować tylko operatory i bramki zachowujące liczbę cząstek (a w przypadku ze spinem także liczbę cząstek każdego gatunku spinowego osobno). Operator, którego działanie przeniosłoby amplitudę do innej liczby cząstek, nie ma gdzie jej umieścić.
ffsim rozwiązuje to, odrzucając taki operator wprost: konwersja takiego operatora na operator liniowy zgłasza ValueError wskazujący, które prawo zachowania zawodzi. To właściwe ustawienie domyślne, ponieważ zastosowanie dla hamiltonianu z wyrazem niezachowującym liczby cząstek zamieniłoby w przeciwnym razie mającą być unitarną transformację w nieunitarne, fizycznie pozbawione sensu odwzorowanie.
-
Evolutionujawnia to odrzucenie z operatora, który ewoluuje. -
OrbitalRotationsprawdza, czy jego (osadzona) macierz rotacji jest blokowo-diagonalna względem podziału na alfa/beta w przypadku spinowym, i zgłaszaValueError, jeśli miesza sektory spinowe.
>>> from qiskit_fermions.circuit.library import Evolution
>>>
>>> non_conserving = FermionOperator.from_terms([([cre(0), cre(1)], 1.0)]) # creates 2 particles
>>> gate = Evolution(2 * norb, non_conserving, time=1.0)
>>>
>>> try:
... gate._apply_unitary_(reference, norb, nelec, copy=True)
... except ValueError as exc:
... print("rejected:", exc)
rejected: The given FermionOperator could not be converted to a LinearOperator because it does not conserve particle number and the z component of spin. Conserves particle number: False Conserves spin z: False
Symulacja bez zachowania liczby cząstek jest zatem niemożliwa w przestrzeni fermionowej, z samej konstrukcji reprezentacji ze stałym sektorem. Jest to cena za zwartą przestrzeń FCI, która w ogóle umożliwia praktyczną symulację fermionową. Jeśli twój algorytm potrzebuje operatorów naruszających liczbę cząstek (na przykład natywnego dla kubitów kanału błędów albo operatora zbudowanego dla zmapowanego hamiltonianu, który nie zachowuje liczby cząstek wyraz po wyrazie), najpierw dokonaj transpilacji do kubitów i zasymuluj otrzymany QuantumCircuit za pomocą symulatora na poziomie kubitów. Działa tu każda ścieżka transpilacji; przewodniki o obwodzie fermionowym i transpilacji opisują to szerzej, ale rozsądnym wyborem domyślnym jest generate_preset_jw_pass_manager().
Konwencja bloków spinowych dla układów ze spinem
nelec ma typ int | tuple[int, int], a o wyborze jednego z obsługiwanych układów modów decyduje jego typ, a nie osobna flaga:
-
int wybiera interpretację bezspinową.
norbmodów jest traktowanych bezpośrednio jako orbitale bezspinowe, anelecto łączna liczba cząstek. -
Para
(n_alpha, n_beta)wybiera interpretację ze spinem dla2 * norbmodów według stałej konwencji bloków spinowych. Mody0 .. norbto orbitale alfa (spin w górę), a modynorb .. 2 * norbto orbitale beta (spin w dół). Każdy gatunek spinowy jest zachowywany niezależnie; wyraz tylko-alfa może przenieść elektron między modami alfa, ale nigdy do modu beta, i odwrotnie.
To rozdzielenie następuje w każdym punkcie wejścia protokołu ffsim w tym pakiecie (konstruktory bramek, implementacje _apply_unitary_placed_ oraz kompilacja sektorów natywnego jądra w Rust) i jest to konwencja używana we wszystkich przewodnikach LUCJ i SKQD (na przykład InitializeModes.from_hartree_fock() wpisuje obsadzenia alfa do modów 0 .. n_alpha, a obsadzenia beta do modów norb .. norb + n_beta). Warto porównać to z modułem qiskit_fermions.operators i ogólnie z FermionicCircuit, które używają ogólnych indeksów modów bez wbudowanej semantyki spinu (zobacz przewodnik o obwodzie fermionowym). Znaczenie bloków spinowych jest narzucane dopiero wtedy, gdy do wywołania symulacji zostanie przekazane nelec ze spinem, a nie jest wpisane w samą reprezentację operatora czy obwodu.
>>> from qiskit_fermions.circuit.library import InitializeModes
>>>
>>> norb, nelec = 3, (2, 1)
>>> init = InitializeModes.from_hartree_fock(norb, nelec)
>>> print([bool(occ) for occ in init.occupation]) # alpha modes 0,1; beta mode 3 (= norb + 0)
[True, True, False, True, False, False]
Następne kroki
-
Przejdź przez przewodnik SKQD, aby zobaczyć, jak
ffsim.apply_unitary()iffsim.sample_state_vector()są używane razem do próbkowania obwodów zbudowanych z bramek tego pakietu oraz do obliczania wartości oczekiwanej hamiltonianu przezffsim.linear_operator()(który sam opiera się na tej samej metodzie protokołu_linear_operator_opisanej tutaj). -
Przejdź przez przewodnik LUCJ, aby poznać drugą połowę historii: przeprowadzenie operatora ansatzu ffsim przez potok transpilacji tego pakietu aż do mapy sprzężeń urządzenia.
-
Przeczytaj przewodnik o obwodzie fermionowym, aby poznać ogólne, niezależne od spinu indeksowanie modów używane poza wywołaniami symulacji.
-
Przeczytaj przewodnik o transpilacji, aby dowiedzieć się, jak opuścić przestrzeń fermionową
i symulować na kubitach, co jest wymagane dla operatorów niezachowujących liczby cząstek.
-
Przejrzyj
qiskit_fermions.protocols, aby zobaczyć pełny katalog protokołów definiowanych przez ten pakiet, w tym protokoły konwersji (SupportsFermionOperator,SupportsMajoranaOperator), które nie mają związku z ffsim. -
Przeczytaj własne przewodniki ffsim o przepływach pracy, za które odpowiada, w szczególności o budowaniu ansatzu UCJ i jego wariacyjnej optymalizacji. Wektory parametrów, którymi te przewodniki sterują, są tym, co przyjmują
UCJiUCCza pośrednictwem budowanych przez siebie operatorów.