Skip to main content
IBM Quantum Platform

In che modo questo pacchetto è correlato a ffsim

Importante

I concetti illustrati in questa guida sono disponibili esclusivamente nell'API " Python ".

ffsim e questo pacchetto si dividono il lavoro anziché sovrapporsi, seguendo un unico filone:

ffsim possiede
qiskit-fermions possiede
Gli operatori di ansatz di alto livello (UCJOpSpinBalanced, e UCCSDOpRestrictedReal i loro omologhi), costruiti a partire da ampiezze del metodo coupled-cluster o da un vettore di parametriIl circuito fermionico (FermionicCircuit) in cui si trasformano tali operatori e la sua trasposizione in un circuito a qubit mediante qualsiasi codifica da fermioni a qubit
Simulazione veloce in uno spazio compatto a numero fisso di particelleIl framework di mappatura e le strutture dati degli operatori che costituiscono il circuito

La conseguenza pratica vale in entrambi i sensi. La matematica di Ansatz è implementata in ffsim, quindi e UCJ UCC accettano direttamente un operatore ffsim come input. Al contrario, i gate Qiskit propri di ffsim sono specifici per la trasformazione di Jordan-Wigner, quindi il vantaggio di utilizzarli in questo contesto consiste proprio nel poter semplificare uno dei suoi operatori di ansatz tramite una codifica diversa.

È proprio nella simulazione che le due cose si incontrano, e questa guida spiega come. ffsim è il backend di simulazione di questo pacchetto, accessibile tramite i propri protocolli anziché tramite un wrapper: la guida SKQD chiama ffsim.apply_unitary(), ffsim.linear_operator(), e direttamente ffsim.sample_state_vector() sugli FermionicCircuitoggetti e sugli FermionOperatoroggetti, senza alcuna fase di conversione.


Collegamento all'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 rispetto a un generico vettore di stato a 2n2^n -dimensioni per i qubit. Definisce i protocolli che qualsiasi oggetto può implementare per partecipare al suo meccanismo di simulazione (si veda qiskit_fermions.protocols per i protocolli propri di questo pacchetto, che seguono lo stesso schema):

Questo pacchetto implementa tale interfaccia: ogni gate fermionico in qiskit_fermions.circuit.library fornisce _apply_unitary_, mentre FermionOperator fornisce e _linear_operator_ _trace_. È proprio questo che rende gli operatori e i circuiti di questo pacchetto compatibili con ffsim, per cui i suoi strumenti funzionano su di essi in modo nativo, senza alcuna fase di conversione. ffsim.apply_unitary() è in grado di simulare un FermionicCircuit end-to-end, ffsim.linear_operator() può diagonalizzare un FermionOperator, e le utilità di campionamento come ffsim.sample_state_vector() (utilizzate nella guida SKQD per trasformare un vettore di stato simulato in conteggi di misura) funzionano senza modifiche.

Il risultato _linear_operator_ è un normale oggetto di tipo SciPy LinearOperator, quindi anche scipy.sparse.linalg.eigsh() and friends 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

È proprio l’integrazione tramite i protocolli di ffsim a rendere possibile tutto questo: il suo ecosistema di utilità di simulazione e campionamento, in continua evoluzione, si applica senza modifiche ai circuiti e agli operatori di questo pacchetto, e gli utenti che già utilizzano ffsim possono integrare i gate e gli operatori di questo pacchetto senza dover apprendere un’altra API di simulazione.


ffsim è una dipendenza facoltativa

ffsim è dichiarato come extra opzionale (pip install "qiskit-fermions[ffsim]", o transitivamente da [all]), protetto in fase di esecuzione da HAS_FFSIM (come illustrato sopra). L'installazione sblocca la simulazione: e ffsim.SupportsLinearOperator ffsim.SupportsTrace vengono implementati convertendo il di questo pacchetto in FermionOperator un ffsim.FermionOperator, quindi senza ffsim generano un MissingOptionalLibraryError. Tutto ciò che non riguarda la simulazione (la creazione di operatori, la loro mappatura e la transpilazione dei circuiti risultanti) non ne ha bisogno.

ffsim dipende in modo transitivo da PySCF, che non supporta Windows; pertanto, su Windows il comando “extra” non produce alcun risultato (un’operazione silenziosa senza effetto tramite un sys_platform marcatore) anziché generare un errore di installazione. Gli utenti Windows che desiderano eseguire la simulazione possono farlo, per il momento, tramite il Sottosistema Windows per l' Linux.


La simulazione fermionica si svolge in un settore con un numero fisso di particelle

