Skip to main content
IBM Quantum Platform

FermionOperator

class FermionOperator(coeffs, actions, modes, boundaries)

Bases : object

Un opérateur fermionique sans spin.


Définition

Cet opérateur est défini par une combinaison linéaire de produits d'opérateurs de création et d'annihilation fermioniques agissant sur des modes fermioniques sans spin. Autrement dit, les termes individuels satisfont aux relations d'anti-commutation suivantes : [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} \, ,

où ii et jj ne font pas la distinction entre les espèces de spin des modes fermioniques qu’ils indexent.

La définition de l'opérateur dans son ensemble est donc la suivante :

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

où Aj^∈{aj,aj†}\hat{A_j} \in \{ a_j, a^\dagger_j \} et cic_i est le coefficient (complexe) constituant la combinaison linéaire des produits. L'indice jj peut prendre n'importe quelle valeur comprise entre 0 et le nombre de modes fermioniques sur lesquels agit l'opérateur, moins 1.


Implémentation

Cette classe stocke les termes et les coefficients dans plusieurs vecteurs creux, à l'instar du format « compressed sparse row » couramment utilisé pour les matrices creuses. Plus précisément, un opérateur contient 4 tableaux :

Chronique « 1 »
Chronique « 2 »
coeffsUn vecteur de coefficients complexes composé de deux nombres à virgule flottante de 64 bits.
actionsUn vecteur de valeurs booléennes contenant la nature des opérations de seconde quantification.
modesUn vecteur d'entiers de 32 bits contenant les indices des modes fermioniques sur lesquels l'opération a été effectuée.
boundariesUn vecteur d'entiers indiquant les limites dans actions et modes.

Les entrées en actions désignent des opérateurs de création (annihilation) par True (False). Les modes fermioniques indexés par modes sont considérés comme dépourvus de spin.

Remarque

Vous pouvez accéder à des copies en lecture seule de ces tableaux internes via leurs méthodes respectives : get_coeffs(), get_actions(), get_modes(), et get_boundaries().

Cette structure de données permet de créer et de manipuler des opérateurs de manière très efficace. Cela implique toutefois qu'un opérateur puisse contenir à tout moment des termes en double. Ces problèmes doivent être résolus manuellement à l'aide de simplify().

Construction

Il est possible de créer un opérateur directement en fournissant les tableaux décrits ci-dessus :

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

Pour plus de commodité, il est possible de créer un opérateur à partir d'un dictionnaire de type « Python » de la manière suivante :

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

Dans cet exemple, nous avons utilisé cre() et ann() pour créer les opérateurs de création et d'annihilation aux modes spécifiés.

Par ailleurs, les méthodes de construction et d'aide rapide suivantes sont disponibles :

Chronique « 1 »
Chronique « 2 »
zero()Construit l'opérateur d'identité additive.
one()Construit l'opérateur d'identité multiplicative.
from_terms(conditions)Crée un nouvel opérateur à partir d'un itérateur de termes (voir aussi iter_terms()).
from_terms_with_groups(conditions)Crée un nouvel opérateur à partir d'un itérateur de termes comportant des groupes (voir aussi iter_terms_with_groups()).

Formatage

Dans les exemples ci-dessus, les opérateurs créés ont été affichés à l'aide de la sortie de format(), ce qui permet d'obtenir une représentation lisible par l'utilisateur de l'opérateur.

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

L'ordre d'affichage de format(op) est explicitement trié avant l'affichage. Par conséquent, cela ne reflète pas l'ordre des termes à l'intérieur de l'opérateur.

La fonction repr() permet d'obtenir une forme alternative, qui donne lieu à une représentation interprétable par Python. En d'autres termes, cette sortie peut facilement être copiée-collée dans un shell Python :

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

Enfin, pour les grands opérateurs, ces deux sorties pourraient s'avérer très longues et indésirables. On peut alors obtenir, grâce à cette fonction str() , un formulaire très simple contenant un minimum d'informations :

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

Itération

La structure de données sous-jacente étant implémentée en Rust et présentant une organisation complexe, il n'est pas possible d'itérer directement dessus :

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

Cette classe fournit plutôt des itérateurs personnalisés pour répondre à ce besoin :

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

iter_terms()

Pour plus de détails sur la mise en œuvre.

Le tableau ci-dessous répertorie tous les itérateurs disponibles :

