Skip to main content
IBM Quantum Platform

FermionOperator

class FermionOperator(coeffs, actions, modes, boundaries)

Basi: object

Un operatore fermionico senza spin.


Definizione

Questo operatore è definito da una combinazione lineare di prodotti di operatori di creazione e annichilimento fermionici che agiscono su modi fermionici privi di spin. In altre parole, i singoli termini soddisfano le seguenti relazioni di anticommutatività: [1]

{ai†,aj†}={ai,aj}=0,  and  {ai,aj†}=δij ,\left\{a^\dagger_i, a^\dagger_j\right\} = \left\{a_i, a_j\right\} = 0,~~\text{and}~~ \left\{a_i, a^\dagger_j\right\} = \delta_{ij} \, ,

dove ii e jj non distinguono la specie di spin delle modalità fermioniche a cui fanno riferimento.

Ne consegue che la definizione dell'intero operatore è la seguente:

FermionOperator=∑ici⨂jAj^ ,\text{\texttt{FermionOperator}} = \sum_i c_i \bigotimes_j \hat{A_j} \, ,

dove Aj^∈{aj,aj†}\hat{A_j} \in \{ a_j, a^\dagger_j \} e cic_i è il coefficiente (complesso) che costituisce la combinazione lineare dei prodotti. L'indice jj può assumere qualsiasi valore compreso tra 0 e il numero di modi fermionici su cui agisce l'operatore meno 1.


Implementazione

Questa classe memorizza i termini e i coefficienti in più vettori sparsi, analogamente al formato "compressed sparse row" comunemente utilizzato per le matrici sparse. Più precisamente, un singolo operatore contiene 4 array:

Rubrica 1
Rubrica 2
coeffsUn vettore di coefficienti complessi costituito da due numeri in virgola mobile a 64 bit.
actionsUn vettore di valori booleani che memorizza la natura delle operazioni di seconda quantizzazione.
modesUn vettore di interi a 32 bit che memorizza gli indici delle modalità fermioniche su cui è stata eseguita l'operazione.
boundariesUn vettore di numeri interi che indica i confini in e actions modes.

Le voci in actions indicano operatori di creazione (annichilimento) da parte di ( True``False ). Le modalità fermioniche indicizzate da modes sono considerate prive di spin.

Nota

È possibile accedere a copie in sola lettura di questi array interni tramite i rispettivi metodi: get_coeffs(), get_actions(), get_modes(), e get_boundaries().

Questa struttura dati consente di creare e manipolare gli operatori in modo molto efficiente. Tuttavia, ciò implica che in qualsiasi momento un operatore possa contenere termini duplicati. Questi devono essere risolti manualmente utilizzando simplify().

Costruzioni

È possibile creare un operatore direttamente fornendo gli array descritti sopra:

>>> from qiskit_fermions.operators import FermionOperator
>>> coeffs = [1.0, 2.0, -3.0, 4.0j, -0.5j]
>>> actions = [True, False, False, True, True, True, False, False]
>>> modes = [0, 0, 0, 1, 0, 1, 2, 3]
>>> boundaries = [0, 0, 1, 2, 4, 8]
>>> op = FermionOperator(coeffs, actions, modes, boundaries)
>>> print(format(op))
  1.000000e0 +0.000000e0j * ()
 -3.000000e0 +0.000000e0j * (-0)
  0.000000e0 +4.000000e0j * (-0 +1)
  2.000000e0 +0.000000e0j * (+0)
 -0.000000e0-5.000000e-1j * (+0 +1 -2 -3)

Per comodità, è possibile creare un operatore a partire da un dizionario di tipo “ Python ” in questo modo:

>>> from qiskit_fermions.operators import cre, ann
>>> op = FermionOperator.from_dict(
...     {
...         (): 1.0,
...         (cre(0),): 2.0,
...         (ann(0),): -3.0,
...         (ann(0), cre(1)): 4.0j,
...         (cre(0), cre(1), ann(2), ann(3)): -0.5j,
...     }
... )
>>> print(format(op))
  1.000000e0 +0.000000e0j * ()
 -3.000000e0 +0.000000e0j * (-0)
  0.000000e0 +4.000000e0j * (-0 +1)
  2.000000e0 +0.000000e0j * (+0)
 -0.000000e0-5.000000e-1j * (+0 +1 -2 -3)

In questo esempio, abbiamo utilizzato cre() e ann() per definire gli operatori di creazione e di annichilimento nelle modalità specificate.

Inoltre, sono disponibili i seguenti metodi di costruzione e di aiuto rapido:

Rubrica 1
Rubrica 2
zero()Costruisce l'operatore di identità additiva.
one()Crea l'operatore di identità moltiplicativa.
from_terms(termini)Crea un nuovo operatore a partire da un iteratore di termini (vedi anche iter_terms()).
from_terms_with_groups(termini)Crea un nuovo operatore a partire da un iteratore di termini con gruppi (vedi anche iter_terms_with_groups()).

Formattazione

Negli esempi precedenti, gli operatori creati sono stati visualizzati utilizzando l'output di format(), il che permette di ottenere una rappresentazione dell'operatore leggibile dall'utente.

>>> print(format(op))
  1.000000e0 +0.000000e0j * ()
 -3.000000e0 +0.000000e0j * (-0)
  0.000000e0 +4.000000e0j * (-0 +1)
  2.000000e0 +0.000000e0j * (+0)
 -0.000000e0-5.000000e-1j * (+0 +1 -2 -3)
Nota

L'ordine di stampa di format(op) viene ordinato in modo esplicito prima della stampa. Pertanto, non riflette l’ordine dei termini all’interno dell’operatore.

È possibile ottenere una forma alternativa dalla funzione repr() , che fornisce una rappresentazione interpretabile nel Python. In altre parole, questo output può essere facilmente copiato e incollato in una shell Python :

>>> print(repr(op))
FermionOperator.from_dict({...})

