Schaltungsanmerkungen
qiskit.circuit.annotation
Dieses Modul enthält die Infrastruktur für die Arbeit mit benutzerdefinierten Schaltkreiskommentaren.
qiskit.circuit.AnnotationDie wichtigste, für den Benutzer sichtbare Klasse ist die Basisklasse, die ebenfalls aus diesem Modul erneut exportiert wird.
Benutzerdefinierte Anmerkung-Unterklassen
Die Annotation Klasse ist dafür vorgesehen, dass Unterklassen davon erstellt werden. Unterklassen müssen ihr namespace Feld festlegen. Dies kann instanzspezifisch oder statisch für eine gesamte Unterklasse gelten. Der Namensraum wird als Teil des Dispatch-Mechanismus verwendet, wie unter „Namensräume“ beschrieben.
Die Gleichheitsprüfung von Schaltkreisen vergleicht auch Anmerkungen zu Objekten in einer von der Reihenfolge abhängigen Weise. Sie werden wahrscheinlich die __eq__() magische Methode in allen Unterklassen implementieren.
Wenn Sie beabsichtigen, dass Ihre Anmerkung über QPY oder :ref:` OpenQASM 3 <qiskit-qasm3> ` serialisiert werden kann, müssen Sie separate Implementierungen der Serialisierungs- und Deserialisierungsmethoden bereitstellen, wie in Serialisierung und Deserialisierung beschrieben.
Namensräume
Der "Namespace" einer Anmerkung wird als Nachschlageschlüssel verwendet, wenn ein Verbraucher entscheidet, welcher Handler aufgerufen werden soll. Dies gilt auch für QPY und OpenQASM 3 Serialisierungskontexte, aber im Allgemeinen werden bei Transpilerdurchläufen auch die Namespaces von Annotationen untersucht, um festzustellen, ob sie relevant sind usw.
Dies kann eine Standard- Python -Kennung sein (z. B. my_namespace), oder eine durch Punkte getrennte Liste von Bezeichnern (z. B. my_namespace.subnamespace). Der Namensraum wird von allen Anwendern von Annotationen verwendet, um zu bestimmen, welcher Handler aufgerufen werden soll.
Eine eigenständige Funktion ermöglicht die Iteration durch Namespaces und übergeordnete Namespaces in der Prioritätsreihenfolge vom spezifischsten zum unspezifischsten.
iter_namespaces
qiskit.circuit.annotation.iter_namespaces(namespace)
Ein Iterator über alle Namespaces, die zum Nachschlagen des angegebenen Namespaces verwendet werden können.
Dies schließt den Namespace und alle übergeordneten Namespaces ein, einschließlich des Root-Namespaces mit leerer Zeichenkette.
Beispiele:
from qiskit.circuit.annotation import iter_namespaces
assert list(iter_namespaces("hello.world")) == ["hello.world", "hello", ""]Parameter
namespace (str)
Rückgabetyp
Serialisierung und Deserialisierung
Anmerkungen stellen vollständig benutzerdefinierte Daten dar, die auch nach der Kompilierung bestehen bleiben können. Dazu können Daten gehören, die für die spätere Verwendung serialisiert werden sollten, z. B. zusätzliche Daten, die von einem Backend-Compiler interpretiert werden. Das native binäre QPY-Format von Qiskit (siehe qiskit.qpy) unterstützt ab Version 15 das Konzept beliebiger Anmerkungen in seinen Nutzdaten. In OpenQASM 3 (siehe qiskit.qasm3) sind Anmerkungen ein zentrales Sprachmerkmal, und die Import/Export-Unterstützung von Qiskit für OpenQASM 3 umfasst die Serialisierung von Annotationen.
Da es sich bei Annotationen jedoch in der Regel um benutzerdefinierte Unterklassen handelt, die Qiskit nicht bekannt sind, können wir keine integrierte Unterstützung für die Serialisierung bieten. Was die Deserialisierung betrifft, verfügt Qiskit im Allgemeinen nicht über ein vorhandenes Annotation Objekt, von dem aus Deserialisierungsmethoden aufgerufen werden können. Es ist zudem zu erwarten, dass sich Annotationen auf einen für Qiskit unbekannten gemeinsamen Zustand innerhalb eines bestimmten Schaltungskontexts beziehen können.
Aus all diesen Gründen wird die Serialisierung und Deserialisierung von Anmerkungen durch benutzerdefinierte Objekte gehandhabt, die an den Schnittstellenpunkten der entsprechenden Serialisierungsfunktionen übergeben werden müssen. In QPY zum Beispiel ist das Argument annotation_factories in qpy.dump() und qpy.load() werden zur Übergabe von Serialisierern verwendet.
QPYSerializer
class qiskit.circuit.annotation.QPYSerializer
Basen: ABC
Die Schnittstelle für Serializer und Deserializer von Annotation Objekten für QPY.
Weitere Informationen über QPY finden Sie unter qiskit.qpy.
Diese Schnittstellendefinitionsklasse ist so konzipiert, dass sie unterklassifiziert werden kann. Die einzelnen Methoden beschreiben ihre Verträge und wie sie aufgerufen werden.
Während der QPY-Serialisierung und -Deserialisierung ruft die QPY-Hauptlogik eine Fabrikfunktion auf, um Instanzen von Unterklassen dieser Klasse zu erzeugen. Der Rückgabewert einer gegebenen Fabrikfunktion wird entweder in einem Serialisierungs- oder einem Deserialisierungskontext verwendet, aber nicht in beiden.
Die Struktur der Aufrufe während der Serialisierung eines einzelnen Schaltkreises ist wie folgt:
- viele Aufrufe von
dump_annotation(), die alle dasselbenamespaceArgument gemeinsam haben, das stets ein (nicht striktes) Präfix aller angegebenen ObjekteAnnotationist. - ein Anruf bei
dump_state().
Die allgemeine Struktur der Aufrufe während der Deserialisierung eines einzelnen Schaltkreises aus einer QPY-Nutzlast ist wie folgt:
- ein Aufruf von
load_state()mit Übergabe vonnamespace(mit demselben nicht-strikten Präfix-Verhalten wie bei der "serialisierenden" Form). - viele Aufrufe von
load_annotation(), die den unter diesem Namespace-Prefix Lookup serialisierten Anmerkungen entsprechen.
Wenn Sie eine Unterklasse bilden, denken Sie daran, dass QPY eine strenge Rückwärtskompatibilität garantieren soll, und es wird dringend empfohlen, dass die Unterklassen der Annotation-Serialisierung dies beibehalten. Insbesondere wird vorgeschlagen, dass jeder nicht-triviale Serialisierer "Versions"-Informationen für den Serialisierer in seinem gesamten "Zustand" enthält (siehe dump_state()), und die Deserialisierung sollte alle Anstrengungen unternehmen, um Abwärtskompatibilität mit früheren Versionen desselben Serialisierers zu unterstützen.
QPYFromOpenQASM3Serializer
class qiskit.circuit.annotation.QPYFromOpenQASM3Serializer(inner)
Basen: QPYSerializer
Ein Adapter, der eine OpenQASM3Serializer in eine QPYSerializer.
Dies funktioniert, weil OpenQASM 3 Annotation Serializer zustandslos sein und UTF-8-encoded einzelne Textzeilen zurückgeben müssen, was eine Untermenge dessen ist, was QPY erlaubt.
Normalerweise erstellen Sie einen solchen mit der Methode as_qpy() eines OpenQASM 3 Annotation Serializer.
Beispiele:
Instanzen dieser Klasse können wie eine Funktion mit Null-Argumenten aufgerufen werden und geben sich selbst zurück. Dadurch können Sie sie direkt als Fabrikfunktion für die QPY-Einstiegspunkte verwenden, wie z.B.:
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()})Dies ist sicher, ohne dass separate Instanzen zurückgegeben werden, da die Basis OpenQASM 3 Serialisierer notwendigerweise zustandslos sind.
Parameter
inner (OpenQASM3Serializer) – den OpenQASM 3 Serializer, von dem dieser abgeleitet ist.
OpenQASM3Serializer
class qiskit.circuit.annotation.OpenQASM3Serializer
Basen: ABC
Die Schnittstelle für Serializer und Deserializer von Annotation Objekten gemäß OpenQASM 3.
Weitere Informationen zur Unterstützung von OpenQASM 3 in Qiskit finden Sie unter qiskit.qasm3.
Diese Schnittstellendefinitionsklasse ist so konzipiert, dass sie unterklassifiziert werden kann. OpenQASM 3 Annotationen sind innerhalb eines Programms zustandslos, daher darf eine Unterklasse keinen Zustand verfolgen.
Beispiele
Ein Block-Sammlung-Transpiler-Durchlauf
Ein Hauptziel des Annotations-Frameworks ist es, benutzerdefinierte Analysen und Befehle auf Schaltungen in einer anweisungslokalen Weise zu speichern, entweder durch den Benutzer beim Eintritt in den Compiler oder für einen Compiler-Durchlauf, um Informationen für einen späteren Gebrauch zu speichern.
BoxOpWir können beispielsweise einen einfachen Transpiler-Durchlauf schreiben, der Abfolgen von Einzel-Qubit-Operationen sammelt, jede Abfolge in ein speichert, dann die gesamte unitäre Aktion berechnet und diese als benutzerdefinierte Anmerkung hinzufügt, sodass dieselbe Analyse später nicht wiederholt werden muss, selbst wenn die internen Abläufe der einzelnen Blöcke optimiert werden.
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 dagUm die Anmerkung auf OpenQASM 3 zu serialisieren, müssen wir eine benutzerdefinierte Logik definieren, da die Analyse selbst vollständig benutzerdefiniert ist. Die Serialisierung ist von der Anmerkung getrennt; es kann Umstände geben, unter denen die Serialisierung anders durchgeführt werden sollte.
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))Schließlich kann dies zusammengefügt werden und zeigt das Ergebnis 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];
}