Chronique « 1 »
Chronique « 2 »
iter_terms()Un itérateur sur les termes de l'opérateur.
iter_terms_with_groups()Un itérateur parcourant les termes de l'opérateur avec leur indice de groupe associé.

Arithmétique

Les opérations arithmétiques suivantes sont prises en charge :

Addition/Soustraction

>>> 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({})

Multiplication et division scalaires

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

Composition de l'opérateur

Remarque

La composition des opérateurs correspond à la multiplication à gauche : c = a & b correspond à C=BAC = B A. En d'autres termes, la composition de deux opérateurs renvoie un opérateur résultant qui effectue « d'abord a puis ». 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)
Remarque

Pour plus de simplicité, la multiplication par la droite est implémentée par c = a @ b (ce qui donne C=ABC = A B ).

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

Autres opérations

Outre les méthodes « magiques » correspondant aux opérations arithmétiques décrites ci-dessus, les méthodes suivantes sont disponibles :

Chronique « 1 »
Chronique « 2 »
adjoint()Renvoie le conjugué hermitien (ou adjoint) de cet opérateur.
ichop( [atol] )Supprime les termes dont la valeur absolue du coefficient est inférieure au seuil indiqué.
simplify( [atol] )Renvoie un opérateur équivalent mais simplifié.
normal_ordered( [sandwich] )Renvoie un opérateur équivalent dont les termes sont classés selon l'ordre habituel.
relabel_modes(permutation)Renvoie un nouvel opérateur dont les modes ont été renommés.

Propriétés

Enfin, il existe différentes méthodes permettant de vérifier certaines propriétés d'un opérateur :

Chronique « 1 »
Chronique « 2 »
is_hermitian( [atol] )Indique si cet opérateur est hermitien.
max_rank()Renvoie le rang maximal des termes de cet opérateur.
conserves_particle_number()Indique si cet opérateur préserve le nombre de particules.

[ 1 ]

https://en.wikipedia.org/wiki/Second _quantification#Opérateurs_de_création_et_d'annihilation_des_fermions


Attributs

groups

Un vecteur facultatif d'indices de groupe pour chaque terme.

Pour plus d'informations, consultez le module grouping .


Méthodes

adjoint

adjoint()

Renvoie le conjugué hermitien (ou adjoint) de cet opérateur.

Cela a les conséquences suivantes sur les termes et les coefficients :

  • les actions de chaque terme inversent leur ordre et alternent entre création et annihilation
  • les coefficients sont des nombres complexes conjugués
>>> 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()

Indique si cet opérateur préserve le nombre de particules.

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

Retours

Savoir si cet opérateur préserve le nombre de particules.

conserves_sector

conserves_sector(block_sizes)

Indique si chaque terme préserve le nombre de particules au sein de chaque bloc de mode.

block_sizes divise la plage des modes en blocs consécutifs qui ne se chevauchent pas : le bloc b couvre les modes [start_b, start_b + block_sizes[b]) où start_b est la somme des tailles des blocs précédents. Un terme conserve le secteur si et seulement si, dans chaque bloc, le nombre de ses opérateurs de création est égal à celui de ses opérateurs d'annihilation. Un terme agissant sur un mode situé au-delà du bloc final ne préserve pas le secteur.

Un espace vide block_sizes traite tous les modes comme un seul bloc, ce qui revient à conserves_particle_number(). Un seul bloc [norb] vérifie la conservation pour un secteur FCI sans spin, tandis que deux blocs identiques [norb, norb] vérifient que les modes alpha et [0, norb) les modes bêta [norb, 2 * norb) sont chacun conservés – c'est-à-dire la conservation à la fois du nombre de particules et de la composante z du 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

Paramètres

block_sizes – les tailles des blocs en mode consécutif qui doivent toutes être conservées.

Retours

Vérifier si chaque terme préserve le nombre de particules au sein de chaque bloc de mode.

equiv

equiv(other, atol=1e-08)

Vérifie si cet opérateur est équivalent à un autre opérateur.

Dans ce contexte, l'équivalence désigne une égalité approximative dans les limites de la tolérance absolue spécifiée. Plus précisément, cette méthode renvoie True, lorsque toutes les valeurs absolues des coefficients de la différence other - self sont inférieures au seuil spécifié atol.

Remarque

