Annotations de circuit
qiskit.circuit.annotation
Ce module contient l'infrastructure nécessaire pour travailler avec des annotations de circuits personnalisées.
qiskit.circuit.AnnotationLa classe principale destinée aux utilisateurs est la classe de base, qui est également réexportée depuis ce module.
Sous-classes d'annotation personnalisées
Cette Annotation classe est destinée à être sous-classée. Les sous-classes doivent définir leur namespace champ. Cela peut être spécifique à une instance ou statique pour l'ensemble d'une sous-classe. L'espace de noms est utilisé dans le cadre du mécanisme de répartition, comme décrit dans la section « Espaces de noms ».
Les contrôles d'égalité des circuits comparent également les annotations sur les objets en fonction de l'ordre. Vous voudrez probablement implémenter la méthode __eq__() dans toutes les sous-classes.
Si vous souhaitez que votre annotation puisse être sérialisée via QPY ou :ref:\N- OpenQASM 3 <qiskit-qasm3> \N-, vous devez fournir des implémentations séparées des méthodes de sérialisation et de désérialisation, comme indiqué dans Sérialisation et désérialisation.
Espaces de noms
L'espace de noms d'une annotation est utilisé comme clé de recherche lorsqu'un consommateur décide du gestionnaire à invoquer. Cela inclut les contextes de sérialisation QPY et OpenQASM 3, mais en général, les passes de transpilation examineront également les espaces de noms des annotations pour déterminer s'ils sont pertinents, et ainsi de suite.
Il peut s'agir d'un identifiant standard de type « Python » (par exemple, my_namespace), ou une liste d'identificateurs séparés par des points (par exemple my_namespace.subnamespace). L'espace de noms est utilisé par tous les utilisateurs d'annotations pour déterminer quel gestionnaire doit être appelé.
Une fonction autonome permet de parcourir les espaces de noms et les espaces de noms parents par ordre de priorité, du plus spécifique au moins spécifique.
iter_namespaces
qiskit.circuit.annotation.iter_namespaces(namespace)
Un itérateur sur tous les espaces de noms qui peuvent être utilisés pour rechercher l'espace de noms donné.
Cela inclut l'espace de noms et tous les parents, y compris l'espace de noms racine à chaîne vide.
Exemples :
from qiskit.circuit.annotation import iter_namespaces
assert list(iter_namespaces("hello.world")) == ["hello.world", "hello", ""]Paramètres
namespace (str)
Type de retour
Sérialisation et désérialisation
Les annotations représentent des données entièrement personnalisées, qui peuvent persister après la compilation. Il peut s'agir de données qui doivent être sérialisées en vue d'une consommation ultérieure, telles que des données supplémentaires interprétées par un compilateur dorsal. Le format binaire natif QPY de Qiskit (voir qiskit.qpy) supporte le concept d'annotations arbitraires dans ses données utiles à partir de la version 15. Dans OpenQASM 3 (voir qiskit.qasm3), les annotations sont une caractéristique essentielle du langage, et le support d'importation/exportation de Qiskit pour OpenQASM 3 inclut la sérialisation des annotations.
Cependant, comme les annotations sont généralement des sous-classes personnalisées et inconnues de Qiskit, nous ne pouvons pas disposer d'une prise en charge intégrée de la sérialisation. En ce qui concerne la désérialisation, Qiskit ne disposera généralement pas d'un objet existant Annotation à partir duquel appeler les méthodes de désérialisation. On peut également s'attendre à ce que les annotations se rapportent à un état partagé, inconnu de Qiskit, au sein d'un contexte de circuit donné.
Pour toutes ces raisons, la sérialisation et la désérialisation des annotations sont gérées par des objets personnalisés, qui doivent être transmis aux points d'interface des fonctions de sérialisation concernées. Par exemple, dans QPY, l'argument annotation_factories dans qpy.dump() et qpy.load() sont utilisés pour passer des sérialiseurs.
QPYSerializer
class qiskit.circuit.annotation.QPYSerializer
Bases : ABC
L'interface pour les sérialiseurs et les désérialiseurs Annotation d'objets vers QPY.
Pour plus d'informations sur le QPY, voir qiskit.qpy.
Cette classe de définition d'interface est conçue pour être sous-classée. Les méthodes individuelles décrivent leurs contrats et la manière dont elles seront appelées.
Lors de la sérialisation et de la désérialisation de QPY, la logique principale de QPY appellera une fonction d'usine pour créer des instances de sous-classes de cette classe. La valeur de retour d'une fonction d'usine donnée sera utilisée dans un contexte de sérialisation ou de désérialisation, mais pas dans les deux.
La structure des appels lors de la sérialisation d'un circuit unique est la suivante :
- de nombreux appels à
dump_annotation(), qui partageront tous le mêmenamespaceargument, lequel sera toujours un préfixe (non strict) de tous lesAnnotationobjets fournis. - un appel à
dump_state().
La structure générale des appels lors de la désérialisation d'un circuit unique à partir d'une charge utile QPY est la suivante :
- un appel à
load_state(), en passant parnamespace(avec le même comportement de préfixation non stricte que la forme "sérialisante"). - de nombreux appels à
load_annotation(), correspondant aux annotations sérialisées sous cette recherche de préfixe d'espace de noms.
Lors de la création d'une sous-classe, rappelez-vous que QPY est conçu pour offrir des garanties strictes de compatibilité ascendante et qu'il est fortement recommandé que les sous-classes de sérialisation d'annotations conservent ces garanties. En particulier, il est suggéré que tout sérialiseur non trivial inclue des informations sur la "version" du sérialiseur dans son "état" total (voir dump_state()), et que la désérialisation fasse tout son possible pour assurer la rétrocompatibilité avec les versions antérieures du même sérialiseur.
QPYFromOpenQASM3Serializer
class qiskit.circuit.annotation.QPYFromOpenQASM3Serializer(inner)
Bases : QPYSerializer
Un adaptateur qui convertit un OpenQASM3Serializer en un QPYSerializer.
Cela fonctionne parce que les sérialiseurs d'annotations de OpenQASM 3 doivent être sans état et renvoyer UTF-8-encoded des lignes de texte uniques, ce qui est un sous-ensemble de ce que permet QPY.
Généralement, vous créez l'un d'entre eux à l'aide de la méthode as_qpy() d'un sérialiseur d'annotations OpenQASM 3.
Exemples :
Les instances de cette classe peuvent être appelées comme une fonction à zéro argument et se renvoient elles-mêmes. Cela vous permet de les utiliser directement en tant que fonction d'usine pour les points d'entrée de QPY, tels que :
import io
from qiskit.circuit import OpenQASM3Serializer, Annotation
from qiskit import qpy
class MyAnnotation(Annotation):
namespace = "my_namespace"
class MySerializer(OpenQASM3Serializer):
def dump(self, annotation):
if not isinstance(annotation, MyAnnotation):
return NotImplemented
return ""
def load(self, namespace, payload):
assert namespace == "my_namespace"
assert payload == ""
return MyAnnotation()
qc = QuantumCircuit(2)
with qc.box(annotations=[MyAnnotation()]):
qc.cx(0, 1)
with io.BytesIO() as fptr:
qpy.dump(fptr, qc, annotation_serializers = {"my_namespace": MySerializer().as_qpy()})C'est sûr, sans renvoyer d'instances séparées, parce que les sérialiseurs de base OpenQASM 3 sont nécessairement sans état.
Paramètres
inner (OpenQASM3Serializer) – le sérialiseur OpenQASM 3 dont il est dérivé.
OpenQASM3Serializer
class qiskit.circuit.annotation.OpenQASM3Serializer
Bases : ABC
L'interface pour les sérialiseurs et les désérialiseurs Annotation d'objets vers OpenQASM e 3.
Pour plus d'informations sur la prise en charge de OpenQASM 3 dans Qiskit, voir qiskit.qasm3.
Cette classe de définition d'interface est conçue pour être sous-classée. OpenQASM 3 sont sans état au sein d'un programme, par conséquent une sous-classe ne doit pas suivre l'état.
Exemples
Une passe de transpileur de collection de blocs
L'un des principaux objectifs du cadre d'annotation est de permettre aux analyses et aux commandes personnalisées d'être stockées sur les circuits de manière locale, soit par l'utilisateur à l'entrée du compilateur, soit pour un passage du compilateur afin de stocker des informations en vue d'une utilisation ultérieure.
Par exemple, nous pouvons écrire un passage de transcompilateur simple qui regroupe les séries d’opérations sur un seul qubit, place chaque série dans un BoxOp, calcule ensuite l’action unitaire totale et l’ajoute sous forme d’annotation personnalisée, de sorte que la même analyse n’ait pas à être répétée par la suite, même si le fonctionnement interne de chaque bloc est optimisé.
from qiskit.circuit import annotation, QuantumCircuit, BoxOp
from qiskit.quantum_info import Operator
from qiskit.transpiler import TransformationPass
class PerformsUnitary(annotation.Annotation):
namespace = "unitary"
def __init__(self, matrix):
self.matrix = matrix
class Collect1qRuns(TransformationPass):
def run(self, dag):
for run in dag.collect_1q_runs():
block = QuantumCircuit(1)
for node in run:
block.append(node.op, [0], [])
box = BoxOp(block, annotations=[PerformsUnitary(Operator(block).data)])
dag.replace_block_with_op(run, box, {run[0].qargs[0]: 0})
return dagAfin de sérialiser l'annotation sur OpenQASM 3, nous devons définir une logique personnalisée, puisque l'analyse elle-même est entièrement personnalisée. La sérialisation est distincte de l'annotation; il peut y avoir des circonstances dans lesquelles la sérialisation doit être effectuée différemment.
import ast
import numpy as np
class Serializer(annotation.OpenQASM3Serializer):
def dump(self, annotation):
if annotation.namespace != "unitary":
return NotImplemented
line = lambda row: "[" + ", ".join(repr(x) for x in row) + "]"
return "[" + ", ".join(line(row) for row in annotation.matrix.tolist()) + "]"
def load(self, namespace, payload):
if namespace != "unitary":
return NotImplemented
return PerformsUnitary(np.array(ast.literal_eval(payload), dtype=complex))Enfin, le tout peut être assemblé, ce qui donne le résultat OpenQASM 3.
from qiskit import qasm3
qc = QuantumCircuit(3)
qc.s(0)
qc.t(0)
qc.y(1)
qc.x(1)
qc.h(2)
qc.s(2)
collected = Collect1qRuns()(qc)
handlers = {"unitary": Serializer()}
dumped = qasm3.dumps(collected, annotation_handlers=handlers)
print(dumped)OPENQASM 3.0;
include "stdgates.inc";
qubit[3] q;
@unitary[[(1+0j), 0j], [0j, (-0.7071067811865475+0.7071067811865475j)]]
box {
s q[0];
t q[0];
}
@unitary[[1j, 0j], [0j, -1j]]
box {
y q[1];
x q[1];
}
@unitary[[(0.7071067811865475+0j), (0.7071067811865475+0j)], [0.7071067811865475j, -0.7071067811865475j]]
box {
h q[2];
s q[2];
}