SparseObservable
class qiskit.quantum_info.SparseObservable(data, /, num_qubits=None)
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
pour les nombres complexes et les opérateurs à qubit unique agissant sur le qubit à partir d'un alphabet restreint . La somme sur 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 sont tirés. Il s'agit explicitement de
Libellé | Opérateur | Valeur numérique | BitTerm attribut |
|---|---|---|---|
"I" | (identité) | Non stocké. | Non stocké. |
"X" | (Pauli X) | 0b0010 (2) | X |
"Y" | (Pauli Y) | 0b0011 (3) | Y |
"Z" | (Pauli Z) | 0b0001 (1) | Z |
"+" | (projecteur sur un état propre positif de X) | 0b1010 (10) | PLUS |
"-" | (projecteur sur l'état propre négatif de X) | 0b0110 (6) | MINUS |
"r" | (projecteur sur un état propre positif de Y) | 0b1011 (11) | RIGHT |
"l" | (projecteur sur l'état propre négatif de Y) | 0b0111 (7) | LEFT |
"0" | (projecteur sur un état propre positif de Z) | 0b1001 (9) | ZERO |
"1" | (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 peut être mesuré efficacement sur le matériel avec de simples mesures , mais ne peut être représenté par SparsePauliOp comme , ce qui nécessite des termes stockés . 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 ; 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 |
|---|---|---|
coeffs | Le multiplicateur scalaire complexe pour chaque terme. | |
bit_terms | Chacun des termes non-identiques du qubit unique pour tous les opérateurs, dans l'ordre. Elles correspondent à la non-identité dans la description de la somme, où les entrées sont stockées dans l'ordre croissant de en premier, et dans l'ordre croissant de à l'intérieur de chaque terme. | |
indices | Le qubit correspondant ( ) 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. | |
boundaries | Les indices qui partitionnent bit_terms et indices en termes complets. Pour le terme numéro , 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 est le nombre de termes dans la somme, et le paramètre est le nombre total de termes de qubits uniques non identiques.
A titre d'exemple :
- dans le cas d'un opérateur zéro,
boundariesest 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é,
boundariesest[0, 0],coeffsa une seule entrée, etbit_termsetindicessont vides. - pour l'opérateur ,
boundariesest[0, 2, 4],coeffsest[1.0, -1.0],bit_termsest[BitTerm.Z, BitTerm.Z, BitTerm.Y, BitTerm.X]etindicesest[0, 2, 1, 3]. L'opérateur peut agir sur plus de quatre qubits, en fonction dunum_qubitsparamètre. Lesbit_termssont des valeurs entières, dont les nombres magiques sont accessibles via la classe d'attributsBitTermla 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
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.XVous 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 et le deuxième bit inférieur une contribution de , 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 . Utilise l'étiquette à lettre unique "X".
PLUS
Valeur par défaut : 10
Le projecteur de l'état propre positif de l'opérateur : . Utilise l'étiquette à une lettre "+".
MINUS
Valeur par défaut : 6
Le projecteur de l'état propre négatif de l'opérateur : . Utilise l'étiquette à une lettre "-".
Y
Valeur par défaut : 3
L'opérateur Pauli . Utilise l'étiquette à lettre unique "Y".
DROITE
Valeur par défaut : 11
Le projecteur de l'état propre positif de l'opérateur : . Utilise l'étiquette à une lettre "r".
GAUCHE
Valeur par défaut : 7
Le projecteur de l'état propre négatif de l'opérateur : . Utilise l'étiquette à une lettre "l".
Z
Valeur par défaut : 1
L'opérateur Pauli . Utilise l'étiquette à lettre unique "Z".
zéro
Valeur par défaut : 9
Le projecteur de l'état propre positif de l'opérateur : . Utilise l'étiquette à une lettre "0".
ONE
Valeur par défaut : 5
Le projecteur de l'état propre négatif de l'opérateur : . 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)])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.
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)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)
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.
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, unPauliouSparsePauliOp, auquel casfrom_pauli()oufrom_sparse_pauli_op()sont appelés comme il convient, ou il peut s'agir d'une liste dans un format valide pour soitfrom_list()oufrom_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,floatoucomplexen 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().
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.
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() == rightapply_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.layoutchamp 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 utilisernum_qubitspour 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'adresselayoutest 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.
Attention, cette méthode utilisera au moins termes s'il y a 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()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 deother. 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 parotherau lieu de multiplier à gauche (par défautFalse). Lesqargssont toujours appliqués auxother. Ceci est particulièrement utile lorsqueqargsest défini, ou queotherpeut ê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 :
- se conjugue à
- conjugués à
- conjugués à
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' e observable par une de Pauli correspond à .
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 parother. La longueur doit correspondre au nombre de qubits dansother.
Retours
Une nouvelle forme a été développée SparseObservable avec des conjugaisons appliquées.
Augmentations
- TypeError – si
othern'est pas de typePauli. - ValueError – si
selfetotheront un nombre différent de qubits (etqargsn'est pas donné). - ValueError – si
qargsla longueur ne correspond pasotherau nombre de qubits. - ValueError – si
qargscontient des doublons ou des indices hors limites. - ValueError – si
othercomporte 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)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)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
itern'est pas une séquence vide, puisqu'elle peut être déduite de la longueur des étiquettes. Siiterpeut ê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 queitern'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_listUn constructeur similaire, mais qui ne prend qu'une seule étiquette et dont le coefficient est toujours fixé à 1.0.
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 avecBitTerm). -
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_termsetindicesen termes. Il doit s'agir d'un tableau Numpy avec le dtypeuintp. -
vérifier -
si
True(par défaut), valider que les données satisfont à toutes les garanties de cohérence. SiFalse, aucun contrôle n'est effectué.AvertissementSi
check=False, lesbit_termsdoivent absolument être toutes des valeurs valides deSparseObservable.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 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_listL'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
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.
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")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
SparseObservablesoit 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)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 :
- transpose en
- transpose en
- transpose en
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>