C'est la comparaison mathématique que l'on souhaite presque toujours. Il diffère de l'opérateur == , qui vérifie l'égalité exacte des termes stockés (leurs coefficients, leurs actions, leurs modes et les limites internes des termes) sans tolérance ni simplification. Deux opérateurs mathématiquement égaux peuvent donc donner un résultat différent lors d'une comparaison sous == s'ils sont stockés différemment – par exemple, un non simplifié par a + a rapport à un 2 * a, ou des termes classés dans un ordre différent. Utilisez equiv pour comparer des opérateurs dans les limites de la tolérance numérique.

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

Paramètres

  • autre – l'autre opérateur à comparer.
  • atol – la tolérance absolue pour la comparaison. Cette valeur est définie par défaut sur 1e-8.
from_1body_tril_spin

classmethod from_1body_tril_spin(one_body_a, one_body_b, norb)

Construit un opérateur à partir d'intégrales triangulaires à un corps distinctes pour chaque espèce de spin.

L'opérateur ainsi obtenu est défini par

∑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})

où cαc^\alpha ( cβc^\beta ) sont les coefficients d'intégrale stockés respectivement dans ( one_body_a``one_body_b , resp.), ii et jj sont les indices dérivés de l'indice triangulaire ijij qui indexe les tableaux, et nn est le nombre d'orbitales, norb.

L'opérateur ainsi obtenu agit sur 2n2n modes fermioniques sans spin présentant un ordre de spin par blocs : les modes 0,…,n−10, \dots, n-1 correspondent aux orbitales de spin α\alpha, tandis que les modes n,…,2n−1n, \dots, 2n-1 correspondent aux orbitales de spin β\beta (ainsi, l'orbitale ii de l'espèce de spin β\beta correspond au mode 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)

Paramètres

  • one_body_a – un tableau unidimensionnel de longueur n∗(n+1)/2n * (n + 1) / 2 contenant les coefficients d'intégrale électronique à un corps des espèces de spin α\alpha, sous la forme d'une matrice triangulaire aplatie.
  • one_body_b – tableau unidimensionnel de longueur n∗(n+1)/2n * (n + 1) / 2 contenant les coefficients d’intégrale électronique à un corps des espèces de spin « β\beta », sous la forme d’une matrice triangulaire aplatie.
  • norb – le nombre d'orbitales, nn.

Retours

La composante à un corps de l'hamiltonien de la structure électronique, telle que définie ci-dessus.

from_1body_tril_spin_sym

classmethod from_1body_tril_spin_sym(one_body_a, norb)

Construit un opérateur à partir d'intégrales triangulaires à un corps symétriques par rapport au spin.

L'opérateur ainsi obtenu est défini par

∑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})

où cαc^\alpha sont les coefficients d'intégrale stockés dans one_body_a, ii et jj sont les indices dérivés de l'indice triangulaire ijij qui indexe le tableau, et nn est le nombre d'orbitales, norb.

L'opérateur ainsi obtenu agit sur 2n2n modes fermioniques sans spin présentant un ordre de spin par blocs : les modes 0,…,n−10, \dots, n-1 correspondent aux orbitales de spin α\alpha, tandis que les modes n,…,2n−1n, \dots, 2n-1 correspondent aux orbitales de spin β\beta (ainsi, l'orbitale ii de l'espèce de spin β\beta correspond au mode 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)

Paramètres

  • one_body_a – un tableau unidimensionnel de longueur n∗(n+1)/2n * (n + 1) / 2 contenant les coefficients d'intégrale électronique à un corps des espèces de spin α\alpha, sous la forme d'une matrice triangulaire aplatie.
  • norb – le nombre d'orbitales, nn.

Retours

La composante à un corps de l'hamiltonien de la structure électronique, telle que définie ci-dessus.

from_2body_tril_spin

classmethod from_2body_tril_spin(two_body_aa, two_body_ab, two_body_bb, norb)

Construit un opérateur à partir d'intégrales triangulaires à deux corps distinctes pour chaque espèce de spin.

L'opérateur ainsi obtenu est défini par

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