Infine, per gli operatori di grandi dimensioni, entrambi questi output potrebbero risultare molto lunghi e indesiderabili. A questo punto, dalla funzione str() è possibile ottenere un modulo molto semplice con informazioni minime:

>>> print(str(op))
<FermionOperator with 5 terms>

Iterazione

Poiché la struttura dati sottostante è implementata in Rust e presenta un layout non banale, non è possibile iterarla direttamente:

>>> list(iter(op))
Traceback (most recent call last):
  ...
TypeError: 'qiskit_fermions.operators.fermion_operator.FermionOperator' object is not iterable

Questa classe fornisce invece degli iteratori personalizzati per soddisfare tale scopo:

>>> list(sorted(op.iter_terms()))
[([], (1+0j)), ([(False, 0)], (-3+0j)), ([(False, 0), (True, 1)], 4j), ([(True, 0)], (2+0j)), ([(True, 0), (True, 1), (False, 2), (False, 3)], (-0-0.5j))]
Vedi anche

iter_terms()

Per ulteriori dettagli relativi all'implementazione.

La tabella seguente elenca tutti gli iteratori disponibili:

Rubrica 1
Rubrica 2
iter_terms()Un iteratore sui termini dell'operatore.
iter_terms_with_groups()Un iteratore sui termini dell'operatore con il relativo indice di gruppo.

Aritmetica

Sono supportate le seguenti operazioni aritmetiche:

Addizione/Sottrazione

>>> op = FermionOperator.one()
>>> (op + op).simplify()
FermionOperator.from_dict({(): 2+0j})
>>> (op - op).simplify()
FermionOperator.from_dict({})
>>> op += op
>>> op.simplify()
FermionOperator.from_dict({(): 2+0j})
>>> op -= op
>>> op.simplify()
FermionOperator.from_dict({})

Moltiplicazione e divisione scalari

>>> op = FermionOperator.one()
>>> (2 * op).simplify()
FermionOperator.from_dict({(): 2+0j})
>>> (op / 2).simplify()
FermionOperator.from_dict({(): 0.5+0j})
>>> op *= 2
>>> op.simplify()
FermionOperator.from_dict({(): 2+0j})
>>> op /= 2
>>> op.simplify()
FermionOperator.from_dict({(): 1+0j})

Composizione dell'operatore

Nota

La composizione degli operatori corrisponde alla moltiplicazione a sinistra: c = a & b corrisponde a C=BAC = B A. In altre parole, la composizione di due operatori restituisce un operatore risultante che esegue “prima a e poi ”. b

>>> op1 = FermionOperator.from_dict({(): 2.0, (cre(0),): 3.0})
>>> op2 = FermionOperator.from_dict({(): 1.5, (ann(1),): 4.0})
>>> comp = (op1 & op2).simplify()
>>> print(format(comp))
  3.000000e0 +0.000000e0j * ()
  8.000000e0 +0.000000e0j * (-1)
  1.200000e1 +0.000000e0j * (-1 +0)
  4.500000e0 +0.000000e0j * (+0)
>>> op2 &= op1
>>> print(format(op2.simplify()))
  3.000000e0 +0.000000e0j * ()
  8.000000e0 +0.000000e0j * (-1)
  4.500000e0 +0.000000e0j * (+0)
  1.200000e1 +0.000000e0j * (+0 -1)
>>> squared = (op1 ** 2).simplify()
>>> print(format(squared))
  4.000000e0 +0.000000e0j * ()
  1.200000e1 +0.000000e0j * (+0)
  9.000000e0 +0.000000e0j * (+0 +0)
Nota

Per comodità, la moltiplicazione a destra viene implementata tramite c = a @ b (il che porta a C=ABC = A B ).

>>> (op1 @ op2).equiv(op2 & op1)
True

Altre operazioni

Oltre ai metodi magici corrispondenti alle operazioni aritmetiche descritte in precedenza, sono disponibili i seguenti metodi:

Rubrica 1
Rubrica 2
adjoint()Restituisce il coniugato ermitiano (o aggiunto) di questo operatore.
ichop( [atol] )Rimuove i termini il cui coefficiente è inferiore alla soglia specificata.
simplify( [atol] )Restituisce un operatore equivalente ma semplificato.
normal_ordered( [panino] )Restituisce un operatore equivalente con termini ordinati normalmente.
relabel_modes(permutazione)Restituisce un nuovo operatore con le modalità rinominate.

Proprietà

Infine, esistono vari metodi per verificare determinate proprietà di un operatore:

Rubrica 1
Rubrica 2
is_hermitian( [atol] )Restituisce se questo operatore è ermitiano.
max_rank()Restituisce il rango massimo dei termini presenti in questo operatore.
conserves_particle_number()Indica se questo operatore conserva il numero di particelle.

[ 1 ]

https://en.wikipedia.org/wiki/Second _quantizzazione#Operatori_di_creazione_e_annichilimento_dei_fermioni


Attributi

groups

Un vettore facoltativo di indici di gruppo per ciascun termine.

Per ulteriori informazioni, consultare il modulo grouping .


Metodi

adjoint

adjoint()

Restituisce il coniugato ermitiano (o aggiunto) di questo operatore.

Ciò influisce sui termini e sui coefficienti come segue:

  • le azioni in ogni termine invertono il loro ordine e alternano creazione e annichilimento
  • i coefficienti sono complessi coniugati
>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.from_dict({(): -1.0j, ((True, 0), (False, 1)): 1.0})
>>> adj = op.adjoint()
>>> print(format(adj))
 -0.000000e0 +1.000000e0j * ()
  1.000000e0 -0.000000e0j * (+1 -0)
conserves_particle_number

conserves_particle_number()

Indica se questo operatore conserva il numero di particelle.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.from_dict({((True, 0), (False, 1)): 1})
>>> op.conserves_particle_number()
True
>>> op = FermionOperator.from_dict({((True, 0),): 1})
>>> op.conserves_particle_number()
False

Risultati

Se questo operatore conservi il numero di particelle.

conserves_sector

conserves_sector(block_sizes)

