Skip to main content
IBM Quantum Platform

SparseObservable

class qiskit.quantum_info.SparseObservable(data, /, num_qubits=None)

GitHub

Bases : object

Une observable sur les bases de Pauli qui stocke ses données dans un format de qubits épars.


Mathematics

Cette observable représente une somme sur les chaînes des opérateurs de Pauli et des projecteurs d'états propres de Pauli, chaque terme étant pondéré par un nombre complexe. En d'autres termes, l'observable complet est

SparseObservable=icinAi(n)\text{\texttt{SparseObservable}} = \sum_i c_i \bigotimes_n A^{(n)}_i

pour les nombres complexes cic_i et les opérateurs à qubit unique agissant sur le qubit nn à partir d'un alphabet restreint Ai(n)A^{(n)}_i. La somme sur ii est la somme des termes individuels, et le produit tensoriel produit les chaînes d'opérateurs.

Les opérateurs de Pauli et les opérateurs de projection de l'état propre de Pauli constituent l'alphabet des opérateurs de qubits uniques autorisés dont les Ai(n)A^{(n)}_i sont tirés. Il s'agit explicitement de

Libellé
Opérateur
Valeur numérique
BitTerm attribut
"I"II (identité)Non stocké.Non stocké.
"X"XX (Pauli X)0b0010 (2)X
"Y"YY (Pauli Y)0b0011 (3)Y
"Z"ZZ (Pauli Z)0b0001 (1)Z
"+"++\lvert+\rangle\langle+\rvert (projecteur sur un état propre positif de X)0b1010 (10)PLUS
"-"\lvert-\rangle\langle-\rvert (projecteur sur l'état propre négatif de X)0b0110 (6)MINUS
"r"rr\lvert r\rangle\langle r\rvert (projecteur sur un état propre positif de Y)0b1011 (11)RIGHT
"l"ll\lvert l\rangle\langle l\rvert (projecteur sur l'état propre négatif de Y)0b0111 (7)LEFT
"0"00\lvert0\rangle\langle0\rvert (projecteur sur un état propre positif de Z)0b1001 (9)ZERO
"1"11\lvert1\rangle\langle1\rvert (projecteur sur l'état propre négatif de Z)0b0101 (5)ONE

L'alphabet autorisé forme une base surcomplète de l'espace des opérateurs. Cela signifie qu'il n'existe pas de somme unique pour représenter un observable donné. En comparaison, SparsePauliOp utilise une base précise de l'espace des opérateurs, de sorte que (après avoir combiné les termes de la même chaîne de Pauli, supprimé les zéros et trié les termes dans un ordre canonique ) il n'y a qu'une seule représentation de tout opérateur.

SparseObservable utilise sa base surcomplète particulière dans le but de rendre "l'efficacité de la mesure" équivalente à "l'efficacité de la représentation". Par exemple, l'observable 00n{\lvert0\rangle\langle0\rvert}^{\otimes n} peut être mesuré efficacement sur le matériel avec de simples mesures ZZ, mais ne peut être représenté par SparsePauliOp comme (I+Z)n/2n{(I + Z)}^{\otimes n}/2^n, ce qui nécessite des termes stockés 2n2^n. SparseObservable ne nécessite qu'un seul terme pour le stocker.

L'inconvénient est qu'il n'est pas pratique de prendre une matrice arbitraire ou SparsePauliOp et de trouver la meilleure représentation de SparseObservable représentation. En règle générale, vous voudrez construire directement un SparseObservable directement, plutôt que d'essayer de le décomposer.


Représentation

La représentation interne de a SparseObservable ne stocke que les opérateurs de qubits non identiques. Il est donc nettement plus efficace pour représenter des observables tels que nqubitsZ(n)\sum_{n\in \text{qubits}} Z^{(n)}; SparseObservable nécessite une quantité de mémoire linéaire par rapport au nombre total de qubits, tandis que SparsePauliOp s'échelonne de manière quadratique.

Les termes sont stockés de manière comprimée, dans un esprit similaire au format compressé des rangées éparses des matrices éparses. Dans cette analogie, les termes de la somme sont les "lignes" et les termes du qubit sont les "colonnes", où une entrée absente représente l'identité plutôt qu'un zéro. Plus explicitement, la représentation est constituée de quatre tableaux contigus :

Attribut
Longueur
Description
coeffsttLe multiplicateur scalaire complexe pour chaque terme.
bit_termsssChacun des termes non-identiques du qubit unique pour tous les opérateurs, dans l'ordre. Elles correspondent à la non-identité Ai(n)A^{(n)}_i dans la description de la somme, où les entrées sont stockées dans l'ordre croissant de ii en premier, et dans l'ordre croissant de nn à l'intérieur de chaque terme.
indicesssLe qubit correspondant ( nn ) pour chacun des opérateurs dans bit_terms. SparseObservable exige que cette liste soit triée par terme, et les algorithmes peuvent compter sur le respect de cet invariant.
boundariest+1t+1Les indices qui partitionnent bit_terms et indices en termes complets. Pour le terme numéro ii, son coefficient complexe est coeffs[i], et ses opérateurs de qubit unique non identiques et leurs qubits correspondants sont la tranche boundaries[i] : boundaries[i+1] en bit_terms et indices respectivement. boundaries a toujours un 0 explicite comme premier élément.

Le paramètre de longueur tt est le nombre de termes dans la somme, et le paramètre ss est le nombre total de termes de qubits uniques non identiques.

A titre d'exemple :

  • dans le cas d'un opérateur zéro, boundaries est de longueur 1 (un seul 0) et tous les autres vecteurs sont vides.
  • dans le cas d'un opérateur d'identité entièrement simplifié, boundaries est [0, 0], coeffs a une seule entrée, et bit_terms et indices sont vides.
  • pour l'opérateur Z2Z0X3Y1Z_2 Z_0 - X_3 Y_1, boundaries est [0, 2, 4], coeffs est [1.0, -1.0], bit_terms est [BitTerm.Z, BitTerm.Z, BitTerm.Y, BitTerm.X] et indices est [0, 2, 1, 3]. L'opérateur peut agir sur plus de quatre qubits, en fonction du num_qubits paramètre. Les bit_terms sont des valeurs entières, dont les nombres magiques sont accessibles via la classe d'attributs BitTerm la classe d'attributs. Notez que les termes et indices à un seul bit sont triés dans l'ordre des termes. Il s'agit d'une exigence de la classe.

Ces cas ne sont pas particuliers, ils sont parfaitement conformes aux règles et ne devraient pas nécessiter de traitement particulier.

L'élément scalaire du tableau bit_terms est stocké sous la forme d'un octet numérique. Les valeurs numériques sont liées à la représentation symplectique de Pauli utilisée SparsePauliOp utilise, et sont accessibles avec un accès nommé par une énumération :

BitTerm

class BitTerm

GitHub

Un IntEnum qui fournit un accès nommé aux valeurs numériques utilisées pour représenter chacun des termes de l'alphabet du qubit unique énumérés dans Alphabet des termes du qubit unique utilisés dans SparseObservable.

Cette classe est rattachée à SparseObservable. Accédez-y en tant que SparseObservable.BitTerm. Si la saisie est trop lourde, que vous ne traitez que des objets :class:¬SparseObservable` et que le nom BitTerm n'est pas ambigu, vous pouvez l'abréger comme suit :

>>> ops = SparseObservable.BitTerm
>>> assert ops.X is SparseObservable.BitTerm.X

Vous pouvez accéder à toutes les valeurs de l'énumération soit par leur nom complet en majuscules, soit par leur libellé en une seule lettre. Les étiquettes à une lettre ne sont généralement pas des identifiants valables sur Python. Vous devez donc utiliser la notation d'indexation pour y accéder :

>>> assert SparseObservable.BitTerm.ZERO is SparseObservable.BitTerm["0"]

La structure numérique de ces valeurs est la suivante : il s'agit de valeurs sur quatre bits dont les deux bits inférieurs sont la représentation symplectique (sans phase) de l'opérateur de Pauli lié à l'objet, où le bit inférieur indique une contribution de ZZ et le deuxième bit inférieur une contribution de XX, tandis que les deux bits supérieurs sont 00 pour un opérateur de Pauli, 01 pour le projecteur d'état propre négatif et 10 pour le projecteur d'état propre positif.

Valeurs

X

Valeur par défaut : 2

L'opérateur Pauli XX. Utilise l'étiquette à lettre unique "X".

PLUS

Valeur par défaut : 10

Le projecteur de l'état propre positif de l'opérateur XX : ++\lvert+\rangle\langle+\rvert. Utilise l'étiquette à une lettre "+".

MINUS

Valeur par défaut : 6

Le projecteur de l'état propre négatif de l'opérateur XX : \lvert-\rangle\langle-\rvert. Utilise l'étiquette à une lettre "-".

Y

Valeur par défaut : 3

L'opérateur Pauli YY. Utilise l'étiquette à lettre unique "Y".

DROITE

Valeur par défaut : 11

Le projecteur de l'état propre positif de l'opérateur YY : rr\lvert r\rangle\langle r\rvert. Utilise l'étiquette à une lettre "r".

GAUCHE

Valeur par défaut : 7

Le projecteur de l'état propre négatif de l'opérateur YY : ll\lvert l\rangle\langle l\rvert. Utilise l'étiquette à une lettre "l".

Z

Valeur par défaut : 1

L'opérateur Pauli ZZ. Utilise l'étiquette à lettre unique "Z".

zéro

Valeur par défaut : 9

Le projecteur de l'état propre positif de l'opérateur ZZ : 00\lvert0\rangle\langle0\rvert. Utilise l'étiquette à une lettre "0".

ONE

Valeur par défaut : 5

Le projecteur de l'état propre négatif de l'opérateur ZZ : 11\lvert1\rangle\langle1\rvert. Utilise l'étiquette à une lettre "1".

Attributs

label

L'étiquette de chaîne à un seul caractère utilisée pour représenter ce terme dans l'alphabet SparseObservable alphabet.

Chacun des attributs de type tableau se comporte comme une séquence Python. Vous pouvez les indexer et les découper avec une sémantique list-standard. Le découpage d'un attribut renvoie un Numpy ndarray contenant une copie des données pertinentes avec le dtype naturel du champ; cela vous permet d'effectuer facilement des opérations mathématiques sur les résultats, comme des opérations de type bitwise sur bit_terms. Vous pouvez attribuer des indices ou des tranches à chacun des attributs, mais vous devez respecter les règles de cohérence des données lors de cette opération. Par exemple :

>>> obs = SparseObservable.from_list([("XZY", 1.5j), ("+1r", -0.5)])
>>> assert isinstance(obs.coeffs[:], np.ndarray)
>>> # Reduce all single-qubit terms to the relevant Pauli operator, if they are a projector.
>>> obs.bit_terms[:] = obs.bit_terms[:] & 0b00_11
>>> assert obs == SparseObservable.from_list([("XZY", 1.5j), ("XZY", -0.5)])
Remarque

La réduction ci-dessus aux bases de Pauli peut également être réalisée avec pauli_bases().

Ordre canonique

Pour une observable mathématique donnée, il existe plusieurs façons de la représenter avec SparseObservable. Par exemple, le même ensemble de termes d'un seul bit et leurs indices correspondants peuvent apparaître plusieurs fois dans l'observable. Mathématiquement, cela équivaut à n'avoir qu'un seul terme dont tous les coefficients sont additionnés. De même, les termes de la somme dans a SparseObservable peuvent être dans n'importe quel ordre tout en représentant la même observable, puisque l'addition est commutative (bien que l'addition en virgule flottante ne soit pas associative), SparseObservable ne donne aucune garantie quant à l'ordre de la somme).

Ces deux types de dégénérescence de représentation peuvent amener == l'opérateur à conclure que deux observables ne sont pas égales, bien qu'elles représentent le même objet. Dans ces cas-là, il peut être utile de définir une forme canonique, qui permette de comparer les observables sur le plan structurel.

Vous pouvez mettre un SparseObservable sous forme canonique en utilisant la méthode simplify() méthode. L'ordre précis des termes dans l'ordre canonique n'est pas spécifié et peut changer d'une version à l'autre de Qiskit. Dans la même version de Qiskit, cependant, vous pouvez comparer deux observables structurellement en comparant leurs formes simplifiées.

Remarque

Si vous souhaitez tenir compte de la tolérance de la virgule flottante dans la comparaison, il est plus sûr d'utiliser une recette telle que :

def equivalent(left, right, tol):
    return (left - right).simplify(tol) == SparseObservable.zero(left.num_qubits)
Remarque

La forme canonique produite par simplify() ne permettra pas à elle seule de détecter universellement toutes les observables équivalentes en raison de l'alphabet de base trop complet. Pour obtenir une expression unique, vous pouvez d'abord représenter l'observable en utilisant uniquement des termes de Pauli en appelant as_paulis()suivi de simplify(). Il convient de noter que l'expansion du projecteur (par ex. + en I et X) n'est pas réalisable à l'échelle.

Indexation

SparseObservable se comporte comme une séquence Python (la forme standard, pas la forme développée collections.abc.Sequence). L'observable peut être indexé par des nombres entiers et parcouru par itération pour obtenir des termes individuels.

Chaque terme apparaît comme une instance d'une classe autonome. Les termes individuels sont copiés à partir de l'observable de base; les mutations qu'ils subissent n'affectent pas l'observable.

Terme

class Term(num_qubits, coeff, bit_terms, indices)

GitHub

Bases : object

Un seul terme d'un ensemble complet SparseObservable.

Ceux-ci sont généralement créés par indexation ou itération dans un fichier SparseObservable.

bit_labels

bit_labels()

Renvoie les étiquettes de bits du terme sous forme de chaîne de caractères.

Les étiquettes de bits correspondent à l'ordre de SparseTerm.indices, de sorte que le i-ième caractère de la chaîne est appliqué à l'index du qubit à term.indices[i].

Retours

Les termes binaires non identitaires sous forme de chaîne concaténée.

bit_terms

Vue en lecture seule sur les termes individuels d'un qubit unique.

Les seules valeurs valables dans le tableau sont celles qui ont une valeur correspondante de BitTerm.

coeff

Le coefficient du terme.

copy

copy()

Obtenez une copie de ce terme.

indices

Vue en lecture seule sur les indices de chaque terme non identitaire d'un qubit unique.

Les indices seront toujours triés.

num_qubits

Le nombre de qubits sur lesquels le terme est défini.

pauli_base

pauli_base()

Obtenir un Pauli qui représente la base de mesure nécessaire pour ce terme.

Par exemple, le projecteur 0l+ renverra un Pauli ZYX. Le résultat Pauli est dense, dans le sens où les identités explicites sont stockées. Une identité dans la sortie de Pauli ne nécessite pas de mesure concrète.

Retourne : Pauli: l'opérateur de Pauli représentant l'opérateur nécessaire

la base de mesure.

Voir aussi

SparseObservable.pauli_bases()

Une méthode similaire pour un observable entier à la fois.

to_observable

to_observable()

Convertir ce terme en un terme complet SparseObservable.


Construction

SparseObservable définit plusieurs constructeurs. Le constructeur par défaut tentera de déléguer à l'un des constructeurs plus spécifiques, en fonction du type d'entrée. Vous pouvez toujours utiliser les constructeurs spécifiques pour mieux contrôler la construction.

Méthode
Récapitulatif
from_label()Convertir une étiquette de chaîne dense en un terme unique SparseObservable.
from_list()Somme une liste de tuples d'étiquettes de chaînes denses et les coefficients associés dans un observable.
from_sparse_list()Somme une liste de tuples d'étiquettes de chaînes éparses, les qubits auxquels elles s'appliquent, et leurs coefficients dans un observable.
from_pauli()Transformez un simple Pauli en un terme unique SparseObservable.
from_sparse_pauli_op()Élever un SparsePauliOp en un SparseObservable.
from_terms()Somme des instances individuelles explicites Term instances.
from_raw_parts()Construire l'observable à partir des tableaux de données brutes.

__new__

__new__(data, /, num_qubits=None)

Le constructeur par défaut de SparseObservable.

Elle délègue à l'une des méthodes explicites de conversion-construction, en fonction du type de l'argument data . Si num_qubits est fourni et que le constructeur impliqué par le type de data n'accepte pas de nombre, l'entier donné doit correspondre à l'entrée.

Paramètres

  • data - Le type de données de l'entrée. Il peut s'agir d'un autre SparseObservableauquel cas l'entrée est copiée, un Pauli ou SparsePauliOp, auquel cas from_pauli() ou from_sparse_pauli_op() sont appelés comme il convient, ou il peut s'agir d'une liste dans un format valide pour soit from_list() ou from_sparse_list().
  • num_qubits (int|None) – Nombre facultatif de qubits pour l'opérateur. Pour la plupart des entrées de données, ce paramètre peut être déduit et n'a pas besoin d'être transmis. Elle n'est nécessaire que pour les listes vides ou le format "sparse-list". S'il est donné inutilement, il doit correspondre aux données saisies.

Outre les constructeurs basés sur la conversion, il existe également des méthodes d'aide qui construisent des formes spéciales d'observables.

Méthode
Récapitulatif
zero()L'opérateur zéro sur un nombre donné de qubits.
identity()L'opérateur d'identité sur un nombre donné de qubits.

Conversions

Un fichier existant SparseObservable peut être converti en d'autres quantum_info formats ou en formats génériques. SparseObservableSachez que d'autres objets pourraient ne pas être en mesure de représenter la même grandeur observable aussi efficacement que celui-ci, et pourraient notamment nécessiter une quantité de mémoire exponentiellement plus importante.

Méthode
Récapitulatif
as_paulis()Créer un nouveau SparseObservablequi se développe en termes d'opérateurs de Pauli uniquement.
to_sparse_list()Exprimer l'observable sous la forme d'une liste éparse dont les éléments sont (bit_terms, indices, coeff).

En outre, SparsePauliOp.from_sparse_observable() est disponible pour la conversion de cette classe en SparsePauliOp. Attention, cette méthode souffre des mêmes problèmes d'utilisation exponentielle de la mémoire que la méthode as_paulis().


Manipulation mathématique

SparseObservable prend en charge l'ensemble standard d'opérateurs mathématiques de type « Python », à l'instar des autres quantum_info opérateurs.

En arithmétique de base, vous pouvez :

  • ajouter deux observables en utilisant +
  • soustraire deux observables en utilisant -
  • multiplier ou diviser par un int, float ou complex en utilisant * et /
  • annuler tous les coefficients d'un observable à l'aide d'une méthode unaire -

Chacun des opérateurs arithmétiques binaires de base dispose d'une méthode in-place spécialisée correspondante, qui modifie le côté gauche in-place. L'utilisation de ces opérateurs est généralement plus efficace que les opérateurs infixes, en particulier pour construire un observable dans une boucle.

Le produit tensoriel est calculé avec tensor() (pour l'ordre standard de juxtaposition des étiquettes de Pauli) ou expand() (pour l'ordre inverse). L'opérateur ^ est surchargé pour être équivalent à tensor().

Remarque

Lors de l'utilisation des opérateurs binaires ^ (tensor()) et & (compose()), prenez garde au fait que les règles de priorité des opérateurs de Python peuvent entraîner un ordre d'évaluation différent de celui auquel vous vous attendiez. En particulier, l'opérateur + se lie plus étroitement que ^ ou &, tout comme * se lie plus étroitement que +.

Lors de l'utilisation des opérateurs dans des expressions mixtes, il est plus sûr d'utiliser des parenthèses pour regrouper les opérandes des produits tensoriels.

A SparseObservable a une forme bien définie adjoint(). Les notions de conjugaison complexe scalaire (conjugate()) et de transposition des valeurs réelles (transpose()) sont définies de manière analogue à la représentation matricielle des autres opérateurs de Pauli dans Qiskit.

Remarques sur l'efficacité

En interne, SparseObservable est mutable sur place, y compris en utilisant des vecteurs croissants sur-alloués pour augmenter le nombre de termes. Cela signifie que le coût de l'ajout d'un observable à l'aide de += est amorti de manière linéaire en fonction du nombre total de termes ajoutés, plutôt qu'en fonction de la complexité quadratique qu'exigerait le site binaire + .

Les additions et les soustractions sont réalisées par une opération d'empilement de termes; il n'y a pas de "simplification" automatique (addition de termes similaires), car la majorité des additions effectuées pour construire un observable ne génèrent qu'un petit nombre de duplications, et la détection de termes similaires entraîne des coûts supplémentaires. Si cela ne correspond pas à vos cas d'utilisation, vous pouvez soit appeler périodiquement simplify()ou discuter avec nous d'autres API pour améliorer la construction des observables.


Attributs

bit_terms

Une liste plate de termes à un seul qubit. Il s'agit plus naturellement d'une liste de listes, mais elle est stockée à plat pour des raisons d'utilisation de la mémoire et de localité, les sous-listes étant désignées par des limites.

boundaries

Indices qui partitionnent bit_terms et indices en sous-listes pour chaque terme de la somme. boundaries[0] : boundaries[1] est l'intervalle des indices dans bit_terms et indices qui correspondent au premier terme de la somme. Tous les indices de qubits non spécifiés sont implicitement l'identité. C'est un élément de plus que coeffspuisque boundaries[0] est toujours un zéro explicite (pour des raisons de facilité algorithmique).

coeffs

Les coefficients de chaque terme abstrait dans la somme. Il y a autant d'éléments que de termes dans la somme.

indices

Une liste plate des indices de qubits sur lesquels agissent les entrées correspondantes dans bit_terms agissent. Cette liste doit toujours être triée par terme, un terme étant une sous-liste désignée par boundaries.

Avertissement

Si vous écrivez dans cet attribut à partir de l'espace Python, vous devez vous assurer que vous n'écrivez que dans des index qui sont triés par terme.

num_qubits

Le nombre de qubits sur lesquels l'opérateur agit.

Cela ne peut être déduit d'aucune autre forme ou valeur, puisque les identités ne sont pas stockées explicitement.

num_terms

Le nombre de termes de la somme que cet opérateur suit.


Méthodes

adjoint

adjoint()

Calculer l'adjoint de cette observable.

Cette notion est bien définie au sens mathématique abstrait. Tous les termes de l'alphabet à un qubit sont auto-adjoints, de sorte que le résultat de cette opération est la même observable, sauf que ses coefficients sont tous leurs conjugués complexes.

Exemples

>>> left = SparseObservable.from_list([("XY+-", 1j)])
>>> right = SparseObservable.from_list([("XY+-", -1j)])
>>> assert left.adjoint() == right

apply_layout

apply_layout(layout, num_qubits=None)

Appliquer un schéma de transposition à ceci SparseObservable.

En règle générale, vous aurez défini votre observable en termes de qubits virtuels des circuits que vous utiliserez pour préparer les états. Après la transpilation, les qubits virtuels sont mis en correspondance avec des qubits physiques particuliers sur un dispositif, qui peut être plus large que votre circuit. Cette cartographie peut également changer au cours du circuit. Cette méthode transforme l'observable d'entrée sur les qubits virtuels en un observable qui peut être appliqué immédiatement après le circuit physique entièrement transposé.

Paramètres

  • layout (TranspileLayout |list[int] | None) – La mise en page à appliquer. Dans la plupart des cas, cette fonction devrait recevoir le QuantumCircuit.layout champ provenant d'un circuit qui a été transpilé pour le matériel. De plus, vous pouvez transmettre une liste de nouveaux indices de qubits. NoneSi cette valeur est explicitement indiquée, aucun remappage n'est appliqué (mais vous pouvez tout de même utiliser num_qubits pour développer l'observable).
  • num_qubits (int | None) – Le nombre de qubits pour étendre l'observable. S'il n'est pas fourni, la sortie sera aussi large que l'élément donné, ou de la même largeur que l'élément donné si l'élément est donné sous une autre forme TranspileLayoutou de la même largeur que l'entrée si l'adresse layout est donnée sous une autre forme.

Retours

Un nouveau SparseObservable avec la mise en page fournie.

as_paulis

as_paulis()

Exprimer l'observable en termes de Pauli uniquement, en écrivant chaque projecteur comme une somme de termes de Pauli.

Notez qu'il n'y a aucune garantie quant à l'ordre des termes de Pauli qui en résultent. Utiliser SparseObservable.simplify() pour obtenir une représentation canonique.

Avertissement

Attention, cette méthode utilisera au moins 2n2^n termes s'il y a nn projecteurs de qubits uniques présents, ce qui peut conduire à un nombre exponentiel de termes.

Retours

La même observable, mais exprimée uniquement en termes de Pauli.

Exemples

Réécrire une observable en termes de projecteurs en opérateurs de Pauli :

>>> obs = SparseObservable("+")
>>> obs.as_paulis()
<SparseObservable with 2 terms on 1 qubit: (0.5+0j)() + (0.5+0j)(X_0)>
>>> direct = SparseObservable.from_list([("I", 0.5), ("Z", 0.5)])
>>> assert direct.simplify() == obs.as_paulis().simplify()

Pour les petits opérateurs, cela peut être utilisé avec simplify() comme forme canonique unique :

>>> left = SparseObservable.from_list([("+", 0.5), ("-", 0.5)])
>>> right = SparseObservable.from_list([("r", 0.5), ("l", 0.5)])
>>> assert left.as_paulis().simplify() == right.as_paulis().simplify()
Voir aussi

SparsePauliOp.from_sparse_observable()

Un constructeur de SparsePauliOp qui peut convertir un SparseObservable dans la SparsePauliOp représentation dense de Pauli.

clear

clear()

Effacez tous les termes de cet opérateur, ce qui le rend à nouveau égal à l'opérateur zéro.

Cela ne modifie pas la capacité des allocations internes; par conséquent, les opérations d'addition ou de soustraction ultérieures ne nécessiteront peut-être pas de réallocation.

Exemples

>>> obs = SparseObservable.from_list([("IX+-rl", 2.0), ("01YZII", -1j)])
>>> obs.clear()
>>> assert obs == SparseObservable.zero(obs.py_num_qubits())

commutes

commutes(other, tol=1e-12)

Vérifiez si l'observable commute avec un autre.

Paramètres

  • other (SparseObservable) – L'autre observable permettant de vérifier la commutation.
  • tol (float) – Si les coefficients dans le produit de soi et d'autrui sont inférieurs à la tolérance (en magnitude), les termes sont ignorés.

Retours

True si les termes commutent, jusqu'à la tolérance, False sinon.

Augmentations

TypeError – Si ne other pouvait être contraint à SparseObservable.

compose

compose(other, /, qargs=None, *, front=False)

Composer un autre SparseObservable sur celui-ci.

En termes d'algèbres d'opérateurs, la composition correspond à la multiplication à gauche : c = a.compose(b) correspond à $C = B A$. En d'autres termes, a.compose(b) renvoie un opérateur qui "exécute a, puis b sur le résultat". L'argument front=True en fait une multiplication à droite.

self et other doivent avoir la même taille, sauf si qargs est donné, auquel cas other peut être plus petit que self, à condition que le nombre de qubits dans other et la longueur de qargs correspondent. qargs ne peut jamais contenir de doublons ou d'indices de qubits qui n'existent pas dans self.

Attention, cette fonction peut entraîner une explosion exponentielle de l'utilisation de la mémoire de l'observable, car l'alphabet de SparseObservable n'est pas fermé à la composition; la composition de deux termes d'un seul bit peut être une somme, ce qui multiplie le nombre total de termes. Cette utilisation de la mémoire n'est pas nécessairement inhérente à l'observable résultant, mais la recherche d'une refactorisation efficace de la somme est généralement tout aussi difficile sur le plan du calcul. Il est préférable d'utiliser la connaissance du domaine de vos observables pour minimiser le nombre de termes existants, plutôt que d'essayer de les simplifier après coup.

Paramètres

  • other - l'observable utilisé pour multiplier à gauche self.

  • qargs -

    s'il est donné, les qubits de self à associer aux qubits de other. En d'autres termes, si elle est donnée, elle est similaire à une mise en œuvre plus efficace de :

    self.compose(other.apply_layout(qargs, self.num_qubits))

    car aucun observable temporaire n'est créé pour stocker la forme de présentation appliquée de other.

  • front - si True, multiplier à droite par other au lieu de multiplier à gauche (par défaut False). Les qargs sont toujours appliqués aux other. Ceci est particulièrement utile lorsque qargs est défini, ou que other peut être un objet qui doit être contraint à SparseObservable.

conjugate

conjugate()

Calculer la conjugaison complexe de cette observable.

Cette opération est définie en termes de conventions matricielles standard de Qiskit, en ce sens que la forme matricielle est considérée comme étant dans la base de calcul $Z$. Les termes de l'alphabet liés à $X$- et $Z$ ne sont pas affectés par la conjugaison complexe, mais les termes liés à $Y$ modifient leurs termes de l'alphabet. Précisément :

  • YY se conjugue à Y-Y
  • rr\lvert r\rangle\langle r\rvert conjugués à ll\lvert l\rangle\langle l\rvert
  • ll\lvert l\rangle\langle l\rvert conjugués à rr\lvert r\rangle\langle r\rvert

En outre, tous les coefficients sont conjugués.

Exemples

>>> obs = SparseObservable([("III", 1j), ("Yrl", 0.5)])
>>> assert obs.conjugate() == SparseObservable([("III", -1j), ("Ylr", -0.5)])

copy

copy()

Obtenir une copie de cet observable.

Exemples

>>> obs = SparseObservable.from_list([("IXZ+lr01", 2.5), ("ZXI-rl10", 0.5j)])
>>> assert obs == obs.copy()
>>> assert obs is not obs.copy()

evolve

evolve(other, /, qargs=None)

Fais évoluer cet observable à l'aide d'un terme de Pauli.

Une évolution de l' OO e observable par une PP de Pauli correspond à POPP^\dagger O P.

Contrairement à une mise en œuvre littérale via deux compositions complètes, cette méthode effectue la conjugaison directement au niveau d'un seul qubit à l'aide d'une table de correspondance fixe. Cela évite de matérialiser toute expression intermédiaire SparseObservable et permet de calculer l'observable évolué en un seul passage sur les termes. self et other doit comporter le même nombre de qubits, sauf si qargs est spécifié, auquel cas other peut être inférieur à self, à condition que le nombre de qubits dans other et la longueur de qargs soient identiques. qargs précise quels qubits de self sont mis en évolution par other.

Actuellement, cette méthode ne prend en charge l'évolution que par un opérateur à un seul terme, ce qui signifie que other doit être un opérateur de Pauli représenté par Pauli.

Paramètres

  • autre – l'opérateur de Pauli utilisé pour conjuguer self.
  • qargs – si spécifié, les qubits de self à faire évoluer par other. La longueur doit correspondre au nombre de qubits dans other.

Retours

Une nouvelle forme a été développée SparseObservable avec des conjugaisons appliquées.

Augmentations

  • TypeError – si other n'est pas de type Pauli.
  • ValueError – si self et other ont un nombre différent de qubits (et qargs n'est pas donné).
  • ValueError – si qargs la longueur ne correspond pas other au nombre de qubits.
  • ValueError – si qargs contient des doublons ou des indices hors limites.
  • ValueError – si other comporte plusieurs termes.

expand

expand(other, /)

Produit tensoriel d'ordre inverse.

Ceci est équivalent à other.tensor(self), sauf que other sera d'abord transformé en SparseObservable s'il ne l'est pas déjà (en appelant le constructeur par défaut).

Paramètres

autre - l'observable à placer du côté gauche du produit tensoriel.

Exemples

Cela équivaut à tensor() en inversant l'ordre des arguments :

>>> left = SparseObservable.from_label("XYZ")
>>> right = SparseObservable.from_label("+-IIrl")
>>> assert left.tensor(right) == right.expand(left)
Voir aussi

tensor()

La même fonction avec l'ordre des arguments inversé. tensor() est l'ordre des arguments le plus standard, et correspond aux autres conventions de Qiskit.

from_label

static from_label(label, /)

Construire une observable à terme unique à partir d'une étiquette de chaîne dense.

L'opérateur résultant aura un coefficient de 1. L'étiquette doit être une séquence de l'alphabet 'IXYZ+-rl01'. L'étiquette est interprétée de manière analogue à une chaîne de bits. En d'autres termes, la lettre la plus à droite est associée au qubit 0, et ainsi de suite. C'est la même chose que les étiquettes pour Pauli et SparsePauliOp.

Paramètres

label (str) – l'étiquette dense.

Exemples

>>> SparseObservable.from_label("IIII+ZI")
<SparseObservable with 1 term on 7 qubits: (1+0j)(+_2 Z_1)>
>>> label = "IYXZI"
>>> pauli = Pauli(label)
>>> assert SparseObservable.from_label(label) == SparseObservable.from_pauli(pauli)
Voir aussi

from_list()

Une généralisation de cette méthode qui construit un opérateur de somme à partir de plusieurs étiquettes et de leurs coefficients correspondants.

from_list

static from_list(iter, /, *, num_qubits=None)

Construire un observable à partir d'une liste d'étiquettes et de coefficients denses.

Il est analogue à SparsePauliOp.from_list()sauf qu'il utilise l'alphabet étendu de SparseObservable. Dans ce formulaire dense, vous devez fournir toutes les identités explicitement dans chaque étiquette.

L'étiquette doit être une séquence de l'alphabet 'IXYZ+-rl01'. L'étiquette est interprétée de manière analogue à une chaîne de bits. En d'autres termes, la lettre la plus à droite est associée au qubit 0, et ainsi de suite. C'est la même chose que les étiquettes pour Pauli et SparsePauliOp.

Paramètres

  • iter (list[tuple[str, complex]]) – Paires d'étiquettes et leurs coefficients associés à additionner. Les étiquettes sont interprétées de la même manière qu'en from_label().
  • num_qubits (int | None) – Il n'est pas nécessaire de le préciser si vous êtes sûr que iter n'est pas une séquence vide, puisqu'elle peut être déduite de la longueur des étiquettes. Si iter peut être vide, vous devez spécifier cet argument pour déterminer le nombre de qubits pour lesquels l'observable est utilisé. Si cette valeur est donnée et que iter n'est pas vide, la valeur doit correspondre à la longueur des étiquettes.

Exemples

Construire un observable à partir d'une liste d'étiquettes de même longueur :

>>> SparseObservable.from_list([
...     ("III++", 1.0),
...     ("II--I", 1.0j),
...     ("I++II", -0.5),
...     ("--III", -0.25j),
... ])
<SparseObservable with 4 terms on 5 qubits:
    (1+0j)(+_1 +_0) + (0+1j)(-_2 -_1) + (-0.5+0j)(+_3 +_2) + (-0-0.25j)(-_4 -_3)>

Utiliser num_qubits pour désambiguïser les entrées potentiellement vides :

>>> SparseObservable.from_list([], num_qubits=10)
<SparseObservable with 0 terms on 10 qubits: 0.0>

Cette méthode est équivalente aux appels à from_sparse_list() avec le champ explicite qubit-arguments fixé à des entiers décroissants :

>>> labels = ["XY+Z", "rl01", "-lXZ"]
>>> coeffs = [1.5j, 2.0, -0.5]
>>> from_list = SparseObservable.from_list(list(zip(labels, coeffs)))
>>> from_sparse_list = SparseObservable.from_sparse_list([
...     (label, (3, 2, 1, 0), coeff)
...     for label, coeff in zip(labels, coeffs)
... ])
>>> assert from_list == from_sparse_list
Voir aussi

from_label()

Un constructeur similaire, mais qui ne prend qu'une seule étiquette et dont le coefficient est toujours fixé à 1.0.

from_sparse_list()

Construire l'observable à partir d'une liste d'étiquettes sans identités explicites, mais avec les qubits auxquels s'applique chaque terme de qubit unique listés explicitement.

from_pauli

static from_pauli(pauli, /)

Construire un SparseObservable à partir d'une seule instance de Pauli instance.

L'observable de sortie aura un seul terme, avec un coefficient unitaire dépendant de la phase.

Paramètres

pauli (Pauli) – le seul Pauli à convertir.

Exemples

>>> label = "IYXZI"
>>> pauli = Pauli(label)
>>> SparseObservable.from_pauli(pauli)
<SparseObservable with 1 term on 5 qubits: (1+0j)(Y_3 X_2 Z_1)>
>>> assert SparseObservable.from_label(label) == SparseObservable.from_pauli(pauli)

from_raw_parts

static from_raw_parts(num_qubits, coeffs, bit_terms, indices, boundaries, check=True)

Construire un SparseObservable à partir de tableaux Numpy bruts qui correspondent à la représentation des données requise décrite dans la documentation au niveau de la classe.

Les données de chaque tableau sont copiées dans des allocations d'espace Rust fraîches et évolutives.

Paramètres

  • num_qubits - nombre de qubits dans l'observable.

  • coeffs - coefficients complexes de chaque terme de l'observable. Il doit s'agir d'un tableau Numpy avec le dtype complex128.

  • bit_terms - liste aplatie des termes à qubit unique comprenant tous les termes complets. Il doit s'agir d'un tableau Numpy avec le dtype uint8 (qui est compatible avec BitTerm).

  • indices - liste triée par terme aplati des qubits auxquels correspond chaque terme d'un qubit unique. Il doit s'agir d'un tableau Numpy avec le dtype uint32.

  • les limites - les indices qui divisent bit_terms et indices en termes. Il doit s'agir d'un tableau Numpy avec le dtype uintp.

  • vérifier -

    si True (par défaut), valider que les données satisfont à toutes les garanties de cohérence. Si False, aucun contrôle n'est effectué.

    Avertissement

    Si check=False, les bit_terms doivent absolument être toutes des valeurs valides de SparseObservable.BitTerm. Si ce n'est pas le cas, un comportement indéfini dans l'espace Rust peut se produire, invalidant entièrement l'exécution du programme.

Exemples

Construire une somme de ZZ sur chaque qubit individuel :

>>> num_qubits = 100
>>> terms = np.full((num_qubits,), SparseObservable.BitTerm.Z, dtype=np.uint8)
>>> indices = np.arange(num_qubits, dtype=np.uint32)
>>> coeffs = np.ones((num_qubits,), dtype=complex)
>>> boundaries = np.arange(num_qubits + 1, dtype=np.uintp)
>>> SparseObservable.from_raw_parts(num_qubits, coeffs, terms, indices, boundaries)
<SparseObservable with 100 terms on 100 qubits: (1+0j)(Z_0) + ... + (1+0j)(Z_99)>

from_sparse_list

static from_sparse_list(iter, /, num_qubits)

Construire un observable à partir d'une liste d'étiquettes, des qubits auxquels chaque élément s'applique et du coefficient du terme entier.

Il est analogue à SparsePauliOp.from_sparse_list()sauf qu'il utilise l'alphabet étendu de SparseObservable.

Les champs "labels" et "indices" des triples sont associés en les zippant ensemble. Par exemple, cela signifie qu'un appel à from_list() peut être converti dans la forme utilisée par cette méthode en fixant le champ "indices" de chaque triple à (num_qubits-1, ..., 1, 0).

Paramètres

  • iter (list[tuple[str, Sequence[int], complex]]) – des triples d'étiquettes, les qubits auxquels s'applique chaque terme de qubit unique et le coefficient du terme entier.
  • num_qubits (int) – le nombre de qubits dans l'opérateur.

Exemples

Construire un opérateur simple :

>>> SparseObservable.from_sparse_list(
...     [("ZX", (1, 4), 1.0), ("YY", (0, 3), 2j)],
...     num_qubits=5,
... )
<SparseObservable with 2 terms on 5 qubits: (1+0j)(X_4 Z_1) + (0+2j)(Y_3 Y_0)>

Construire l'observable d'identité (bien qu'en réalité, il suffise d'utiliser identity()) :

>>> SparseObservable.from_sparse_list([("", (), 1.0)], num_qubits=100)
<SparseObservable with 1 term on 100 qubits: (1+0j)()>

Cette méthode peut reproduire le comportement de from_list()si le champ qubit-arguments du triple est réglé sur des nombres entiers décroissants :

>>> labels = ["XY+Z", "rl01", "-lXZ"]
>>> coeffs = [1.5j, 2.0, -0.5]
>>> from_list = SparseObservable.from_list(list(zip(labels, coeffs)))
>>> from_sparse_list = SparseObservable.from_sparse_list([
...     (label, (3, 2, 1, 0), coeff)
...     for label, coeff in zip(labels, coeffs)
... ])
>>> assert from_list == from_sparse_list
Voir aussi

to_sparse_list()

L'inverse de cette méthode.

from_sparse_pauli_op

static from_sparse_pauli_op(op, /)

Construire un SparseObservable à partir d'une SparsePauliOp d'une instance.

Il s'agira d'une traduction largement directe de l'article SparsePauliOpen particulier, il n'y a pas de sommation à la volée de termes similaires, ni de tentative de refactorisation des sommes de termes de Pauli en opérateurs de projection équivalents.

Paramètres

op (SparsePauliOp) – l'opérateur à convertir.

Exemples

>>> spo = SparsePauliOp.from_list([("III", 1.0), ("IIZ", 0.5), ("IZI", 0.5)])
>>> SparseObservable.from_sparse_pauli_op(spo)
<SparseObservable with 3 terms on 3 qubits: (1+0j)() + (0.5+0j)(Z_0) + (0.5+0j)(Z_1)>

from_terms

static from_terms(obj, /, num_qubits=None)

Construire un SparseObservable à partir de termes individuels.

Tous les termes doivent avoir le même nombre de qubits. S'il est fourni, l'argument num_qubits doit correspondre aux termes.

Aucune simplification n'est effectuée dans le cadre de la création observable.

Paramètres

  • obj (Iterable[Term]) – Itérable de termes individuels à partir desquels l'observable est construit.
  • num_qubits (int | None) – Le nombre de qubits sur lesquels l'observable doit agir. Elle est généralement déduite de l'entrée, mais peut être explicitement donnée pour gérer le cas d'un itérable vide.

Retours

L'observable correspondant.

identity

static identity(num_qubits)

Obtenir l'opérateur d'identité sur le nombre de qubits donné.

Exemples

Obtenez l'opérateur d'identité pour 100 qubits :

>>> SparseObservable.identity(100)
<SparseObservable with 1 term on 100 qubits: (1+0j)()>

pauli_bases

pauli_bases()

Obtenir un PauliList qui représente la base de mesure nécessaire pour chaque terme (dans l'ordre) de cet observable.

Par exemple, le projecteur 0l+ renverra un Pauli ZXY. Le résultat Pauli est dense, dans le sens où les identités explicites sont stockées. Une identité dans la sortie de Pauli ne nécessite pas de mesure concrète.

Cela renvoie une entrée dans la liste de Pauli pour chaque terme de la somme.

Retours

la liste des opérateurs de Pauli représentant les bases de mesure nécessaires.

Type de retour

PauliList

simplify

simplify(tol=1e-08)

Additionner tous les termes similaires dans cet opérateur, en les supprimant si le coefficient complexe résultant a une valeur absolue dans la tolérance de zéro.

Cette opération a pour effet secondaire de trier l'opérateur dans l' ordre canonique.

Remarque

Lorsque vous utilisez cette fonction pour des comparaisons d'égalité, notez que l'arrondi des nombres à virgule flottante et la non-associativité de l'addition de nombres à virgule flottante peuvent faire en sorte que des coefficients non nuls de termes additionnés soient jugés inégaux. Pour comparer deux observables dans les limites d'une tolérance donnée, la méthode la plus sûre consiste à vérifier si la différence canonicisée entre ces deux observables est nulle.

Paramètres

tol (float) – après avoir additionné les termes similaires, tous les coefficients dont la valeur absolue est inférieure à la tolérance absolue donnée seront supprimés de la sortie.

Exemples

En utilisant simplify() pour comparer deux opérateurs qui représentent le même observable, mais dont la comparaison serait inégale en raison des tests structurels par défaut :

>>> base = SparseObservable.from_sparse_list([
...     ("XZ", (2, 1), 1e-10),  # value too small
...     ("+-", (3, 1), 2j),
...     ("+-", (3, 1), 2j),     # can be combined with the above
...     ("01", (3, 1), 0.5),    # out of order compared to `expected`
... ], num_qubits=5)
>>> expected = SparseObservable.from_list([("I0I1I", 0.5), ("I+I-I", 4j)])
>>> assert base != expected  # non-canonical comparison
>>> assert base.simplify() == expected.simplify()

Notez que dans l'exemple ci-dessus, les coefficients sont choisis de manière à ce que tous les calculs en virgule flottante soient exacts et qu'il n'y ait pas de problèmes d'arrondi ou d'associativité intermédiaires. Si cela ne peut être garanti, la forme la plus sûre est la suivante :

>>> left = SparseObservable.from_list([("XYZ", 1.0/3.0)] * 3)   # sums to 1.0
>>> right = SparseObservable.from_list([("XYZ", 1.0/7.0)] * 7)  # doesn't sum to 1.0
>>> assert left.simplify() != right.simplify()
>>> assert (left - right).simplify() == SparseObservable.zero(left.num_qubits)

tensor

tensor(other, /)

Produit tensoriel de deux observables.

L'ordre des bits est défini de telle sorte que les indices des qubits de l'argument restent inchangés, et que les indices de self soient décalés du nombre de qubits contenus dans other. Il s'agit de la même convention que celle utilisée par les autres opérateurs de Qiskit quantum_info .

Cette fonction est utilisée pour l'opérateur infixe ^ . Si vous utilisez cet opérateur, sachez que les règles de priorité des opérateurs de Python peuvent modifier l'ordre d'évaluation par rapport à vos attentes. En particulier, l'opérateur + se lie plus étroitement que ^, tout comme * se lie plus étroitement que +. Utilisez des parenthèses pour fixer l'ordre d'évaluation, si nécessaire.

L'argument sera transformé en SparseObservable en utilisant son constructeur par défaut, s'il n'est pas déjà sous la forme correcte.

Paramètres

autre - l'observable à placer du côté droit du produit tensoriel.

Exemples

L'ordre des bits est tel que le produit tensoriel de deux observables obtenues à partir d'une seule étiquette "ressemble" à une observable obtenue en concaténant les deux chaînes :

>>> left = SparseObservable.from_label("XYZ")
>>> right = SparseObservable.from_label("+-IIrl")
>>> assert left.tensor(right) == SparseObservable.from_label("XYZ+-IIrl")

Vous pouvez également utiliser l'opérateur infixe ^ pour les produits tensoriels, qui moulera de manière similaire le côté droit de l'opération s'il ne s'agit pas déjà d'un produit tensoriel SparseObservable:

>>> assert SparseObservable("rl") ^ Pauli("XYZ") == SparseObservable("rlXYZ")
Voir aussi

expand()

La même fonction, mais avec l'ordre des arguments inversé. Cela peut s'avérer utile si vous souhaitez utiliser le comportement du casting pour l'argument, mais que vous voulez que vos SparseObservable soit du côté droit de l'ordre tensoriel.

to_sparse_list

to_sparse_list()

Exprimez l'observable en termes de format de liste clairsemée.

Cela peut être considéré comme une contre-opération de SparseObservable.from_sparse_list()mais il n'est pas garanti que l'ordre des termes soit le même après un aller-retour vers une liste clairsemée.

Exemples

>>> obs = SparseObservable.from_list([("IIXIZ", 2j), ("IIZIX", 2j)])
>>> reconstructed = SparseObservable.from_sparse_list(obs.to_sparse_list(), obs.num_qubits)
Voir aussi

from_sparse_list()

Le constructeur qui peut interpréter ces listes.

transpose

transpose()

Calculer la transposition matricielle de cet observable.

Cette opération est définie en termes de conventions matricielles standard de Qiskit, en ce sens que la forme matricielle est considérée comme étant dans la base de calcul $Z$. Les termes de l'alphabet liés à $X$- et $Z$ ne sont pas affectés par la transposition, mais les termes liés à $Y$ modifient leurs termes de l'alphabet. Précisément :

  • YY transpose en Y-Y
  • rr\lvert r\rangle\langle r\rvert transpose en ll\lvert l\rangle\langle l\rvert
  • ll\lvert l\rangle\langle l\rvert transpose en rr\lvert r\rangle\langle r\rvert

Exemples

>>> obs = SparseObservable([("III", 1j), ("Yrl", 0.5)])
>>> assert obs.transpose() == SparseObservable([("III", 1j), ("Ylr", -0.5)])

zero

static zero(num_qubits)

Obtenir l'opérateur zéro sur le nombre de qubits donné.

L'opérateur zéro est l'opérateur dont la valeur d'espérance est nulle pour tous les états quantiques. Il n'y a pas de termes. C'est l'élément d'identité pour l'addition de deux instances SparseObservable tout ce qui est ajouté à l'opérateur zéro est égal à lui-même.

Si vous souhaitez que le projecteur passe à l'état zéro, utilisez :

>>> num_qubits = 10
>>> all_zeros = SparseObservable.from_label("0" * num_qubits)

Exemples

Obtenez l'opérateur zéro pour 100 qubits :

>>> SparseObservable.zero(100)
<SparseObservable with 0 terms on 100 qubits: 0.0>
Cette page a-t-elle été utile ?
Signaler un bogue, une coquille ou proposer du contenu sur GitHub.