où cααc^{\alpha\alpha} ( cαβc^{\alpha\beta}, cββc^{\beta\beta} ) sont les coefficients d’intégrale stockés respectivement dans ( two_body_aa``two_body_ab , two_body_bb,), ijklijkl est l’indice courant du tableau, P\mathcal{P} ( P′\mathcal{P'} ) génère les permutations uniques de l’indice à 4 éléments (i,j,k,l)(i,j,k,l) (voir ci-dessous), et nn est le nombre d’orbitales, norb.

Les coefficients à deux corps sont attendus dans l'ordre des chimistes, (ij∣kl)(ij|kl), c'est-à-dire que les deux paires d'indices (i,j)(i,j) et (k,l)(k,l) désignent chacune une densité de charge. Le facteur 12\frac{1}{2} correspond au préfacteur classique à deux corps.

L'opérateur ainsi obtenu agit sur 2n2n modes fermioniques sans spin présentant un ordre de spin par blocs : les modes 0,…,n−10, \dots, n-1 correspondent aux orbitales de spin α\alpha, tandis que les modes n,…,2n−1n, \dots, 2n-1 correspondent aux orbitales de spin β\beta (ainsi, l'orbitale ii de l'espèce de spin β\beta correspond au mode i+ni+n ).

Remarque

two_body_aa et two_body_bb sont des matrices symétriques de S8-fold. Cela signifie qu'il s'agit des données triangulaires inférieures aplaties de matrices de forme (npair, npair), où npair = (norb * (norb + 1) // 2. Ce sont là les données du triangle inférieur des tableaux à 4 dimensions de forme (norb, norb, norb, norb). Par conséquent, l'expression P\mathcal{P} ci-dessus étend l'index aplati ijklijkl à toutes les permutations d'index (i,j,k,l)(i,j,k,l) qui indexent ces tableaux à 4 dimensions.

Cependant, elle n'est two_body_ab symétrique que par rapport à S4-fold. Elle contient donc l'intégralité des données de la matrice (npair, npair) (mais toujours sous forme aplatie). La fonction P′\mathcal{P'} effectue le développement d'indice correspondant. (Dans la définition ci-dessus, nous avons réutilisé l'indice ijklijkl, ce qui constitue un abus de notation.)

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

Paramètres

  • two_body_aa – un tableau unidimensionnel contenant les coefficients d’intégrale électronique symétrique à deux corps de l’ S8-fold, pour les espèces à spin αα\alpha\alpha, sous la forme d’un tableau aplati.
  • two_body_ab – un tableau unidimensionnel contenant les coefficients d'intégrale électronique symétriques à deux corps de l' S4-fold, pour les espèces à spin de type αβ\alpha\beta, sous la forme d'un tableau aplati.
  • two_body_bb – un tableau unidimensionnel contenant les coefficients d'intégrale électronique symétrique à deux corps de l' S8-fold, pour les espèces à spin ββ\beta\beta, sous la forme d'un tableau aplati.
  • norb – le nombre d'orbitales, nn.

Retours

La composante à deux corps de l'hamiltonien de la structure électronique, telle que définie ci-dessus.

from_2body_tril_spin_sym

classmethod from_2body_tril_spin_sym(two_body_aa, norb)

Construit un opérateur à partir d'intégrales triangulaires à deux corps à symétrie de spin.

L'opérateur ainsi obtenu est défini par

∑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})

où cααc^{\alpha\alpha} sont les coefficients d'intégrale stockés dans two_body_aa, ijklijkl est l'indice courant du tableau, P\mathcal{P} génère les permutations uniques de l'indice à 4 éléments (i,j,k,l)(i,j,k,l) (voir ci-dessous), et nn est le nombre d'orbitales, norb.

Les coefficients à deux corps sont attendus dans l'ordre des chimistes, (ij∣kl)(ij|kl), c'est-à-dire que les deux paires d'indices (i,j)(i,j) et (k,l)(k,l) désignent chacune une densité de charge. Le facteur 12\frac{1}{2} correspond au préfacteur classique à deux corps.

L'opérateur ainsi obtenu agit sur 2n2n modes fermioniques sans spin présentant un ordre de spin par blocs : les modes 0,…,n−10, \dots, n-1 correspondent aux orbitales de spin α\alpha, tandis que les modes n,…,2n−1n, \dots, 2n-1 correspondent aux orbitales de spin β\beta (ainsi, l'orbitale ii de l'espèce de spin β\beta correspond au mode i+ni+n ).

Remarque

two_body_aa est un tableau symétrique de dimension S8-fold. Cela signifie qu'il s'agit des données triangulaires inférieures aplaties d'une matrice de forme (npair, npair), où npair = (norb * (norb + 1) // 2. Il s'agit là des données de la partie triangulaire inférieure du tableau à 4 dimensions de forme (norb, norb, norb, norb). Par conséquent, l'expression P\mathcal{P} ci-dessus étend l'index aplati ijklijkl à toutes les permutations d'index (i,j,k,l)(i,j,k,l) qui indexent ce tableau à 4 dimensions.

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

Paramètres

  • two_body_aa – un tableau unidimensionnel contenant les coefficients d’intégrale électronique symétrique à deux corps de l’ S8-fold, pour les espèces à spin αα\alpha\alpha, sous la forme d’un tableau aplati.
  • norb – le nombre d'orbitales, nn.

Retours

La composante à deux corps de l'hamiltonien de la structure électronique, telle que définie ci-dessus.

from_dict

classmethod from_dict(data)

Crée un nouvel opérateur à partir d'un dictionnaire.

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

Paramètres

données – un dictionnaire associant des tuples de termes à des coefficients complexes. Chaque clé est un tuple composé de paires (bool, int) . Vous pouvez utiliser et cre() ann() pour simplifier leur construction.

Retours

Un nouvel opérateur.

from_fcidump

classmethod from_fcidump(fcidump)

Construit un FermionOperator à partir d'une structure de données FCIDump .

En supposant que vous disposiez d'un fichier FCIDump nommé molecule.fcidump, vous pouvez construire l'opérateur de seconde quantification de la manière suivante :

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

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

Paramètres

fcidump – la structure de données FCIDump.

Retours

L'opérateur construit. Lorsque le FCIDump fournit une énergie constante (par exemple, la répulsion nucléaire), celle-ci est intégrée sous forme de terme d'identité ().

from_terms

classmethod from_terms(terms)

Crée un nouvel opérateur à partir d'un itérateur de termes (voir aussi 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

Paramètres

termes – un itérateur de termes tels que générés par iter_terms().

Retours

Un nouvel opérateur.

from_terms_with_groups

classmethod from_terms_with_groups(terms)

Crée un nouvel opérateur à partir d'un itérateur de termes comportant des groupes (voir aussi 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

Paramètres

termes – un itérateur de termes tels que générés par iter_terms_with_groups().

Retours

Un nouvel opérateur.

get_actions

get_actions()

Renvoie une liste en lecture seule des actions de l'opérateur.

Remarque

Cette méthode renvoie une copie des données internes.

Voir aussi

Vous trouverez ici une explication de la structure interne des données.

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

Retours

Une liste des actions de l'opérateur.

get_boundaries

get_boundaries()

Renvoie une liste en lecture seule contenant les indices indiquant les limites entre les termes de l'opérateur.

Remarque

Cette méthode renvoie une copie des données internes.

Voir aussi

Vous trouverez ici une explication de la structure interne des données.

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

Retours

Liste des limites des conditions générales de l'opérateur.

get_coeffs

get_coeffs()

Renvoie une liste en lecture seule contenant les coefficients de l'opérateur.

Remarque

Cette méthode renvoie une copie des données internes.

Voir aussi

Vous trouverez ici une explication de la structure interne des données.

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

Retours

Liste des coefficients de l'opérateur.

get_modes

get_modes()

Renvoie une liste en lecture seule contenant les indices des modes sur lesquels l'opérateur a agi.

Remarque

Cette méthode renvoie une copie des données internes.

Voir aussi

Vous trouverez ici une explication de la structure interne des données.

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

Retours

Liste des modes de l'opérateur.

get_support

get_support()

Renvoie l'ensemble des indices de mode sur lesquels cet opérateur agit.

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

Retours

L'ensemble des indices de mode sur lesquels agit cet opérateur.

has_groups

has_groups()

Indique si cet opérateur prend en charge les indices de groupe.

Cela revient à vérifier (mais coûte moins cher que) op.groups is not None, car cela ne nécessite pas de copier les indices de groupe hors de l'opérateur pour les inspecter.

Remarque

Cette fonction renvoie une valeur même True lorsque groups est une liste vide, ce qui correspond à l'état d'un opérateur groupé ne contenant aucun terme.

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

Retours

Vérifier si cet opérateur groups est activé.

ichop

ichop(atol=1e-08)

Supprime les termes dont la valeur absolue du coefficient est inférieure au seuil indiqué.

Cette méthode modifie l'opérateur in situ et renvoie None.

Attention

Cette méthode tronque les coefficients de manière « gloutonne »! Si l'opérateur sur lequel porte l'action peut contenir des coefficients distincts pour des termes identiques, pensez plutôt simplify() à appeler!

>>> 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 * ()

Paramètres

atol – la tolérance absolue pour le seuil de coupure. Cette valeur est définie par défaut sur 1e-8.

is_hermitian

is_hermitian(atol=1e-08)

Indique si cet opérateur est hermitien.

Remarque

Cette vérification est mise en œuvre equiv() en utilisant la différence normal_ordered() entre self et ses adjoint() et 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

Paramètres

atol – Précision numérique jusqu'à laquelle les coefficients sont considérés comme égaux. Cette valeur est définie par défaut sur 1e-8.

Retours

Si cet opérateur est hermitien.

iter_terms

iter_terms()

Un itérateur parcourant les termes de l'opérateur.

Avertissement

La modification des éléments de l'itération n'a aucune incidence sur les données sous-jacentes de l'opérateur.

>>> 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 itérateur parcourant les termes de l'opérateur avec leur indice de groupe associé.

Avertissement

La modification des éléments de l'itération n'a aucune incidence sur les données sous-jacentes de l'opérateur.

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

Renvoie le rang maximal des termes de cet opérateur.

Remarque

La longueur du terme le plus long peut dépendre de la forme de l’opérateur, ce qui signifie que (par exemple) la simplification de l’opérateur ou le rang normal peuvent aboutir à un rang maximal différent.

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

Retours

Le rang maximal de cet opérateur.

normal_ordered

normal_ordered(sandwich=None)

Renvoie un opérateur équivalent dont les termes sont classés selon l'ordre habituel.

L'ordre normal d'un terme d'opérateur est défini de telle sorte que toutes les actions de création apparaissent avant toutes les actions d'annihilation. Au sein de chaque groupe, les modes sur lesquels s'opère l'action sont classés par ordre lexicographique. Le fait que leur ordre soit croissant ou décroissant dépend de la valeur de l'argument sandwich :

  • None (par défaut) : les deux groupes sont classés par ordre lexicographique décroissant (par exemple +1 +0 -1 -0)
  • True: les indices les plus grands apparaissent vers le milieu, c'est-à-dire que les actions de création sont classées par ordre lexicographique croissant, tandis que celles d'annihilation sont classées par ordre décroissant (par exemple +0 +1 -1 -0)
  • False: les indices les plus petits apparaissent vers le milieu, c'est-à-dire que les actions de création sont classées par ordre lexicographique décroissant, tandis que celles d'annihilation sont classées par ordre croissant (par exemple +1 +0 -0 -1)
Remarque

Lorsqu'on réorganise un terme, il faut tenir compte des relations d'anti-commutation, aiaj†=δij−aj†aia_i a^\dagger_j = \delta_{ij} - a^\dagger_j a^i, ce qui implique que le nombre de termes peut varier.

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

Retours

Un opérateur équivalent mais à ordre normal.

num_groups

num_groups()

Renvoie le nombre de groupes.

Si vaut groups None, cette fonction renvoie également None. Sinon, elle renvoie le nombre de groupes, défini comme étant l'indice du plus grand groupe rencontré plus 1 (ce qui peut donc servir d'indice pour le groupe suivant).

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

Retours

L'indice du plus grand groupe affiche une groups hausse de 1.

one

classmethod one()

Construit l'opérateur d'identité multiplicative.

La composition de l'opérateur ainsi construit avec un autre opérateur n'a aucun effet.

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

relabel_modes(permutation)

Renvoie un nouvel opérateur dont les modes ont été renommés.

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

Paramètres

permutation – la liste des permutations d'index. Le mode est i renommé en permutation[i], de sorte que la liste ne doit contenir aucune entrée en double et doit être suffisamment longue pour répertorier tous les modes sur lesquels l'opérateur agit (sa longueur doit être supérieure à l'indice de mode le plus élevé de l'opérateur).

Retours

Un nouvel opérateur dont les modes ont été renommés.

Augmentations

ValueError – si contient permutation des entrées en double, ou si elle est trop courte pour redéfinir un mode sur lequel l'opérateur agit.

simplify

simplify(atol=1e-08)

Renvoie un opérateur équivalent mais simplifié.

Le processus de simplification commence par additionner tous les coefficients appartenant à des termes identiques, puis ne conserve que ceux dont le coefficient total dépasse la tolérance spécifiée (tout comme ichop()).

Lorsqu'un opérateur a fait l'objet d'une manipulation arithmétique ou a été construit d'une manière qui ne garantit pas l'unicité des termes, il convient d'appeler cette méthode avant d'appliquer toute méthode filtrant les coefficients numériquement faibles, afin d'éviter toute perte d'information. Consultez l'exemple ci-dessous, qui illustre comment peut ichop() tronquer des termes dont la somme donne une valeur totale du coefficient qui ne devrait pas être tronquée :

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

Paramètres

atol – la tolérance absolue pour le seuil de coupure. Cette valeur est définie par défaut sur 1e-8.

Retours

Un opérateur équivalent mais simplifié.

split_out_groups

split_out_groups(group_indices=None)

Divise cet opérateur en une liste facultative de nouveaux opérateurs en fonction de groups.

Si vaut groups None, cette fonction renvoie également None. Sinon, si group_indices est None (valeur par défaut), elle renvoie une liste contenant un nouvel opérateur pour chaque indice de groupe dans groups, classés par ordre d'indice. Si group_indices est fourni, seuls les indices demandés sont construits, dans l'ordre indiqué : cela permet d'éviter le coût lié à la construction d'opérateurs pour des groupes qui ne seront jamais utilisés, ce qui est particulièrement avantageux lorsqu'on n'a besoin que d'un petit nombre de groupes parmi un ensemble beaucoup plus vaste, par exemple lors du sous-échantillonnage de groupes pour une formule de produit aléatoire. Un index en double dans group_indices est renvoyé une fois par occurrence.

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

Paramètres

group_indices – les indices de groupe pour lesquels créer des opérateurs, dans l'ordre de sortie souhaité. En l'absence de spécification, tous les groupes sont créés, dans l'ordre de leur index.

Retours

Un vecteur facultatif contenant un nouvel opérateur pour chaque indice de groupe demandé.

zero

classmethod zero()

Construit l'opérateur d'identité additive.

L'ajout de l'opérateur ainsi créé à un autre opérateur n'a aucun effet.

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

Méthodes protocolaires

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

Renvoie une instance de SciPy pour LinearOperator cet opérateur sur le secteur FCI (norb, nelec) .

Cela s'effectue ffsim.SupportsLinearOperator en convertissant en un et ffsim.FermionOperator en le transmettant à ffsim.linear_operator(): la simulation relève de la responsabilité de ffsim, et ce paquet gère le circuit indépendant du mappeur plutôt qu'un deuxième moteur de simulation.

ffsim rejette tout opérateur qui ne conserve pas à la fois le nombre de particules et la composante z du spin, car un tel opérateur n'a aucune action sur un secteur fixe, et ignore tacitement tout terme agissant sur une orbitale située en dehors de celui-ci [0, norb).

Paramètres

  • norb (int) – le nombre d'orbitales spatiales.
  • nelec (int |tuple[int, int]) – le nombre d'électrons – un nombre entier pour un secteur sans spin, ou une paire (n_alpha, n_beta) (p) pour un secteur avec spin.
  • self (FermionOperator)

Retours

A en appliquant scipy.sparse.linalg.LinearOperator cet opérateur au secteur demandé.

Type de retour

scipy.sparse.linalg.LinearOperator

_majorana_operator_

_majorana_operator_()

Convertit cet opérateur en un MajoranaOperator.

Cela implémente le protocole SupportsMajoranaOperator en déléguant à fermion_to_majorana().

_trace_

_trace_(norb, nelec)

Renvoie la trace de cet opérateur sur le secteur FCI (norb, nelec) .

Cela permet de mettre en œuvre via ffsim.SupportsTrace ffsim.trace(), en reproduisant _linear_operator_.

Paramètres

  • norb (int) – le nombre d'orbitales spatiales.
  • nelec (int |tuple[int, int]) – le nombre d'électrons – un nombre entier pour un secteur sans spin, ou une paire (n_alpha, n_beta) (p) pour un secteur avec spin.
  • self (FermionOperator)

Retours

La trace de cet opérateur sur le secteur demandé.

Type de retour

complexe

Cette page a-t-elle été utile ?
Signaler un bogue, une coquille ou proposer du contenu sur GitHub.