Indica se ogni termine mantiene il numero di particelle all'interno di ciascun blocco modale.

block_sizes suddivide l'intervallo delle modalità in blocchi consecutivi e non sovrapposti: il blocco b comprende le modalità [start_b, start_b + block_sizes[b]) dove start_b è la somma delle dimensioni dei blocchi precedenti. Un termine conserva il settore se e solo se, in ogni blocco, il numero dei suoi operatori di creazione è uguale al numero dei suoi operatori di annichilimento. Un termine che agisce su una modalità al di fuori del blocco finale non conserva il settore.

Un "empty" tratta block_sizes tutte le modalità come un unico blocco, rendendo questa operazione equivalente a conserves_particle_number(). Un singolo blocco [norb] verifica la conservazione per un settore FCI senza spin, mentre due blocchi uguali [norb, norb] verificano che le modalità alfa [0, norb) e le modalità beta [norb, 2 * norb) siano ciascuna conservate – ovvero la conservazione sia del numero di particelle che della componente z dello spin.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.from_dict({((True, 0), (False, 2)): 1})
>>> op.conserves_sector([4])  # one spinless block of 4 orbitals
True
>>> op.conserves_sector([2, 2])  # moves a particle from the alpha block to the beta block
False

Parametri

block_sizes – le dimensioni dei blocchi in modalità consecutiva che devono essere mantenute.

Risultati

Se ogni termine conserva il numero di particelle all'interno di ciascun blocco modale.

equiv

equiv(other, atol=1e-08)

Verifica se questo operatore è equivalente a un altro operatore.

In questo contesto, per “equivalenza” si intende un’uguaglianza approssimativa entro i limiti della tolleranza assoluta specificata. Per essere più precisi, questo metodo restituisce True, quando tutti i valori assoluti dei coefficienti nella differenza other - self sono inferiori alla soglia specificata atol.

Nota

Questo è il confronto matematico che quasi sempre si desidera. Si differenzia dall’operatore == , che verifica l’uguaglianza esatta dei termini memorizzati (i loro coefficienti, azioni, modalità e confini interni dei termini) senza tolleranza né semplificazioni. Due operatori matematicamente uguali possono quindi risultare diversi nel confronto == se sono memorizzati in modo diverso – ad esempio, un non semplificato a + a rispetto a 2 * a, oppure termini disposti in un ordine diverso. Utilizzare equiv per confrontare gli operatori entro i limiti della tolleranza numerica.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.from_dict({(): 1e-7})
>>> zero = FermionOperator.zero()
>>> op.equiv(zero)
False
>>> op.equiv(zero, 1e-6)
True
>>> op.equiv(zero, 1e-9)
False

Parametri

  • altro – l’altro operatore con cui effettuare il confronto.
  • atol – la tolleranza assoluta per il confronto. Il valore predefinito è 1e-8.
from_1body_tril_spin

classmethod from_1body_tril_spin(one_body_a, one_body_b, norb)

Costruisce un operatore a partire da integrali triangolari a un corpo relativi a specie di spin distinte.

L'operatore risultante è definito da

∑iciiαai†ai+ciiβai+n†ai+n+∑i<jcijα(ai†aj+aj†ai)+cijβ(ai+n†aj+n+aj+n†ai+n)\sum_i c^\alpha_{ii} a^\dagger_i a_i + c^\beta_{ii} a^\dagger_{i+n} a_{i+n} + \sum_{i \lt j} c^\alpha_{ij} (a^\dagger_i a_j + a^\dagger_j a_i) + c^\beta_{ij} (a^\dagger_{i+n} a_{j+n} + a^\dagger_{j+n} a_{i+n})

dove cαc^\alpha ( cβc^\beta ) sono i coefficienti integrali memorizzati in one_body_a (one_body_b, rispettivamente), ii e jj sono gli indici ricavati dall'indice triangolare ijij che indicizza gli array, e nn è il numero di orbitali, norb.

L'operatore risultante agisce su 2n2n modi fermionici privi di spin con ordinamento a blocchi di spin: i modi 0,…,n−10, \dots, n-1 sono gli orbitali di spin α\alpha, mentre i modi n,…,2n−1n, \dots, 2n-1 sono gli orbitali di spin β\beta (pertanto, l'orbitale ii della specie di spin β\beta corrisponde al modo i+ni+n ).

>>> import numpy as np
>>> from qiskit_fermions.operators import FermionOperator
>>> one_body_a = np.array([1.0, 2.0, 3.0])
>>> one_body_b = np.array([-1.0, -2.0, -3.0])
>>> op = FermionOperator.from_1body_tril_spin(one_body_a, one_body_b, norb=2)
>>> print(format(op))
  1.000000e0 +0.000000e0j * (+0 -0)
  2.000000e0 +0.000000e0j * (+0 -1)
  2.000000e0 +0.000000e0j * (+1 -0)
  3.000000e0 +0.000000e0j * (+1 -1)
 -1.000000e0 +0.000000e0j * (+2 -2)
 -2.000000e0 +0.000000e0j * (+2 -3)
 -2.000000e0 +0.000000e0j * (+3 -2)
 -3.000000e0 +0.000000e0j * (+3 -3)

Parametri

  • one_body_a – un array unidimensionale di lunghezza n∗(n+1)/2n * (n + 1) / 2 che memorizza i coefficienti dell’integrale elettronico a un corpo delle specie con spin α\alpha, sotto forma di matrice triangolare appiattita.
  • one_body_b – un array unidimensionale di lunghezza n∗(n+1)/2n * (n + 1) / 2 che memorizza i coefficienti dell’integrale elettronico a un corpo delle specie con spin β\beta, sotto forma di matrice triangolare appiattita.
  • norb – il numero di orbitali, nn.

Risultati

La componente a un corpo dell'Hamiltoniano della struttura elettronica, come definito sopra.

from_1body_tril_spin_sym

classmethod from_1body_tril_spin_sym(one_body_a, norb)

