Come questo pacchetto si rapporta a ffsim
I concetti di questa guida sono disponibili solo nell'API Python.
ffsim e questo pacchetto si dividono il lavoro anziché sovrapporsi, lungo un'unica linea:
| ffsim si occupa di | qiskit-fermions si occupa di |
|---|---|
Gli operatori di ansatz di alto livello (UCJOpSpinBalanced, UCCSDOpRestrictedReal e i loro simili), costruiti da ampiezze coupled-cluster o da un vettore di parametri | Il circuito fermionico (FermionicCircuit) in cui quegli operatori si trasformano, e la sua transpilazione in un circuito di qubit con qualsiasi codifica da fermioni a qubit |
| Simulazione veloce in uno spazio compatto a numero di particelle fisso | Il framework dei mapper e le strutture dati degli operatori da cui è costruito il circuito |
La conseguenza pratica vale in entrambe le direzioni. La matematica dell'ansatz risiede in ffsim, quindi UCJ e UCC accettano direttamente un operatore ffsim come input. Viceversa, i gate Qiskit propri di ffsim sono specifici della trasformazione di Jordan-Wigner, quindi ciò che ottieni portandolo qui è poter abbassare uno dei suoi operatori di ansatz attraverso una codifica diversa.
La simulazione è il punto in cui i due si incontrano, e questa guida spiega come. ffsim è il backend di simulazione di questo pacchetto, raggiunto tramite i suoi protocolli anziché un wrapper: la guida SKQD chiama ffsim.apply_unitary(), ffsim.linear_operator() e ffsim.sample_state_vector() direttamente su oggetti FermionicCircuite su oggetti FermionOperatorsenza alcun passaggio di conversione.
Integrarsi con l'interfaccia di simulazione di ffsim
ffsim è un simulatore ad alte prestazioni per circuiti quantistici fermionici che sfrutta la conservazione del numero di particelle e dello spin-Z per rappresentare i vettori di stato in modo più compatto di un generico statevector di qubit a dimensioni. Definisce protocolli che qualsiasi oggetto può implementare per partecipare al suo meccanismo di simulazione (vedi qiskit_fermions.protocols per i protocolli propri di questo pacchetto, che seguono lo stesso design):
-
ffsim.SupportsApplyUnitary, tramite il metodo_apply_unitary_(vec, norb, nelec, copy), che applica l'oggetto come unitario a un vettore di stato a numero di particelle fisso -
ffsim.SupportsLinearOperator, tramite il metodo_linear_operator_(norb, nelec), che restituisce una vistascipy.sparse.linalg.LinearOperatordell'oggetto su quello stesso settore -
ffsim.SupportsTrace, tramite il metodo_trace_(norb, nelec), che restituisce la traccia dell'oggetto su quel settore.
Questo pacchetto implementa quell'interfaccia: ogni gate fermionico in qiskit_fermions.circuit.library fornisce _apply_unitary_, e FermionOperator fornisce _linear_operator_ e _trace_. Questo è ciò che rende gli operatori e i circuiti di questo pacchetto compatibili con ffsim, così i suoi strumenti funzionano su di essi in modo nativo, senza alcun passaggio di conversione. ffsim.apply_unitary() può simulare un FermionicCircuit da un capo all'altro, ffsim.linear_operator() può diagonalizzare un FermionOperator, e utilità di campionamento come ffsim.sample_state_vector() (usata nella guida SKQD per trasformare uno statevector simulato in conteggi di misura) funzionano senza modifiche.
Ciò che _linear_operator_ restituisce è un normale LinearOperator di SciPy, quindi anche scipy.sparse.linalg.eigsh() e simili funzionano su di esso.
>>> 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
L'accoppiamento tramite i protocolli di ffsim è ciò che rende possibile tutto questo: il suo ecosistema, in sviluppo attivo, di utilità di simulazione e campionamento si applica invariato ai circuiti e agli operatori di questo pacchetto, e chi già lavora con ffsim può integrare i gate e gli operatori di questo pacchetto senza imparare un'altra API di simulazione.
ffsim è una dipendenza opzionale
ffsim è dichiarato come extra opzionale (pip install "qiskit-fermions[ffsim]", oppure in modo transitivo da [all]), protetto a runtime da HAS_FFSIM (come mostrato sopra). Installarlo sblocca la simulazione: ffsim.SupportsLinearOperator e ffsim.SupportsTrace sono implementati convertendo il FermionOperator di questo pacchetto in un ffsim.FermionOperator, quindi senza ffsim sollevano MissingOptionalLibraryError. Tutto ciò che non simula (costruire operatori, mapparli e transpilare i circuiti risultanti) non ne ha bisogno.
La simulazione fermionica avviene in un settore a numero di particelle fisso
Sia _apply_unitary_ sia _linear_operator_ accettano un argomento nelec e rappresentano il vettore di stato sulla base dei determinanti a numero di particelle fisso per quel settore (norb, nelec), rispecchiando la configurazione dello spazio FCI di ffsim (e, transitivamente, di PySCF), anziché sullo spazio completo di dimensione che userebbe un simulatore di qubit generico. Questo è uno spazio molto più piccolo (la sua dimensione è un prodotto di coefficienti binomiali anziché una potenza di due), ma comporta una restrizione rigida. Solo gli operatori e i gate che preservano il numero di particelle (e, nel caso con spin, il numero di particelle di ciascuna specie di spin singolarmente) possono esservi rappresentati. Un operatore la cui azione sposterebbe ampiezza su un numero di particelle diverso non ha dove metterla.
ffsim risolve il problema rifiutando del tutto un operatore di questo tipo: convertirne uno in un operatore lineare solleva un ValueError che indica quale legge di conservazione non è rispettata. È l'impostazione predefinita corretta, perché applicare per un Hamiltoniano con un termine che non conserva il numero di particelle trasformerebbe altrimenti una presunta mappa unitaria in una mappa non unitaria e fisicamente priva di significato.
-
Evolutionfa emergere quel rifiuto dall'operatore che evolve. -
OrbitalRotationverifica che la sua matrice di rotazione (incorporata) sia diagonale a blocchi rispetto alla suddivisione alpha/beta nel caso con spin, e solleva unValueErrorse mescola i settori di spin.
>>> 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
La simulazione che non conserva il numero di particelle è quindi fuori portata nello spazio fermionico, per costruzione della rappresentazione a settore fisso, e questo è il prezzo dello spazio FCI compatto che rende trattabile la simulazione fermionica fin dall'inizio. Se il tuo algoritmo richiede operatori che violano il numero di particelle (per esempio, un canale di errore nativo dei qubit, o un operatore costruito per un Hamiltoniano mappato che non conserva il numero di particelle termine per termine), esegui prima la transpilazione sui qubit e simula il QuantumCircuit risultante con un simulatore a livello di qubit. Qualsiasi percorso di transpilazione funziona a questo scopo; le guide sul circuito fermionico e sulla transpilazione entrano più nel dettaglio, ma generate_preset_jw_pass_manager() è una scelta predefinita ragionevole.
La convenzione block-spin per i sistemi con spin
nelec è di tipo int | tuple[int, int], e il suo tipo, non un flag separato, determina quale dei layout di modi supportati viene selezionato:
-
Un int seleziona l'interpretazione senza spin (spinless). I
norbmodi sono trattati direttamente come orbitali senza spin, enelecè il numero totale di particelle. -
Una coppia
(n_alpha, n_beta)seleziona l'interpretazione con spin di2 * norbmodi secondo una convenzione block-spin fissa. I modi0 .. norbsono gli orbitali alpha (spin up) e i modinorb .. 2 * norbsono gli orbitali beta (spin down). Ogni specie di spin si conserva indipendentemente; un termine solo alpha può spostare un elettrone tra modi alpha ma mai in un modo beta, e viceversa.
Questo smistamento avviene in ogni punto di ingresso del protocollo ffsim in questo pacchetto (costruttori di gate, implementazioni di _apply_unitary_placed_ e compilazione dei settori del kernel Rust nativo), ed è la convenzione usata in tutte le guide LUCJ e SKQD (per esempio, InitializeModes.from_hartree_fock() inserisce le occupazioni alpha nei modi 0 .. n_alpha e le occupazioni beta nei modi norb .. norb + n_beta). Vale la pena contrapporla al modulo qiskit_fermions.operators e a FermionicCircuit in generale, che usano indici di modo generici senza alcuna semantica di spin intrinseca (vedi la guida al circuito fermionico). Il significato block-spin viene imposto solo quando si fornisce un nelec con spin a una chiamata di simulazione, non è incorporato nella rappresentazione dell'operatore o del circuito stessa.
>>> 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]
Prossimi passi
-
Segui la guida SKQD per vedere
ffsim.apply_unitary()effsim.sample_state_vector()usati insieme per campionare circuiti costruiti con i gate di questo pacchetto, e per valutare il valore di aspettazione di un Hamiltoniano tramiteffsim.linear_operator()(a sua volta basato sullo stesso metodo di protocollo_linear_operator_descritto qui). -
Segui la guida LUCJ per l'altra metà della storia: portare un operatore ansatz di ffsim attraverso la pipeline di transpilazione di questo pacchetto e fino alla mappa di accoppiamento di un dispositivo.
-
Leggi la guida al circuito fermionico per l'indicizzazione generica dei modi, indipendente dallo spin, usata al di fuori delle chiamate di simulazione.
-
Leggi la guida alla transpilazione per scoprire come uscire dallo spazio fermionico
e simulare sui qubit, cosa necessaria per gli operatori che non conservano le particelle.
-
Consulta
qiskit_fermions.protocolsper il catalogo completo dei protocolli definiti da questo pacchetto, inclusi i protocolli di conversione (SupportsFermionOperator,SupportsMajoranaOperator) che non hanno a che fare con ffsim. -
Leggi le guide di ffsim per i flussi di lavoro di cui è responsabile, in particolare costruire un ansatz UCJ e ottimizzarlo in modo variazionale. I vettori di parametri che queste guide gestiscono sono ciò che
UCJeUCCaccettano, tramite gli operatori che costruiscono.