Anotaciones de circuitos
qiskit.circuit.annotation
Este módulo contiene la infraestructura para trabajar con anotaciones de circuitos personalizados.
La principal clase de cara al usuario es la clase base qiskit.circuit.Annotationque también se reexporta desde este módulo.
Subclases de anotaciones personalizadas
La clase Annotation clase está pensada para ser subclasificada. Las subclases deben establecer su campo namespace campo. Puede ser específico para una instancia o estático para toda una subclase. El namespace se utiliza como parte del mecanismo de envío, como se describe en Namespacing.
Las comprobaciones de igualdad de circuitos también comparan las anotaciones de los objetos en función del orden. Es probable que desee implementar el método __eq__() en cualquier subclase.
Si desea que su anotación pueda serializarse a través de QPY o :ref:` OpenQASM 3 <qiskit-qasm3> `, debe proporcionar implementaciones separadas de los métodos de serialización y deserialización, tal y como se explica en Serialización y deserialización.
Espacio de nombres
El "espacio de nombres" de una anotación se utiliza como clave de búsqueda cuando cualquier consumidor está decidiendo qué manejador invocar. Esto incluye en contextos de serialización QPY y OpenQASM 3, pero en general, los pases del transpilador también mirarán los namespaces de las anotaciones para determinar si son relevantes, etc.
Puede tratarse de un identificador estándar de Python (por ejemplo, my_namespace), o una lista de identificadores separados por puntos (p. ej., my_namespace.subnamespace). Todos los usuarios de anotaciones utilizan el espacio de nombres para determinar qué controlador debe invocarse.
Una función independiente permite iterar por los espacios de nombres y los espacios de nombres padre en orden de prioridad de más específico a menos específico.
iter_namespaces
qiskit.circuit.annotation.iter_namespaces(namespace)
Un iterador sobre todos los espacios de nombres que se pueden utilizar para buscar el espacio de nombres dado.
Esto incluye el espacio de nombres y todos los padres, incluido el espacio de nombres raíz de cadena vacía.
Ejemplos:
from qiskit.circuit.annotation import iter_namespaces
assert list(iter_namespaces("hello.world")) == ["hello.world", "hello", ""]Parámetros
namespace (str)
Tipo de retorno
Serialización y deserialización
Las anotaciones representan datos completamente personalizados, que pueden persistir tras la compilación. Esto puede incluir datos que deben ser serializados para su posterior consumo, como datos adicionales que son interpretados por un compilador backend. El formato binario nativo QPY de Qiskit (véase qiskit.qpy) admite el concepto de anotaciones arbitrarias en sus cargas útiles a partir de la versión 15. En OpenQASM 3 (véase qiskit.qasm3), las anotaciones son una característica central del lenguaje, y el soporte de importación/exportación de Qiskit para OpenQASM 3 incluye la serialización de anotaciones.
Sin embargo, dado que las anotaciones son generalmente subclases personalizadas y desconocidas para Qiskit, no podemos tener soporte incorporado para la serialización. En el frente de deserialización, Qiskit no tendrá, en general, un objeto existente Annotation existente desde el que llamar a los métodos de deserialización. También se espera que las anotaciones puedan referirse a algún estado compartido desconocido para Qiskit dentro de un contexto de circuito dado.
Por todas estas razones, la serialización y deserialización de anotaciones es manejada por objetos personalizados, que deben ser pasados en los puntos de interfaz de las funciones de serialización relevantes. Por ejemplo, en QPY, el argumento annotation_factories en qpy.dump() y qpy.load() se utilizan para pasar serializadores.
QPYSerializador
class qiskit.circuit.annotation.QPYSerializer
Bases: ABC
La interfaz para serializadores y deserializadores de Annotation objetos a QPY.
Para más información sobre QPY, véase qiskit.qpy.
Esta clase de definición de interfaz está diseñada para ser subclasificada. Los métodos individuales describen sus contratos y cómo serán llamados.
Durante la serialización y deserialización de QPY, la lógica principal de QPY llamará a una función de fábrica para crear instancias de subclases de esta clase. El valor de retorno de una función de fábrica dada se utilizará en un contexto de serialización o deserialización, pero no en ambos.
La estructura de las llamadas durante la serialización de un circuito único es:
- muchas llamadas a
dump_annotation(), que compartirán todas el mismo argumentonamespace, que siempre será un prefijo (no estricto) de todos losAnnotationobjetos dados. - una llamada a
dump_state().
La estructura general de las llamadas durante la deserialización de un circuito único a partir de una carga útil QPY es la siguiente:
- una llamada a
load_state(), pasando unnamespace(con el mismo comportamiento de prefijación no estricto que la forma de "serialización"). - muchas llamadas a
load_annotation(), correspondientes a anotaciones serializadas bajo esa búsqueda de prefijo de espacio de nombres.
Al subclasificar esto, recuerde que QPY está pensado para tener estrictas garantías de compatibilidad con versiones anteriores, y se recomienda encarecidamente que las subclases de serialización de anotaciones mantengan esto. En particular, se sugiere que cualquier serializador no trivial incluya información de "versión" para el serializador en su "estado" total (véase dump_state()), y la deserialización debe hacer todo lo posible para apoyar la compatibilidad con versiones anteriores del mismo serializador.
QPYFromOpenQASM3Serializer
class qiskit.circuit.annotation.QPYFromOpenQASM3Serializer(inner)
Bases: QPYSerializer
Un adaptador que convierte un OpenQASM3Serializer en un QPYSerializer.
Esto funciona porque los serializadores de anotaciones de OpenQASM 3 deben ser apátridas y devolver UTF-8-encoded líneas de texto individuales, que es un subconjunto de lo que permite QPY.
Normalmente se crea uno de estos utilizando el método as_qpy() de un serializador de anotaciones de OpenQASM 3.
Ejemplos:
Las instancias de esta clase se pueden llamar como una función sin argumentos y se devuelven a sí mismas. Esto le permite utilizarlos directamente como una función de fábrica a los puntos de entrada QPY, tales como:
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()})Esto es seguro, sin devolver instancias separadas, porque la base OpenQASM 3 serializadores son necesariamente sin estado.
Parámetros
inner (OpenQASM3Serializer) – el serializador OpenQASM 3 del que deriva.
OpenQASM3Serializer
class qiskit.circuit.annotation.OpenQASM3Serializer
Bases: ABC
La interfaz para serializadores y deserializadores de Annotation objetos a OpenQASM 3.
Para obtener más información sobre la compatibilidad con OpenQASM 3 en Qiskit, consulte qiskit.qasm3.
Esta clase de definición de interfaz está diseñada para ser subclasificada. OpenQASM 3 las anotaciones no tienen estado dentro de un programa, por lo tanto una subclase no debe rastrear el estado.
Ejemplos
Una pasada del transpilador de colección de bloques
Uno de los principales objetivos del marco de anotación es permitir que los análisis y comandos personalizados se almacenen en los circuitos de una manera local a la instrucción, ya sea por el usuario en la entrada al compilador, o para que una pasada del compilador almacene información para su consumo posterior.
Por ejemplo, podemos escribir un simple pase de transpilador que recoja ejecuciones de operaciones de un solo qubit, y ponga cada ejecución en un BoxOpa continuación, calcula la acción unitaria total y la adjunta como una anotación personalizada, por lo que no es necesario repetir el mismo análisis más tarde, incluso si los internos de cada bloque están optimizados.
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 dagPara serializar la anotación a OpenQASM 3, debemos definir una lógica personalizada, ya que el análisis en sí es totalmente personalizado. La serialización es independiente de la anotación; puede haber circunstancias en las que la serialización deba hacerse de otra manera.
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))Por último, se puede juntar todo esto, mostrando la salida 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];
}