Costruisce un operatore a partire da integrali triangolari a un corpo simmetrici rispetto allo spin.

L'operatore risultante è definito da

∑iciiα(ai†ai+ai+n†ai+n)+∑i<jcijα(ai†aj+aj†ai+ai+n†aj+n+aj+n†ai+n)\sum_i c^\alpha_{ii} (a^\dagger_i a_i + a^\dagger_{i+n} a_{i+n}) + \sum_{i \lt j} c^\alpha_{ij} (a^\dagger_i a_j + a^\dagger_j a_i + a^\dagger_{i+n} a_{j+n} + a^\dagger_{j+n} a_{i+n})

dove cαc^\alpha sono i coefficienti integrali memorizzati in one_body_a, ii e jj sono gli indici ricavati dall'indice triangolare ijij che indicizza l'array, e nn è il numero di orbitali, norb.

L'operatore risultante agisce su 2n2n modi fermionici privi di spin con ordinamento a blocchi di spin: i modi 0,…,n−10, \dots, n-1 sono gli orbitali di spin α\alpha, mentre i modi n,…,2n−1n, \dots, 2n-1 sono gli orbitali di spin β\beta (pertanto, l'orbitale ii della specie di spin β\beta corrisponde al modo i+ni+n ).

>>> import numpy as np
>>> from qiskit_fermions.operators import FermionOperator
>>> one_body_a = np.array([1.0, 2.0, 3.0])
>>> op = FermionOperator.from_1body_tril_spin_sym(one_body_a, norb=2)
>>> print(format(op))
  1.000000e0 +0.000000e0j * (+0 -0)
  2.000000e0 +0.000000e0j * (+0 -1)
  2.000000e0 +0.000000e0j * (+1 -0)
  3.000000e0 +0.000000e0j * (+1 -1)
  1.000000e0 +0.000000e0j * (+2 -2)
  2.000000e0 +0.000000e0j * (+2 -3)
  2.000000e0 +0.000000e0j * (+3 -2)
  3.000000e0 +0.000000e0j * (+3 -3)

Parametri

  • one_body_a – un array unidimensionale di lunghezza n∗(n+1)/2n * (n + 1) / 2 che memorizza i coefficienti dell’integrale elettronico a un corpo delle specie con spin α\alpha, sotto forma di matrice triangolare appiattita.
  • norb – il numero di orbitali, nn.

Risultati

La componente a un corpo dell'Hamiltoniano della struttura elettronica, come definito sopra.

from_2body_tril_spin

classmethod from_2body_tril_spin(two_body_aa, two_body_ab, two_body_bb, norb)

Costruisce un operatore a partire da integrali triangolari a due corpi relativi a specie di spin distinte.

L'operatore risultante è definito da

