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]
où et 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 :
où et est le coefficient (complexe) constituant la combinaison linéaire des produits. L'indice 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 » |
|---|---|
coeffs | Un vecteur de coefficients complexes composé de deux nombres à virgule flottante de 64 bits. |
actions | Un vecteur de valeurs booléennes contenant la nature des opérations de seconde quantification. |
modes | Un vecteur d'entiers de 32 bits contenant les indices des modes fermioniques sur lesquels l'opération a été effectuée. |
boundaries | Un 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.
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)
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))]
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
La composition des opérateurs correspond à la multiplication à gauche : c = a & b correspond à . 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)
Pour plus de simplicité, la multiplication par la droite est implémentée par c = a @ b (ce qui donne ).
>>> (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 ]
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.
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
où ( ) sont les coefficients d'intégrale stockés respectivement dans ( one_body_a``one_body_b , resp.), et sont les indices dérivés de l'indice triangulaire qui indexe les tableaux, et est le nombre d'orbitales, norb.
L'opérateur ainsi obtenu agit sur modes fermioniques sans spin présentant un ordre de spin par blocs : les modes correspondent aux orbitales de spin , tandis que les modes correspondent aux orbitales de spin (ainsi, l'orbitale de l'espèce de spin correspond au mode ).
>>> 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 contenant les coefficients d'intégrale électronique à un corps des espèces de spin , sous la forme d'une matrice triangulaire aplatie.
- one_body_b – tableau unidimensionnel de longueur contenant les coefficients d’intégrale électronique à un corps des espèces de spin « », sous la forme d’une matrice triangulaire aplatie.
- norb – le nombre d'orbitales, .
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
où sont les coefficients d'intégrale stockés dans one_body_a, et sont les indices dérivés de l'indice triangulaire qui indexe le tableau, et est le nombre d'orbitales, norb.
L'opérateur ainsi obtenu agit sur modes fermioniques sans spin présentant un ordre de spin par blocs : les modes correspondent aux orbitales de spin , tandis que les modes correspondent aux orbitales de spin (ainsi, l'orbitale de l'espèce de spin correspond au mode ).
>>> 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 contenant les coefficients d'intégrale électronique à un corps des espèces de spin , sous la forme d'une matrice triangulaire aplatie.
- norb – le nombre d'orbitales, .
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
où ( , ) sont les coefficients d’intégrale stockés respectivement dans ( two_body_aa``two_body_ab , two_body_bb,), est l’indice courant du tableau, ( ) génère les permutations uniques de l’indice à 4 éléments (voir ci-dessous), et est le nombre d’orbitales, norb.
Les coefficients à deux corps sont attendus dans l'ordre des chimistes, , c'est-à-dire que les deux paires d'indices et désignent chacune une densité de charge. Le facteur correspond au préfacteur classique à deux corps.
L'opérateur ainsi obtenu agit sur modes fermioniques sans spin présentant un ordre de spin par blocs : les modes correspondent aux orbitales de spin , tandis que les modes correspondent aux orbitales de spin (ainsi, l'orbitale de l'espèce de spin correspond au mode ).
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 ci-dessus étend l'index aplati à toutes les permutations d'index 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 effectue le développement d'indice correspondant. (Dans la définition ci-dessus, nous avons réutilisé l'indice , 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 , 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 , 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 , sous la forme d'un tableau aplati.
- norb – le nombre d'orbitales, .
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
où sont les coefficients d'intégrale stockés dans two_body_aa, est l'indice courant du tableau, génère les permutations uniques de l'indice à 4 éléments (voir ci-dessous), et est le nombre d'orbitales, norb.
Les coefficients à deux corps sont attendus dans l'ordre des chimistes, , c'est-à-dire que les deux paires d'indices et désignent chacune une densité de charge. Le facteur correspond au préfacteur classique à deux corps.
L'opérateur ainsi obtenu agit sur modes fermioniques sans spin présentant un ordre de spin par blocs : les modes correspondent aux orbitales de spin , tandis que les modes correspondent aux orbitales de spin (ainsi, l'orbitale de l'espèce de spin correspond au mode ).
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 ci-dessus étend l'index aplati à toutes les permutations d'index 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 , sous la forme d’un tableau aplati.
- norb – le nombre d'orbitales, .
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.
Cette méthode renvoie une copie des données internes.
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.
Cette méthode renvoie une copie des données internes.
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.
Cette méthode renvoie une copie des données internes.
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.
Cette méthode renvoie une copie des données internes.
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.
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.
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.
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.
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é.
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.
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)
Lorsqu'on réorganise un terme, il faut tenir compte des relations d'anti-commutation, , 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
_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