Sia _apply_unitary_ che _linear_operator_ accettano un nelec argomento e rappresentano il vettore di stato sulla base determinante a numero fisso di particelle per quel (norb, nelec) settore, rispecchiando la configurazione dello spazio FCI di ffsim (e, per traslazione, di PySCF’s ), anziché lo spazio completo a dimensioni 2num_modes2^{\text{num\_modes}} che un simulatore di qubit generico utilizzerebbe. Si tratta di uno spazio molto più piccolo (la sua dimensione è il prodotto dei coefficienti binomiali anziché una potenza di due), ma presenta una restrizione rigida. In esso possono essere rappresentati solo gli operatori e i gate che conservano il numero di particelle (e, nel caso in cui lo spin sia non nullo, il numero di particelle di ciascuna specie di spin singolarmente). Un operatore la cui azione comporterebbe lo spostamento dell'ampiezza su un numero diverso di particelle non avrebbe dove collocarla.

ffsim risolve il problema rifiutando categoricamente un operatore di questo tipo: la conversione di un operatore in un operatore lineare genera un errore che indica ValueError quale legge di conservazione non viene rispettata. Questa è l'impostazione predefinita corretta, poiché l'applicazione dell' exp⁡(−itH)\exp(-i t H) e a un hamiltoniano HH contenente un termine che non conserva le particelle trasformerebbe altrimenti una mappa potenzialmente unitaria in una mappa non unitaria e fisicamente priva di significato.

  • Evolution superfici che, a seguito del rifiuto da parte dell’operatore, si evolvono.
  • OrbitalRotation verifica che la sua matrice di rotazione (incorporata) sia diagonale a blocchi rispetto alla suddivisione alfa/beta nel caso con spin, e genera un'eccezione ValueError se 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 preserva le particelle è quindi impossibile nello spazio fermionico, per come è costruita la rappresentazione a settore fisso, e questo è il prezzo da pagare per lo spazio FCI compatto che rende la simulazione fermionica fattibile in primo luogo. Se il tuo algoritmo richiede operatori che violano il numero di particelle (ad esempio, un canale di errore nativo per i qubit o un operatore costruito per un hamiltoniano mappato che non conserva il numero di particelle termine per termine), effettua prima la transpilazione in qubit e simula il risultato QuantumCircuit utilizzando invece un simulatore a livello di qubit. In questo caso va bene qualsiasi percorso di transpilazione; il circuito fermionico e le guide alla transpilazione forniscono maggiori dettagli, ma generate_preset_jw_pass_manager() è una scelta predefinita ragionevole.


La convenzione "block-spin" per i sistemi con spin

nelec viene specificato il tipo int | tuple[int, int], ed è proprio il tipo, e non un flag separato, a determinare quale delle configurazioni di modalità supportate venga selezionata:

  • Un int indica l'interpretazione senza spin. Le modalità norb sono considerate direttamente come orbitali privi di spin, e nelec è il numero totale di particelle.
  • Una coppia (n_alpha, n_beta) seleziona l’interpretazione “spinful” delle modalità 2 * norb secondo una convenzione fissa di “block-spin”. Le modalità 0 .. norb corrispondono agli orbitali alfa (spin verso l'alto) e le modalità norb .. 2 * norb corrispondono agli orbitali beta (spin verso il basso). Ogni specie di spin è conservata in modo indipendente; un termine esclusivamente alfa può spostare un elettrone tra i modi alfa, ma mai in un modo beta, e viceversa.

Questa trasmissione avviene in ogni punto di ingresso del protocollo ffsim presente in questo pacchetto (costruttori di gate, _apply_unitary_placed_ implementazioni e compilazione dei settori del kernel nativo di Rust) ed è la convenzione utilizzata in tutte le guide LUCJ e SKQD (ad esempio, InitializeModes.from_hartree_fock() inserisce le occupazioni alfa nelle modalità 0 .. n_alpha e le occupazioni beta nelle modalità norb .. norb + n_beta). È opportuno mettere a confronto questo approccio con il modulo qiskit_fermions.operators e, più in FermionicCircuit generale, con quelli che utilizzano indici di modo generici privi di semantica intrinseca dello spin (si veda la guida ai circuiti fermionici ). Il significato di "block-spin" viene imposto solo quando nelec viene fornito uno "spinful" a una chiamata di simulazione, e non è integrato nella rappresentazione dell'operatore o del circuito stesso.

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

Passi successivi

Questa pagina è stata utile?
Segnala un bug, un errore di battitura o richiedi contenuti su GitHub.