∑ijkl12∑(i,j,k,l)∈P(ijkl)cijklααai†ak†alaj+cijklββai+n†ak+n†al+naj+n+∑ijkl12∑(i,j,k,l)∈P′(ijkl)cijklαβai+n†ak†alaj+n+cijklαβai†ak+n†al+naj+\sum_{ijkl} \frac{1}{2} \sum_{(i,j,k,l) \in \mathcal{P}(ijkl)} c^{\alpha\alpha}_{ijkl} a^\dagger_i a^\dagger_k a_l a_j + c^{\beta\beta}_{ijkl} a^\dagger_{i+n} a^\dagger_{k+n} a_{l+n} a_{j+n} + \sum_{ijkl} \frac{1}{2} \sum_{(i,j,k,l) \in \mathcal{P'}(ijkl)} c^{\alpha\beta}_{ijkl} a^\dagger_{i+n} a^\dagger_k a_l a_{j+n} + c^{\alpha\beta}_{ijkl} a^\dagger_i a^\dagger_{k+n} a_{l+n} a_j +

dove cααc^{\alpha\alpha} ( cαβc^{\alpha\beta}, cββc^{\beta\beta} ) sono i coefficienti integrali memorizzati rispettivamente in ( two_body_aa``two_body_ab , two_body_bb,), ijklijkl è l'indice corrente dell'array, P\mathcal{P} ( P′\mathcal{P'} ) genera le permutazioni univoche del 4-indice (i,j,k,l)(i,j,k,l) (vedi sotto), e nn è il numero di orbitali, norb.

I coefficienti a due corpi sono definiti secondo la notazione dei chimici, (ij∣kl)(ij|kl), ovvero le due coppie di indici (i,j)(i,j) e (k,l)(k,l) indicano ciascuna una densità di carica. Il fattore 12\frac{1}{2} è il prefattore convenzionale a due corpi.

L'operatore risultante agisce su 2n2n modi fermionici privi di spin con ordinamento a blocchi di spin: i modi 0,…,n−10, \dots, n-1 sono gli orbitali di spin α\alpha, mentre i modi n,…,2n−1n, \dots, 2n-1 sono gli orbitali di spin β\beta (pertanto, l'orbitale ii della specie di spin β\beta corrisponde al modo i+ni+n ).

Nota

two_body_aa e two_body_bb sono matrici simmetriche di tipo S8-fold. Ciò significa che si tratta dei dati triangolari inferiori appiattiti delle matrici di forma (npair, npair), dove npair = (norb * (norb + 1) // 2. Questi, a loro volta, sono i dati del triangolo inferiore delle matrici a 4 dimensioni di forma (norb, norb, norb, norb). Pertanto, l'espressione P\mathcal{P} sopra riportata espande l'indice appiattito ijklijkl in tutte le permutazioni dell'indice (i,j,k,l)(i,j,k,l) che indicizzano questi array a 4 dimensioni.

Tuttavia, two_body_ab è simmetrico solo rispetto a S4-fold. Pertanto, contiene tutti i dati della matrice (npair, npair) (ma ancora in forma appiattita). Il metodo P′\mathcal{P'} esegue la corrispondente espansione degli indici. (Nella definizione precedente, abbiamo riutilizzato l'indice ijklijkl come abuso di notazione.)

>>> import numpy as np
>>> from qiskit_fermions.operators import FermionOperator
>>> two_body_aa = np.arange(1, 7, dtype=float)
>>> two_body_ab = np.arange(11, 20, dtype=float)
>>> two_body_bb = np.arange(-1, -7, -1, dtype=float)
>>> op = FermionOperator.from_2body_tril_spin(two_body_aa, two_body_ab, two_body_bb, norb=2)
>>> len(op)
64

Parametri

  • two_body_aa – un array unidimensionale contenente i coefficienti integrali elettronici simmetrici a due corpi dell’ S8-fold, relativi alle specie con spin αα\alpha\alpha, sotto forma di array appiattito.
  • two_body_ab – un array unidimensionale contenente i coefficienti integrali elettronici simmetrici a due corpi dell' S4-fold, relativi alle specie con spin αβ\alpha\beta, sotto forma di array appiattito.
  • two_body_bb – un array unidimensionale contenente i coefficienti integrali elettronici simmetrici a due corpi dell’ S8-fold, relativi alle specie con spin ββ\beta\beta, sotto forma di array appiattito.
  • norb – il numero di orbitali, nn.

Risultati

La componente a due corpi dell’Hamiltoniano della struttura elettronica, come definito sopra.

from_2body_tril_spin_sym

classmethod from_2body_tril_spin_sym(two_body_aa, norb)

Costruisce un operatore a partire da integrali triangolari a due corpi simmetrici rispetto allo spin.

L'operatore risultante è definito da

∑ijkl12cijklαα∑(i,j,k,l)∈P(ijkl)(ai†ak†alaj+ai+n†ak†alaj+n+ai†ak+n†al+naj+ai+n†ak+n†al+naj+n)\sum_{ijkl} \frac{1}{2} c^{\alpha\alpha}_{ijkl} \sum_{(i,j,k,l) \in \mathcal{P}(ijkl)} (a^\dagger_i a^\dagger_k a_l a_j + a^\dagger_{i+n} a^\dagger_k a_l a_{j+n} + a^\dagger_i a^\dagger_{k+n} a_{l+n} a_j + a^\dagger_{i+n} a^\dagger_{k+n} a_{l+n} a_{j+n})

dove cααc^{\alpha\alpha} sono i coefficienti integrali memorizzati in two_body_aa, ijklijkl è l'indice corrente dell'array, P\mathcal{P} genera le permutazioni univoche dell'indice a 4 elementi (i,j,k,l)(i,j,k,l) (vedi sotto), e nn è il numero di orbitali, norb.

I coefficienti a due corpi sono definiti secondo la notazione dei chimici, (ij∣kl)(ij|kl), ovvero le due coppie di indici (i,j)(i,j) e (k,l)(k,l) indicano ciascuna una densità di carica. Il fattore 12\frac{1}{2} è il prefattore convenzionale a due corpi.

L'operatore risultante agisce su 2n2n modi fermionici privi di spin con ordinamento a blocchi di spin: i modi 0,…,n−10, \dots, n-1 sono gli orbitali di spin α\alpha, mentre i modi n,…,2n−1n, \dots, 2n-1 sono gli orbitali di spin β\beta (pertanto, l'orbitale ii della specie di spin β\beta corrisponde al modo i+ni+n ).

Nota

two_body_aa è una matrice simmetrica di tipo S8-fold. Ciò significa che si tratta dei dati della parte inferiore triangolare appiattita di una matrice di forma (npair, npair), dove npair = (norb * (norb + 1) // 2. Questi, a loro volta, costituiscono i dati del triangolo inferiore della matrice a 4 dimensioni di forma (norb, norb, norb, norb). Pertanto, l' P\mathcal{P} e sopra riportata espande l'indice appiattito ijklijkl in tutte le permutazioni dell'indice (i,j,k,l)(i,j,k,l) che indicizzano questo array a 4 dimensioni.

>>> import numpy as np
>>> from qiskit_fermions.operators import FermionOperator
>>> two_body_aa = np.arange(1, 7, dtype=float)
>>> op = FermionOperator.from_2body_tril_spin_sym(two_body_aa, norb=2)
>>> len(op)
64

Parametri

  • two_body_aa – un array unidimensionale contenente i coefficienti integrali elettronici simmetrici a due corpi dell’ S8-fold, relativi alle specie con spin αα\alpha\alpha, sotto forma di array appiattito.
  • norb – il numero di orbitali, nn.

Risultati

La componente a due corpi dell’Hamiltoniano della struttura elettronica, come definito sopra.

from_dict

classmethod from_dict(data)

Crea un nuovo operatore a partire da un dizionario.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.from_dict(
...     {
...         (): 1.0-1.0j,
...         ((True, 0), (False, 1)): 2.0,
...     }
... )
>>> print(format(op))
  1.000000e0 -1.000000e0j * ()
  2.000000e0 +0.000000e0j * (+0 -1)

Parametri

dati – un dizionario che associa tuple di termini a coefficienti complessi. Ogni chiave è una tupla composta da coppie (bool, int) . È possibile utilizzare e cre() ann() per semplificarne la costruzione.

Risultati

Un nuovo operatore.

from_fcidump

classmethod from_fcidump(fcidump)

Crea un FermionOperator partendo da una struttura dati FCIDump .

Supponendo di avere un file FCIDump denominato molecule.fcidump, è possibile costruire l'operatore a seconda quantizzazione nel modo seguente:

from qiskit_fermions.operators import FermionOperator
from qiskit_fermions.operators.library import FCIDump

fcidump = FCIDump.from_file("molecule.fcidump")
operator = FermionOperator.from_fcidump(fcidump)

Parametri

fcidump – la struttura dati FCIDump.

Risultati

L'operatore costruito. Quando FCIDump fornisce un'energia costante (ad esempio, quella della repulsione nucleare), questa viene inclusa come termine identitario ().

from_terms

classmethod from_terms(terms)

Crea un nuovo operatore a partire da un iteratore di termini (vedi anche iter_terms()).

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.from_dict({(): 2.0, ((True, 0),): 1.0, ((False, 1),): -1.0j})
>>> op.equiv(FermionOperator.from_terms(op.iter_terms()))
True

Parametri

termini – un iteratore di termini generato da iter_terms().

Risultati

Un nuovo operatore.

from_terms_with_groups

classmethod from_terms_with_groups(terms)

Crea un nuovo operatore a partire da un iteratore di termini con gruppi (vedi anche iter_terms_with_groups()).

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator(
...     [2.0, 1.0, -1.0],
...     [True, False, True, False],
...     [0, 1, 1, 0],
...     [0, 0, 2, 4],
... )
>>> op.groups = [0, 1, 1]
>>> reconstructed = FermionOperator.from_terms_with_groups(op.iter_terms_with_groups())
>>> op.equiv(reconstructed) and op.groups == reconstructed.groups
True

Parametri

termini – un iteratore di termini generato da iter_terms_with_groups().

Risultati

Un nuovo operatore.

get_actions

get_actions()

Restituisce un elenco in sola lettura delle azioni dell'operatore.

Nota

Questo metodo restituisce una copia dei dati interni.

Vedi anche

La spiegazione della struttura interna dei dati è disponibile qui.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.one()
>>> op += FermionOperator.from_dict({((True, 0), (False, 1)): 1.0})
>>> op.get_actions()
[True, False]

Risultati

Un elenco delle azioni dell'operatore.

get_boundaries

get_boundaries()

Restituisce un elenco in sola lettura degli indici che indicano i confini tra i termini dell'operatore.

Nota

Questo metodo restituisce una copia dei dati interni.

Vedi anche

La spiegazione della struttura interna dei dati è disponibile qui.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.one()
>>> op += FermionOperator.from_dict({((True, 0), (False, 1)): 1.0})
>>> op.get_boundaries()
[0, 0, 2]

Risultati

Un elenco dei limiti previsti dai termini e dalle condizioni dell'operatore.

get_coeffs

get_coeffs()

Restituisce un elenco in sola lettura dei coefficienti dell'operatore.

Nota

Questo metodo restituisce una copia dei dati interni.

Vedi anche

La spiegazione della struttura interna dei dati è disponibile qui.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.one()
>>> op += -1j * FermionOperator.one()
>>> op.get_coeffs()
[(1+0j), -1j]

Risultati

Un elenco dei coefficienti dell'operatore.

get_modes

get_modes()

Restituisce un elenco in sola lettura degli indici delle modalità su cui l'operatore ha agito.

Nota

Questo metodo restituisce una copia dei dati interni.

Vedi anche

La spiegazione della struttura interna dei dati è disponibile qui.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.one()
>>> op += FermionOperator.from_dict({((True, 0), (False, 1)): 1.0})
>>> op.get_modes()
[0, 1]

Risultati

Un elenco delle modalità dell'operatore.

get_support

get_support()

Restituisce l'insieme degli indici di modalità su cui agisce questo operatore.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.from_dict(
...     {
...         ((True, 0), (False, 4)): 1,
...         ((True, 1), (True, 3), (False, 4), (False, 7)): 1,
...     }
... )
>>> assert op.get_support() == {0, 1, 3, 4, 7}

Risultati

L'insieme degli indici di modalità su cui agisce questo operatore.

has_groups

has_groups()

Indica se questo operatore tiene traccia degli indici di gruppo.

Ciò equivale a (ma è più economico di) controllare op.groups is not None, poiché non copia gli indici dei gruppi dall'operatore per esaminarli.

Nota

Questo restituisce un valore anche True quando groups è una lista vuota, ovvero lo stato di un operatore raggruppato che non contiene termini.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator(
...     [1.0, 2.0, -1.0, -2.0],
...     [True, False, True, False, True, False, True, False],
...     [0, 1, 2, 3, 1, 0, 3, 2],
...     [0, 2, 4, 6, 8],
... )
>>> op.has_groups()
False
>>> op.groups = [0, 1, 0, 1]
>>> op.has_groups()
True

Risultati

Se groups è impostato su questo operatore.

ichop

ichop(atol=1e-08)

Rimuove i termini il cui coefficiente è inferiore alla soglia specificata.

Questo metodo modifica l'operatore direttamente e restituisce None.

Attenzione

Questo metodo tronca i coefficienti in modo avido! Se l'operatore su cui si agisce può contenere coefficienti distinti per termini duplicati, valuta invece simplify() di chiamare!

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.from_dict({(): 1e-4, ((True, 0),): 1e-6, ((False, 0),): 1e-10})
>>> print(format(op))
  1.000000e-4 +0.000000e0j * ()
 1.000000e-10 +0.000000e0j * (-0)
  1.000000e-6 +0.000000e0j * (+0)
>>> op.ichop()
>>> print(format(op))
  1.000000e-4 +0.000000e0j * ()
  1.000000e-6 +0.000000e0j * (+0)
>>> op.ichop(1e-5)
>>> print(format(op))
  1.000000e-4 +0.000000e0j * ()

Parametri

atol – la tolleranza assoluta per il valore di soglia. Il valore predefinito è 1e-8.

is_hermitian

is_hermitian(atol=1e-08)

Restituisce se questo operatore è ermitiano.

Nota

Questo controllo viene implementato utilizzando equiv() la differenza normal_ordered() tra e self i suoi e adjoint() zero().

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.from_dict({
...     ((True, 0), (False, 1)): 1.00001j,
...     ((True, 1), (False, 0)): -1j,
... })
>>> op.is_hermitian()
False
>>> op.is_hermitian(1e-4)
True

Parametri

atol – Il livello di precisione numerica entro il quale i coefficienti sono considerati uguali. Il valore predefinito è 1e-8.

Risultati

Se questo operatore sia ermitiano.

iter_terms

iter_terms()

Un iteratore sui termini dell'operatore.

Avvertenza

La modifica degli elementi dell'iterazione non influisce sui dati sottostanti dell'operatore.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.from_dict({(): 2.0, ((True, 0),): 1.0, ((False, 1),): -1.0j})
>>> list(sorted(op.iter_terms()))
[([], (2+0j)), ([(False, 1)], (-0-1j)), ([(True, 0)], (1+0j))]
iter_terms_with_groups

iter_terms_with_groups()

Un iteratore sui termini dell'operatore con il relativo indice di gruppo.

Avvertenza

La modifica degli elementi dell'iterazione non influisce sui dati sottostanti dell'operatore.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator(
...     [2.0, 1.0, -1.0],
...     [True, False, True, False],
...     [0, 1, 1, 0],
...     [0, 0, 2, 4],
... )
>>> op.groups = [0, 1, 1]
>>> list(sorted(op.iter_terms_with_groups()))
[([], (2+0j), 0), ([(True, 0), (False, 1)], (1+0j), 1), ([(True, 1), (False, 0)], (-1+0j), 1)]
max_rank

max_rank()

Restituisce il rango massimo dei termini presenti in questo operatore.

Nota

La lunghezza del termine più lungo può dipendere dalla forma dell’operatore, il che significa che (ad esempio) la semplificazione dell’operatore o l’ordinamento normale possono determinare un rango massimo diverso.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.from_dict({
...     ((True, 0), (False, 1), (True, 2), (False, 3)): 1,
... })
>>> op.max_rank()
4

Risultati

Il grado massimo di questo operatore.

normal_ordered

normal_ordered(sandwich=None)

Restituisce un operatore equivalente con termini ordinati normalmente.

L'ordine normale di un termine operatore è definito in modo tale che tutte le azioni di creazione compaiano prima di tutte le azioni di annichilimento. All’interno di ciascun gruppo, le modalità su cui agisce l’azione sono ordinate in ordine lessicografico. Il fatto che l'ordine sia crescente o decrescente dipende dal valore dell'argomento sandwich :

  • None (impostazione predefinita): entrambi i gruppi sono ordinati in ordine lessicografico decrescente (ad es. +1 +0 -1 -0)
  • True: gli indici più grandi compaiono verso il centro, ovvero le azioni di creazione sono in ordine lessicografico crescente mentre quelle di annichilimento sono in ordine decrescente (ad es. +0 +1 -1 -0)
  • False: gli indici più piccoli compaiono verso il centro, ovvero le azioni di creazione sono in ordine lessicografico decrescente mentre quelle di annichilimento sono in ordine crescente (ad es. +1 +0 -0 -1)
Nota

Quando si riorganizza un termine, occorre tenere conto delle relazioni di anticommutazione, aiaj†=δij−aj†aia_i a^\dagger_j = \delta_{ij} - a^\dagger_j a^i, il che implica che il numero dei termini possa variare.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.from_dict({((False, 1), (True, 1), (False, 0), (True, 0)): 1})
>>> print(format(op.normal_ordered().simplify()))
  1.000000e0 +0.000000e0j * ()
 -1.000000e0 +0.000000e0j * (+0 -0)
 -1.000000e0 +0.000000e0j * (+1 -1)
 -1.000000e0 +0.000000e0j * (+1 +0 -1 -0)
>>> print(format(op.normal_ordered(sandwich=True).simplify()))
  1.000000e0 +0.000000e0j * ()
 -1.000000e0 +0.000000e0j * (+0 -0)
  1.000000e0 +0.000000e0j * (+0 +1 -1 -0)
 -1.000000e0 +0.000000e0j * (+1 -1)
>>> print(format(op.normal_ordered(sandwich=False).simplify()))
  1.000000e0 +0.000000e0j * ()
 -1.000000e0 +0.000000e0j * (+0 -0)
 -1.000000e0 +0.000000e0j * (+1 -1)
  1.000000e0 +0.000000e0j * (+1 +0 -0 -1)

Risultati

Un operatore equivalente ma con ordine normale.

num_groups

num_groups()

Restituisce il numero di gruppi.

Se è groups None, questa funzione restituisce anche None. In caso contrario, restituirà il numero di gruppi, definito come l'indice del gruppo più grande presente più 1 (che potrà quindi essere utilizzato come indice per il gruppo successivo).

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator(
...     [1.0, 2.0, -1.0, -2.0],
...     [True, False, True, False, True, False, True, False],
...     [0, 1, 2, 3, 1, 0, 3, 2],
...     [0, 2, 4, 6, 8],
... )
>>> op.groups = [0, 1, 0, 1]
>>> op.num_groups()
2

Risultati

L'indice di gruppo più importante è in groups rialzo di 1.

one

classmethod one()

Crea l'operatore di identità moltiplicativa.

La composizione dell'operatore ottenuto con questo metodo con un altro operatore non produce alcun effetto.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.from_dict({(): 2.0})
>>> one = FermionOperator.one()
>>> op & one == op
True
relabel_modes

relabel_modes(permutation)

Restituisce un nuovo operatore con le modalità rinominate.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.from_dict({
...     ((True, 0), (False, 1)): 1,
...     ((True, 0), (False, 1), (True, 2), (False, 3)): 1,
... })
>>> permutation = [5, 6, 4, 3]
>>> relabeled = op.relabel_modes(permutation)
>>> print(format(relabeled))
  1.000000e0 +0.000000e0j * (+5 -6)
  1.000000e0 +0.000000e0j * (+5 -6 +4 -3)

Parametri

permutazione – l'elenco delle permutazioni degli indici. La modalità viene i rinominata in permutation[i], pertanto l'elenco non deve contenere voci duplicate e deve essere sufficientemente lungo da indicizzare ogni modalità su cui agisce l'operatore (la sua lunghezza deve superare l'indice di modalità più elevato dell'operatore).

Risultati

Un nuovo operatore con le modalità rinominate.

Aumenti

ValueError – se contiene permutation voci duplicate o è troppo breve per ridenominare una modalità su cui agisce l'operatore.

simplify

simplify(atol=1e-08)

Restituisce un operatore equivalente ma semplificato.

Il processo di semplificazione somma innanzitutto tutti i coefficienti appartenenti a termini uguali e poi mantiene solo quelli il cui coefficiente totale supera la tolleranza specificata (proprio come ichop()).

Quando un operatore è stato manipolato aritmeticamente o costruito in modo tale da non garantire l'unicità dei termini, è opportuno richiamare questo metodo prima di applicare qualsiasi metodo che filtri i coefficienti numericamente piccoli, al fine di evitare la perdita di informazioni. Si veda l'esempio riportato di seguito, che illustra come ichop() sia possibile troncare termini la cui somma porta a un coefficiente di grandezza totale che non dovrebbe essere troncato:

>>> from qiskit_fermions.operators import FermionOperator
>>> coeffs = [1e-5] * int(1e5)
>>> boundaries = [0] + [0] * int(1e5)
>>> op = FermionOperator(coeffs, [], [], boundaries)
>>> canon = op.simplify(1e-4)
>>> assert canon.equiv(op.one(), 1e-6)
>>> op.ichop(1e-4)
>>> assert op.equiv(op.zero(), 1e-6)

Parametri

atol – la tolleranza assoluta per il valore di soglia. Il valore predefinito è 1e-8.

Risultati

Un operatore equivalente ma semplificato.

split_out_groups

split_out_groups(group_indices=None)

Suddivide questo operatore in un elenco facoltativo di nuovi operatori in base a groups.

Se è groups None, questa funzione restituisce anche None. Altrimenti, se group_indices è None (impostazione predefinita), restituisce una lista contenente un nuovo operatore per ogni indice di gruppo in groups, in ordine di indice. Se group_indices è specificato, vengono generati solo gli indici richiesti, nell'ordine indicato: ciò evita il costo legato alla creazione di operatori per gruppi che non vengono mai utilizzati, il che è particolarmente vantaggioso quando è necessario ricorrere solo a un numero limitato di gruppi rispetto a un totale molto più ampio, ad esempio quando si effettua un sottocampionamento dei gruppi per una formula di prodotto randomizzata. Un indice duplicato in group_indices viene restituito una volta per ogni occorrenza.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator(
...     [1.0, 2.0, -1.0, -2.0],
...     [True, False, True, False, True, False, True, False],
...     [0, 1, 2, 3, 1, 0, 3, 2],
...     [0, 2, 4, 6, 8],
... )
>>> print(op.split_out_groups())
None
>>> op.groups = [0, 1, 0, 1]
>>> groups = op.split_out_groups()
>>> for g in groups:
...     print(list(sorted(g.iter_terms())))
[([(True, 0), (False, 1)], (1+0j)), ([(True, 1), (False, 0)], (-1+0j))]
[([(True, 2), (False, 3)], (2+0j)), ([(True, 3), (False, 2)], (-2+0j))]
>>> groups = op.split_out_groups(group_indices=[1])
>>> for g in groups:
...     print(list(sorted(g.iter_terms())))
[([(True, 2), (False, 3)], (2+0j)), ([(True, 3), (False, 2)], (-2+0j))]

Parametri

group_indices – gli indici di gruppo per i quali creare gli operatori, nell'ordine di output desiderato. Se non specificato, vengono creati tutti i gruppi, in ordine di indice.

Risultati

Un vettore facoltativo contenente un nuovo operatore per ciascun indice di gruppo richiesto.

zero

classmethod zero()

Costruisce l'operatore di identità additiva.

L'aggiunta di un operatore costruito con questo metodo a un altro operatore non ha alcun effetto.

>>> from qiskit_fermions.operators import FermionOperator
>>> op = FermionOperator.from_dict({(): 2.0})
>>> zero = FermionOperator.zero()
>>> op + zero == op
True

Metodi del protocollo

_anti_commutator_

static _anti_commutator_(op_a, op_b)

_commutator_

static _commutator_(op_a, op_b)

_double_commutator_

static _double_commutator_(op_a, op_b, op_c, sign)

_linear_operator_

_linear_operator_(norb, nelec)

Restituisce un oggetto SciPy per LinearOperator questo operatore nel settore FCI (norb, nelec) .

Ciò viene implementato ffsim.SupportsLinearOperator convertendo il dato in un e ffsim.FermionOperator passandolo a ffsim.linear_operator(): la simulazione è di competenza di ffsim, mentre questo pacchetto gestisce il circuito indipendente dal mappatore anziché un secondo backend di simulazione.

ffsim rifiuta un operatore che non conservi sia il numero di particelle sia la componente z dello spin, poiché un operatore di questo tipo non ha alcun effetto su un settore fisso e ignora silenziosamente un termine che agisce su un orbitale al di fuori di esso [0, norb).

Parametri

  • norb (int) – il numero di orbitali spaziali.
  • nelec (int |tuple[int, int]) – il numero di elettroni – un numero intero per un settore senza spin, oppure una coppia (n_alpha, n_beta) per un settore con spin.
  • self (FermionOperator)

Risultati

A applicando scipy.sparse.linalg.LinearOperator questo operatore al settore richiesto.

Tipo di restituzione

scipy.sparse.linalg.LinearOperator

_majorana_operator_

_majorana_operator_()

Converte questo operatore in un MajoranaOperator.

Questo implementa il protocollo SupportsMajoranaOperator delegando a fermion_to_majorana().

_trace_

_trace_(norb, nelec)

Restituisce la traccia di questo operatore sul settore FCI (norb, nelec) .

Ciò implementa tramite ffsim.SupportsTrace ffsim.trace(), rispecchiando _linear_operator_.

Parametri

  • norb (int) – il numero di orbitali spaziali.
  • nelec (int |tuple[int, int]) – il numero di elettroni – un numero intero per un settore senza spin, oppure una coppia (n_alpha, n_beta) per un settore con spin.
  • self (FermionOperator)

Risultati

La traccia di questo operatore sul settore richiesto.

Tipo di restituzione

complesso

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