Serialización QPY
qiskit.qpy
QPY es un formato de serialización binaria de objetos QuantumCircuit diseñado para ser multiplataforma, independiente de la versión Python y compatible con versiones anteriores. QPY se debe utilizar si necesitas un mecanismo para guardar o copiar entre sistemas un QuantumCircuit que preserve la estructura completa del objeto Qiskit (excepto los atributos personalizados definidos fuera del código Qiskit). Esto difiere de otros formatos de serialización como OpenQASM ( 2.0 o 3.0 ) que tiene un modelo de abstracción diferente y puede resultar en una pérdida de información contenida en el circuito original (o es incapaz de representar algunos aspectos de los objetos Qiskit) o Python 's pickle que preservará el objeto Qiskit exactamente pero sólo funcionará para una única versión de Qiskit (también es potencialmente inseguro ).
Uso básico
El uso de QPY está definido para que sea sencillo y refleje la API de usuario de los serializadores de la biblioteca estándar de Python, pickle y json. Hay 2 funciones de cara al usuario: qiskit.qpy.dump() y qiskit.qpy.load() que se utilizan para volcar datos QPY a un objeto de archivo y cargar circuitos a partir de datos QPY en un objeto de archivo, respectivamente. Por ejemplo:
from qiskit.circuit import QuantumCircuit
from qiskit import qpy
qc = QuantumCircuit(2, name='Bell', metadata={'test': True})
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
with open('bell.qpy', 'wb') as fd:
qpy.dump(qc, fd)
with open('bell.qpy', 'rb') as fd:
new_qc = qpy.load(fd)[0]La función qiskit.qpy.dump() también permite incluir varios circuitos en un único archivo QPY:
with open('twenty_bells.qpy', 'wb') as fd:
qpy.dump([qc] * 20, fd)y luego cargar ese archivo devolverá una lista con todos los circuitos
with open('twenty_bells.qpy', 'rb') as fd:
twenty_new_bells = qpy.load(fd)Documentación de API
load
qiskit.qpy.load(file_obj, metadata_deserializer=None, annotation_factories=None)
Cargar un archivo binario QPY
Esta función se utiliza para cargar un archivo de programa QPY Qiskit serializado y crear QuantumCircuit objetos a partir de su contenido. Por ejemplo:
from qiskit import qpy
with open('bell.qpy', 'rb') as fd:
circuits = qpy.load(fd)o con un archivo comprimido con gzip:
import gzip
from qiskit import qpy
with gzip.open('bell.qpy.gz', 'rb') as fd:
circuits = qpy.load(fd)que leerá el contenido del qpy y devolverá una lista de QuantumCircuit objetos del archivo.
Parámetros
- file_obj (BinaryIO) – Objeto similar a un fichero que contiene los datos binarios QPY de un circuito.
- metadata_deserializer (type[JSONDecoder] | None) – Una clase JSONDecoder opcional que se utilizará para el parámetro
cls«kwarg» en la llamada internajson.loadempleada para deserializar la carga útil JSON utilizada para el.metadataatributo de cualquier programa del archivo QPY. Si no se especifica esto, los metadatos del circuito se analizarán como JSON mediante la función de la biblioteca estándarjson.load(), utilizando la clase predeterminadaJSONDecoder. - annotation_factories (Mapping[str, Callable[[], annotation.QPYSerializer]] | None) – Asignación de espacios de nombres a funciones que crean nuevas instancias de
annotation.QPUSerializer, para gestionar la carga deAnnotationobjetos personalizados.
Devuelve
La lista de programas Qiskit contenidos en los datos QPY. Siempre se devuelve una lista, aunque sólo haya 1 programa en los datos QPY.
Eleva
- QiskitError - si
file_objno es un archivo QPY válido - TypeError : cuando se carga un tipo de datos no válido.
- MissingOptionalLibraryError – Si la
symenginebiblioteca «engine» no está instalada al cargar una carga útil de QPY de las versiones 10, 11 o 12 que utilice la codificación simbólica «symengine» y contengaParameterExpressioninstancias. - QpyError - si se carga un tipo de datos conocido pero no admitido.
Tipo de retorno
lista[QPY_SUPPORTED_TYPES]
dump
qiskit.qpy.dump(programs, file_obj, metadata_serializer=None, use_symengine=False, version=18, annotation_factories=None)
Escribir datos binarios QPY en un archivo
Esta función permite guardar un circuito en un archivo para utilizarlo posteriormente o transferirlo entre máquinas. El formato QPY es compatible con versiones anteriores y puede cargarse con futuras versiones de Qiskit.
Por ejemplo:
from qiskit.circuit import QuantumCircuit
from qiskit import qpy
qc = QuantumCircuit(2, name='Bell', metadata={'test': True})
qc.h(0)
qc.cx(0, 1)
qc.measure_all()a partir de esto puedes escribir los datos de qpy en un archivo:
with open('bell.qpy', 'wb') as fd:
qpy.dump(qc, fd)o un archivo comprimido con gzip:
import gzip
with gzip.open('bell.qpy.gz', 'wb') as fd:
qpy.dump(qc, fd)Que guardará el circuito serializado qpy en el archivo proporcionado.
Parámetros
-
programs (list[QPY_SUPPORTED_TYPES] | QPY_SUPPORTED_TYPES) – Objetos compatibles con QPY que se van a almacenar en el archivo especificado como objeto. QPY es compatible con
QuantumCircuit. -
file_obj (BinaryIO) – El archivo como objeto para escribir los datos QPY también
-
metadata_serializer (type[JSONEncoder] | None) – Una clase JSONEncoder opcional a la que se le pasará el
.metadataatributo de cada programa enprogramsy que se utilizará como argumentoclsclave (kwarg) en la llamada ajson.dump ()para serializar ese diccionario a JSON. -
use_symengine (bool) – Esta bandera ya no se utiliza en las versiones de QPY compatibles con esta función y no tendrá ningún impacto en la carga útil QPY generada, salvo para establecer un campo en el encabezado de un archivo QPY v13 que no se utiliza.
-
version (int) –
La versión del formato QPY a emitir. Por defecto, se utiliza el último formato soportado de
QPY_VERSIONsin embargo, por razones de compatibilidad, si necesita cargar la carga útil QPY generada con una versión anterior de Qiskit, también puede seleccionar una versión de formato QPY anterior hasta la versión mínima de exportación soportada, que sólo puede cambiar durante un lanzamiento de versión principal de Qiskit, para generar una versión de formato QPY anterior. Puede acceder a la versión actual de QPY y a la versión mínima compatible conqpy.QPY_VERSIONyqpy.QPY_COMPATIBILITY_VERSIONrespectivamente.NotaSi se especifica con una versión anterior de QPY, persistirán las limitaciones y los posibles errores derivados del formato QPY en esa versión. Esto sólo debe utilizarse si es necesaria la compatibilidad con la carga de la carga útil con una versión anterior de Qiskit.
-
annotation_factories (Mapping[str, Callable[[], annotation.QPYSerializer]] | None) – Asignación de espacios de nombres a funciones que crean nuevas instancias de
annotation.QPUSerializer, para gestionar el volcado de objetosAnnotationpersonalizados. La llamada posterior aload()deberá utilizar objetos serializadores similares, que reconozcan el formato de salida personalizado de dichos serializadores.
Eleva
- TypeError : cuando se introduce un tipo de datos no válido.
- ValueError : cuando se pasa un número de versión no admitido como argumento
version.
get_qpy_version
qiskit.qpy.get_qpy_version(file_obj)
Esta función identifica la versión QPY del fichero.
Esta función leerá la cabecera de file_obj y devolverá la versión en formato QPY. No avanzará el cursor de file_obj. Si está utilizando esto para una lectura posterior, como para llamar a load()puede pasar directamente file_obj . Por ejemplo:
from qiskit import qpy
qpy_version = qpy.get_qpy_version(qpy_file)
if qpy_version > 12:
qpy.load(qpy_file)Parámetros
file_obj (BinaryIO) – Objeto similar a un fichero que contiene los datos binarios QPY de un circuito.
Devuelve
La versión QPY del archivo especificado.
Tipo de retorno
Estas funciones lanzarán una subclase personalizada de QiskitError si encuentran problemas durante la serialización o deserialización.
QpyError
exception qiskit.qpy.QpyError(*message)
Bases: QiskitError
Errores generados por el módulo qpy.
Configura el mensaje de error.
Cuando se establece una versión QPY de destino inferior a la máxima para la serialización, pero el objeto que se va a serializar contiene características que no se pueden representar en ese formato, se produce una subclase de QpyError :
UnsupportedFeatureForVersion
exception qiskit.qpy.UnsupportedFeatureForVersion(feature, required, target)
Bases: QpyError
Error QPY que se produce cuando la versión de volcado de destino es demasiado baja para una característica presente en el objeto que se va a serializar.
Parámetros
qiskit.qpy.QPY_VERSION
La versión actual del formato QPY a partir de esta versión. Este es el valor por defecto del argumento de la palabra clave version en qpy.dump() y también el límite superior de los valores aceptados para el mismo argumento. Este es también el límite superior de las versiones compatibles con qpy.load().
Type
qiskit.qpy.QPY_COMPATIBILITY_VERSION
La versión del formato QPY de compatibilidad mínima actual. Esta es la versión mínima que qpy.dump() aceptará para el argumento de la palabra clave version . qpy.load() podrá cargar todas las versiones de formato de QPY publicadas (hasta QPY_VERSION).
Type
Compatibilidad con QPY
El formato QPY está diseñado para ser compatible con versiones anteriores en el futuro. Esto significa que deberías poder cargar un QPY con cualquier versión de Qiskit más reciente que la que lo generó. Sin embargo, cargar un archivo QPY con una versión anterior de Qiskit no es compatible y puede no funcionar.
Por ejemplo, si generaste un archivo QPY utilizando qiskit-terra 0.18.1 podrías cargar ese archivo QPY con qiskit-terra 0.19.0 y un hipotético qiskit-terra 0.29.0. Sin embargo, cargar ese archivo QPY con 0.18.0 no es compatible y puede no funcionar.
Ten en cuenta que los metadatos de los circuitos y los objetos personalizados Annotation se serializan y deserializan mediante clases proporcionadas por el usuario, ya que los propios objetos son totalmente personalizables por el usuario, por lo que su compatibilidad con versiones anteriores y futuras queda limitada por lo que el usuario proporcione.
Si una función que se está cargando está obsoleta en la versión correspondiente de qiskit, QPY lanzará un mensaje QPYLoadingDeprecatedFeatureWarning informando del periodo de depreciación y de cómo se gestionará internamente la función.
QPYLoadingDeprecatedFeatureWarning
exception qiskit.qpy.QPYLoadingDeprecatedFeatureWarning
Bases: QiskitWarning
Advertencia visible de desaprobación para las funciones de carga QPY sin un punto estable en la pila de llamadas.
En las versiones de Qiskit anteriores a 1.2.4, el use_symengine=True argumento de qpy.dump() podía provocar problemas de compatibilidad con versiones anteriores si había ParameterExpression objetos que serializar. En concreto:
- Cuando la versión de carga de Qiskit es 1.2.4 o superior, se pueden cargar los archivos QPY generados con cualquier versión de Qiskit >= 0.46.0. Si se utilizó una versión de Qiskit entre 0.45.0 y 0.45.3 para generar los archivos, y se dio el argumento no predeterminado
use_symengine=Trueaqpy.dump(), el archivo sólo puede leerse si la versión desymengineutilizada en el entorno generador era de la serie 0.11 o 0.13, pero si el entorno se creó durante la ventana de soporte de Qiskit 0.45, es probable que se utilizarasymengine==0.9.2. - Cuando la versión de carga de Qiskit está comprendida entre 0.46.0 y 1.2.2, ambas inclusive, el archivo sólo puede leerse si la versión instalada de
symengineen el entorno de carga coincide con la versión utilizada en el entorno de generación.
Para recuperar un archivo QPY que falla con errores relacionados con la versión symengine durante una llamada a qpy.load()primero intente utilizar Qiskit >= 1.2.4 para cargar el archivo. Si esto sigue fallando, es probable que se deba a que se utilizó Qiskit 0.45.x para generar el archivo con use_symengine=True. En este caso, utilice Qiskit 0.45.3 con symengine==0.9.2 para cargar el archivo y, a continuación, vuelva a exportarlo a QPY configurando use_symengine=False. El archivo resultante puede ser cargado por cualquier versión posterior de Qiskit.
A partir de la versión de Qiskit 2.0.0, que eliminó el módulo Pulse de la biblioteca, QPY proporciona un soporte limitado para cargar cargas útiles que incluyan datos de pulsos. Cargando una carga útil ScheduleBlock , se producirá una QpyError se producirá una excepción. Al cargar una carga útil para un circuito que contenía puertas de impulsos, el circuito de salida contendrá instrucciones personalizadas sin datos de calibración adjuntos para cada puerta de impulsos, dejándolas indefinidas.
Historial de versiones del formato QPY
Si estás planeando cargar un archivo QPY entre diferentes versiones de Qiskit, es útil saber qué versiones estaban disponibles en una determinada versión. Dado que QPY es compatible con versiones anteriores pero no con versiones posteriores, debe asegurarse de que la versión de un formato QPY determinado se publicó en la versión con la que está llamando load() con. La siguiente tabla enumera las versiones de QPY compatibles con todas las versiones de Qiskit (y qiskit-terra antes de Qiskit 1.0.0 ) desde la introducción de QPY en qiskit-terra 0.18.0.
Versión de Qiskit (qiskit-terra para < 1.0.0 ) | dump() formato(s) versiones de salida | load() versión máxima admitida (siempre se pueden leer versiones de formatos anteriores) |
|---|---|---|
| 2.6.1 | 13, 14, 15, 16, 17, 18 | 18 |
| 2.6.0 | 13, 14, 15, 16, 17, 18 | 18 |
| 2.5.2 | 13, 14, 15, 16, 17 | 17 |
| 2.5.1 | 13, 14, 15, 16, 17 | 17 |
| 2.5.0 | 13, 14, 15, 16, 17 | 17 |
| 2.4.1 | 13, 14, 15, 16, 17 | 17 |
| 2.4.0 | 13, 14, 15, 16, 17 | 17 |
| 2.3.1 | 13, 14, 15, 16, 17 | 17 |
| 2.3.0 | 13, 14, 15, 16, 17 | 17 |
| 2.2.2 | 13, 14, 15, 16 | 16 |
| 2.2.1 | 13, 14, 15, 16 | 16 |
| 2.2.0 | 13, 14, 15, 16 | 16 |
| 2.1.2 | 13, 14, 15, 16 | 16 |
| 2.1.1 | 13, 14, 15, 16 | 16 |
| 2.1.0 | 13, 14, 15 | 19 |
| 2.0.2 | 13, 14 | 14 |
| 2.0.1 | 13, 14 | 14 |
| 2.0.0 | 13, 14 | 14 |
| 1.4.3 | 10, 11, 12, 13 | 13 |
| 1.4.2 | 10, 11, 12, 13 | 13 |
| 1.4.1 | 10, 11, 12, 13 | 13 |
| 1.4.0 | 10, 11, 12, 13 | 13 |
| 1.3.3 | 10, 11, 12, 13 | 13 |
| 1.3.2 | 10, 11, 12, 13 | 13 |
| 1.3.1 | 10, 11, 12, 13 | 13 |
| 1.3.0 | 10, 11, 12, 13 | 13 |
| 1.2.4 | 10, 11, 12 | 6 |
| 1.2.3 (tirón) | 10, 11, 12 | 6 |
| 1.2.2 | 10, 11, 12 | 6 |
| 1.2.1 | 10, 11, 12 | 6 |
| 1.2.0 | 10, 11, 12 | 6 |
| 1.1.0 | 10, 11, 12 | 6 |
| 1.0.2 | 10 y 11 | 5 |
| 1.0.1 | 10 y 11 | 5 |
| 1.0.0 | 10 y 11 | 5 |
| 0.46.1 | 10 | 10 |
| 0.45.3 | 10 | 10 |
| 0.45.2 | 10 | 10 |
| 0.45.1 | 10 | 10 |
| 0.45.0 | 10 | 10 |
| 0.25.3 | 9 | 9 |
| 0.25.2 | 9 | 9 |
| 0.25.1 | 9 | 9 |
| 0.24.2 | 8 | 8 |
| 0.24.1 | 7 | 7 |
| 0.24.0 | 7 | 7 |
| 0.23.3 | 6 | 6 |
| 0.23.2 | 6 | 6 |
| 0.23.1 | 6 | 6 |
| 0.23.0 | 6 | 6 |
| 0.22.4 | 5 | 5 |
| 0.22.3 | 5 | 5 |
| 0.22.2 | 5 | 5 |
| 0.22.1 | 5 | 5 |
| 0.22.0 | 5 | 5 |
| 0.21.2 | 5 | 5 |
| 0.21.1 | 5 | 5 |
| 0.21.0 | 5 | 5 |
| 0.20.2 | 4 | 4 |
| 0.20.1 | 4 | 4 |
| 0.20.0 | 4 | 4 |
| 0.19.2 | 4 | 4 |
| 0.19.1 | 3 | 3 |
| 0.19.0 | 2 | 2 |
| 0.18.3 | 1 | 1 |
| 0.18.2 | 1 | 1 |
| 0.18.1 | 1 | 1 |
| 0.18.0 | 1 | 1 |
Formato QPY
El formato de serialización QPY es un formato de serialización binario multiplataforma portable para QuantumCircuit objetos en Qiskit. El formato básico de los ficheros es el siguiente:
Un archivo QPY (u objeto de memoria) comienza siempre con la siguiente cadena de 6 bytes UTF8 : QISKIT a la que sigue inmediatamente la cabecera general del archivo. El contenido de la cabecera del archivo definido como una estructura C es:
struct {
uint8_t qpy_version;
uint8_t qiskit_major_version;
uint8_t qiskit_minor_version;
uint8_t qiskit_patch_version;
uint64_t num_circuits;
}A partir de V10, se añade un nuevo campo a la estructura de cabecera del archivo para representar el esquema de codificación utilizado para las expresiones simbólicas:
struct {
uint8_t qpy_version;
uint8_t qiskit_major_version;
uint8_t qiskit_minor_version;
uint8_t qiskit_patch_version;
uint64_t num_circuits;
char symbolic_encoding;
}A partir de V16, la estructura de cabecera del archivo va seguida inmediatamente de una tabla de inicio de circuito que contiene los desplazamientos de byte de cada carga útil de circuito en el archivo. En la tabla de inicio de circuito hay num_circuits entradas, cada una de las cuales es del tipo uint64_t. En todas las versiones anteriores, la cabecera del archivo va seguida inmediatamente por las cargas útiles del circuito en secuencia, sin ningún relleno intermedio.
Todos los valores utilizan el orden de bytes de red [1] (big-endian) para garantizar la compatibilidad entre plataformas. La excepción a esto se da en las versiones del formato QPY <= 17, en las que la codificación de los números enteros y los decimales es INSTRUCTION_PARAM de tipo «little endian».
Cada circuito individual se compone de las siguientes partes ordenadas de arriba a abajo:
HEADER
METADATA
REGISTERS
ANNOTATION_HEADER
STANDALONE_VARS
CUSTOM_DEFINITIONS
INSTRUCTIONSCambiado en la versión QPY: 15 Se ha añadido ANNOTATION_HEADER entre REGISTERS y STANDALONE_VARS.
Cambiado en la versión QPY: 12 Se ha añadido STANDALONE_VARS entre REGISTERS y CUSTOM_DEFINITIONS.
Hay una carga útil de circuito para cada circuito (donde el número total viene dictado por num_circuits en la cabecera del archivo). No hay relleno entre los circuitos de los datos.
Versión 18
La versión 18 elimina el campo CalibrationsPack de la carga útil del circuito. Las calibraciones de la puerta de impulsos se eliminaron de Qiskit en la versión 2.0 y, desde entonces, ese campo siempre se ha escrito como un marcador de posición vacío (num_cals = 0). Su eliminación ahorra 2 bytes por circuito y simplifica el formato.
La versión 18 también corrige la codificación de los valores INSTRUCTION_PARAM enteros y de tipo flotante al orden de bytes «big-endian», en consonancia con el resto de la especificación de QPY. En las versiones 1 a 17, se escribieron por error en formato «little-endian».
PARAMETER_VECTOR_TABLE
La versión 18 almacena cada elemento ParameterVector una sola vez por carga útil de circuito y sus elementos hacen referencia a él mediante un índice, en lugar de repetir la identidad del vector en cada elemento.
La carga útil del circuito se añade inmediatamente PARAMETER_VECTOR_TABLE después de los encabezados de anotación, antes de las definiciones de instrucciones personalizadas:
struct {
uint16_t num_vectors;
}a las que siguen las entradas num_vectors de
struct {
uint16_t vector_name_size;
uint64_t vector_size;
char uuid[16]; // the vector's root UUID
}cada uno de ellos seguido inmediatamente de bytes vector_name_size en utf8 del nombre del vector.
Un PARAMETER_VECTOR_ELEMENT se reduce, en consecuencia, a una referencia:
struct {
uint16_t vector_index; // index into PARAMETER_VECTOR_TABLE
uint64_t index; // index of this element within that vector
}Ya no se almacena el UUID propio del elemento, ya que se trata del UUID raíz del vector más el índice del elemento —una relación que el lector siempre ha utilizado para recuperar el vector propietario—. Por lo tanto, un elemento ocupa 10 bytes en lugar de 34 más la longitud del nombre del vector, a costa de dos bytes por circuito para el recuento cuando un circuito no utiliza ningún vector de parámetros.
Una carga útil anidada —un bloque de flujo de control o una definición de instrucción personalizada— lleva su propia tabla, de modo que cada circuito sigue siendo decodificable por sí solo.
Nuevo « ParamRegisterPack »
La versión 18 sustituye la codificación de la carga útil de un «Register», que almacena un valor entero o ClassicalRegister un solo Clbit. Aparece como condición de una instrucción (véase INSTRUCCIONES ) y como un INSTRUCTION_PARAM de tipo 'R'.
Hasta la versión 17, ambos casos compartían una cadena « utf8 » sin tipo: un registro era simplemente su nombre, mientras que un «clbit» era un carácter nulo "\x00" seguido del índice del bit en el circuito*, escrito en dígitos decimales*. A partir de la versión 18, la carga útil comienza con un byte de etiqueta que identifica cuál de las dos es:
struct { // classical register, tag == 1
uint8_t kind;
char name[]; // to the end of the payload
}
struct { // single clbit, tag == 0
uint8_t kind;
uint32_t index; // index of the bit in the circuit
}El nombre del registro no necesita una longitud propia, ya que el campo que lo enmarca ya delimita la carga útil: conditional_reg_name_size para una condición, y el INSTRUCTION_PARAM encabezado size para un parámetro.
La versión 18 también restringe los elementos de tipo «bit-term» de la carga útil SPARSE_OBSERVABLE de «!«H» (uint16_t) a «!«B» (uint8_t).
Los valores de siempre SparseObservable.BitTerm caben en un solo byte, por lo que el tipo más amplio almacenaba un byte de relleno por cada término de un bit. Por lo tanto, una carga útil de la versión 18 es un byte más pequeña por término de bit que la carga útil equivalente de la versión 17. Ningún otro campo de SPARSE_OBSERVABLE cambia, y el significado de bitterm_data_len no se ve afectado, ya que cuenta elementos en lugar de bytes.
Modificaciones en los REGISTROS
La representación de los registros definidos en REGISTERS y actualizados en la versión 4 ) en QPY ha cambiado en la versión 18. El primer cambio es el tipo de la matriz de asignación de índices de registro, que pasa de int64_t a uint32_t (que es lo que era antes de QPY v4 ). El cambio original int64_t se realizó para permitir el uso de -1 como valor centinela para un bit que no forma parte del circuito. En realidad, esto no es necesario, ya que podemos utilizar el valor máximo de a ( uint32_t 4294967295) como valor centinela. En la versión 18, los valores de 4294967295 deben tratarse como si fueran -1, tal y como ocurría en versiones anteriores de QPY, y el bit en esa posición de la matriz no forma parte del circuito.
La versión 18 también añade 'a' como valor para el type campo para representar un AncillaRegister. Anteriormente, los registros de ancilla se codificaban como 'q' y, por lo tanto, se deserializaban como objetos QuantumRegister normales. Los valores 'c' y 'q' actuales siguen representando los registros cuánticos y clásicos, respectivamente.
El formato de la cabecera REGISTERS también se ha actualizado para
struct {
char type;
_Bool standalone;
uint32_t size;
uint16_t name_size;
_bool in_circuit;
char register_attachment;
}Con la incorporación del byte adicional al final de la estructura para el campo register_attachment . Si este valor es 1, indica que se trata de un registro contiguo conectado a un circuito. De este modo, el array que sigue al nombre con los índices de bits tendrá una longitud de 1 y contendrá el índice inicial del registro. Los índices corresponden, por tanto, al intervalo de longitud size a partir de ese índice inicial. Por ejemplo, si el índice inicial es 5 y el size es 10, los índices son 5, 6, 7, 8, 9, 10, 11, 12, 13, 14.
Modificaciones en los nombres de CUSTOM_INSTRUCTION
Desde la versión 11 hasta la 17, los nombres de los bloques CUSTOM_INSTRUCTION llevaban como sufijo una cadena hexadecimal UUID aleatoria (p. ej., "my_gate_b3ecab5b4d6a4eb6bc2b2dbf18d83e1e"), tal y como se describe en la versión 11. Dado que se generaba un UUID para cada llamada dump() , los volcados repetidos del mismo circuito producían secuencias de bytes diferentes; la salida de QPY no era determinista.
A partir de la versión 18, el sufijo UUID se sustituye por un contador que se reinicia al inicio de cada llamada dump() (p. ej., "my_gate_0").
Versión 17
La versión 17 incorpora compatibilidad con la serialización y deserialización PauliEvolutionGate de elementos que contengan SparseObservable operadores «as».
Cambios en PAULI_EVOLUTION
El formato de PAULI_EVOLUTION en sí mismo no ha cambiado, pero sí se ha actualizado el formato de los operadores que siguen inmediatamente a la puerta de evolución compactada. En lugar de los elementos operator_count definidos según el formato SPARSE_PAULI_OP_LIST_ELEM, la carga útil especifica ahora el tipo de cada operador para tener en cuenta tanto los operadores de tipo SparsePauliOp como los de tipo SparseObservable.
La nueva carga útil que sigue a PAULI_EVOLUTION contiene ahora exactamente operator_count secuencias de un bool ("!?") seguidas del operador. Si el valor booleano es True, el operador es un SparseObservable y se interpreta con el nuevo formato SPARSE_OBSERVABLE (véase más abajo). FalseSi es así, el operador es un SparsePauliOp y se interpreta según el formato SPARSE_PAULI_OP_LIST_ELEM existente.
Nuevo SPARSE_OBSERVABLE
El formato SPARSE_OBSERVABLE representa una instancia de un SparseObservable, definida por
struct {
uint32_t num_qubits;
uint64_t coeff_data_len;
uint64_t bitterm_data_len;
uint64_t inds_data_len;
uint64_t bounds_data_len;
}a lo que le sigue inmediatamente el número de qubits y, a continuación, las matrices de datos de los coeficientes, los términos binarios, los índices y los límites del observable. Cada uno de los cuatro campos *_len indica el número de elementos del array correspondiente, no el número de bytes que ocupa; hay que multiplicarlo por el tamaño del tipo de elemento para obtener la longitud en bytes.
- Cada coeficiente se almacena como dos elementos «!d» consecutivos: primero la parte real y luego la parte imaginaria, por lo que el número de coeficientes
coeff_data_lense duplica.- Los elementos del término de bits son de tipo «!«H» y representan el valor « u8 » del
SparseObservable.BitTerm. A partir de la versión 18, estos se almacenan como «!«B» en su lugar.- Los elementos del índice son de tipo «!I».
- Los elementos de los límites son de tipo «!Q».
Versión 16
La versión 16 añade una tabla de inicio de circuito al formato de archivo QPY. Sirve como índice de las compensaciones de bytes de cada carga útil del circuito en el archivo. La motivación de este cambio es permitir una carga más eficiente de los circuitos utilizando multi-threading en una futura implementación Rust del deserializador QPY.
Cambios en la DURACIÓN
Se ha añadido una nueva variante a la codificación de tipo DURATION existente para picosegundos. Se codifica del siguiente modo, y se añade a las variantes admitidas anteriormente.
Clase Qiskit | Código de tipo | Carga útil |
|---|---|---|
ps | p | Un double value. |
Versión 15
La versión 15 añade el concepto de anotaciones personalizadas al formato de la carga útil. El propio QPY no especifica cómo se serializan o deserializan las anotaciones, ya que son objetos de usuario personalizados. Sin embargo, el formato coopera con los subserializadores.
La versión 15 añade el campo ANNOTATION_HEADER entre los campos STANDALONE_VARS y CUSTOM_DEFINITIONS en el nivel superior de una carga útil de circuito único. Modifica la interpretación de un campo de la estructura INSTRUCTION de forma compatible con ABI, y añade un trailer INSTRUCTION_ANNOTATIONS a INSTRUCTION que está presente condicionado a un bit establecido en la carga útil INSTRUCTION .
Nueva ANOTACIÓN_ENCABEZADO
El campo ANNOTATION_HEADER es una carga útil de tamaño variable en la cabecera. Comienza con una instancia de ANNOTATION_HEADER_STATIC, que es la estructura C:
struct ANNOTATION_HEADER_STATIC {
uint32_t num_namespaces;
}Esto es seguido inmediatamente por num_namespaces instancias de la carga útil ANNOTATION_STATE . El orden de éstos es importante y debe conservarse durante el proceso de deserialización, ya que las siguientes cargas útiles de INSTRUCTION_ANNOTATION se indexarán en él.
La carga útil de ANNOTATION_STATE comienza con la estructura C fija:
struct ANNOTATION_STATE_HEADER {
uint32_t namespace_size;
uint64_t state_size;
}Esta cabecera va seguida inmediatamente de namespace_size bytes de texto codificado en UTF-8, que comprenden el espacio de nombres. Esos bytes van seguidos inmediatamente por state_size bytes de datos arbitrarios. El formato de esta carga útil de "estado" no está definido por QPY. En cambio, es responsabilidad de un objeto externo asociado al espacio de nombres almacenado. El formato no dicta cómo producir estos objetos; como las anotaciones son totalmente personalizadas, el usuario debe suministrar los métodos de serialización y deserialización.
Cambios en la INSTRUCCIÓN
La estructura INSTRUCTION se modifica de forma compatible con ABI a su definición anterior en la versión 9. La nueva estructura es la estructura C (recuerde que no hay relleno entre los campos, ni al final de la estructura):
struct INSTRUCTION {
uint16_t name_size;
uint16_t label_size;
uint16_t num_parameters;
uint32_t num_qargs;
uint32_t num_cargs;
uint8_t extras_key;
uint16_t conditional_reg_name_size;
int64_t conditional_value;
uint32_t num_ctrl_qubits;
uint32_t ctrl_state;
}donde el campo uint8_t extras_key sustituye al anterior uint8_t conditional_key. La diferencia es puramente interpretativa. Los dos bits inferiores del byte se siguen interpretando como la definición de la condición y su tipo. El bit alto del byte es ahora una bandera, que indica si un campo INSTRUCTION_ANNOTATIONS_HEADER está presente (si el bit está activado) en los datos finales de la estructura INSTRUCTION .
En el flujo de datos aparece una carga útil de instrucciones completa, que incluye los objetos finales y no contiene bytes de relleno entre los elementos, tal y como se muestra a continuación:
struct INSTRUCTION;
uint8_t name[name_size];
uint8_t label[label_size];
uint8_t register[conditional_reg_name_size]; (1)
struct INSTRUCTION_PARAM; (2)
struct INSTRUCTION_ARG[num_qargs];
struct INSTRUCTION_ARG[num_cargs];
struct INSTRUCTION_PARAM[num_parameters];
INSTRUCTION_ANNOTATIONS; (3)Se aplican las siguientes notas:
- si los dos bits bajos del
extras_keytienen el valor2, indicando que la condición es unEXPRESSION, elconditional_reg_name_sizees siempre cero. - este campo está presente si y sólo si los dos bits bajos del
extras_keytienen el valor2, lo que indica que la condición es unEXPRESSION. - este campo está presente si y sólo si el bit alto de
extras_keyestá activado. Este campo tiene un tamaño variable; véase Nuevo INSTRUCTION_ANNOTATIONS.
Nuevas INSTRUCCIONES_ANOTACIONES
La carga útil de INSTRUCTION_ANNOTATIONS comienza con la estructura C:
struct INSTRUCTION_ANNOTATIONS_HEADER {
uint32_t num_annotations;
}A esta carga útil le siguen inmediatamente num_annotations instancias de la carga útil INSTRUCTION_ANNOTATION , de tamaño variable.
La carga útil de INSRTUCTION_ANNOTATION está definida por la siguiente estructura en C más un número de bytes igual a payload_size, denominado ANNOTATION_PAYLOAD.
struct INSTRUCTION_ANNOTATION {
uint32_t namespace_index;
uint32_t payload_size;
}El namespace_index es un índice entero en la lista de objetos definidos ANNOTATION_NAMESPACE en el ANNOTATION_HEADER. El espacio de nombres de serialización para una anotación es la cadena codificada UTF-8 en la carga útil correspondiente.
El formato del objeto ANNOTATION_PAYLOAD no está especificado por QPY. Está definido por un objeto de serialización externo asociado con el espacio de nombres al que hace referencia el namespace_index y su estado de serializador asociado en el ANNOTATION_HEADER.
Cambios dentro de PARAM_EXPR_ELEM_V13
La estructura en sí no ha cambiado. Sin embargo, en el caso de un PARAM_EXPR_ELEM_V13 que representa una ParameterExpression.subs() llamada (con op_code = 15, y por lo tanto lhs_type = 'p' y rhs_type = 'n'), el MAPPING final ahora asigna las claves de los bytes sin procesar de los Parameter UUID a los valores sustituidos. Anteriormente (en las versiones 13 y 14 de QPY), esta asignación almacenaba los nombres de los parámetros como claves.
Versión 14
La versión 14 añade un nuevo tipo básico DURATION, soporte para clases adicionales Type clases Float y Durationy un nuevo tipo de nodo de expresión Stretch.
DURACIÓN
Un Duration está codificado por un ASCII de un solo byte char que codifica el tipo de tipo, seguido de una carga útil que varía en función del tipo. Los códigos definidos son:
Clase Qiskit | Código de tipo | Carga útil |
|---|---|---|
dt | t | Un unsigned long long value. |
ns | n | Un double value. |
us | u | Un double value. |
ms | m | Un double value. |
s | s | Un double value. |
Cambios en EXPR_VAR_DECLARATION
El tipo EXPR_VAR_DECLARATION se utiliza ahora para representar tanto Var variables independientes como Stretch identificadores. Para apoyar este cambio, el código de tipo de uso tiene dos nuevas entradas posibles, además de las ya existentes:
Código de tipo | Significado |
|---|---|
A | Un tramo de capture hasta el circuito. |
O | Un tramo del circuito declarado local. |
Cambios en EXPRESSION
El código de tipo EXPRESIÓN tiene una nueva entrada posible, s, correspondiente a expr.Stretch nodos.
Clase Qiskit | Código de tipo | Carga útil | Hijos |
|---|---|---|---|
Stretch | s | Una unsigned short var_index | 0 |
Cambios en EXPR_TYPE
La siguiente tabla muestra las nuevas clases de tipos añadidas en la versión:
Cambios en EXPR_VALUE
El sistema de tipos de la expresión clásica admite ahora nuevos tipos de codificación para los literales de valor, además de las codificaciones existentes para int y bool. A continuación se indican las nuevas codificaciones de los tipos de valores:
Python tipo | Código de tipo | Carga útil |
|---|---|---|
float | f | Un double value. |
Duration | t | Un DURATION. |
Versión 13
En la versión 13 se añadió una representación de serialización nativa de Qiskit para ParameterExpression. Las versiones anteriores de QPY utilizaban o bien sympy o bien symengine para serializar la expresión simbólica subyacente. A partir de la versión 13, QPY representa ahora la secuencia de llamadas a la API utilizadas para crear el ParameterExpression.
El principal cambio en el formato de serialización se encuentra en la carga útil PARAMETER_EXPR. Los expr_size bytes que siguen al encabezado contienen ahora una matriz de PARAM_EXPR_ELEM_V13 estructuras. La idea es que este array se lea una estructura cada vez, de modo que cada estructura describa una de las llamadas que hay que realizar para reconstruir el ParameterExpression.
PARAM_EXPR_ELEM_V13
El formato struct se define como:
struct {
unsigned char op_code;
char lhs_type;
char lhs[16];
char rhs_type;
char rhs[16];
} PARAM_EXPR_ELEM_V13;El op_code campo se utiliza para definir la operación añadida al ParameterExpression. El valor puede ser:
op_code | ParameterExpression método |
|---|---|
| 0 | __add__() |
| 1 | __sub__() |
| 2 | __mul__() |
| 3 | __truediv__() |
| 4 | __pow__() |
| 5 | sin() |
| 6 | cos() |
| 7 | tan() |
| 8 | arcsin() |
| 9 | arccos() |
| 10 | exp() |
| 5 | log() |
| 6 | sign() |
| 13 | gradient() |
| 14 | conjugate() |
| 19 | subs() |
| 16 | abs() |
| 17 | arctan() |
| 255 | Nulo |
El valor NULL de 255 sólo se utiliza para rellenar el campo de código op para entradas que no son operaciones reales sino que indican definiciones recursivas. A continuación, los campos lhs_type y rhs_type se utilizan para describir los tipos de operandos y pueden ser uno de los siguientes caracteres codificados UTF-8 :
Valor | Tipo |
|---|---|
n | None |
p | Parameter |
f | float |
c | complex |
i | int |
s | Inicio de la definición recursiva ParameterExpression |
e | Fin de la definición recursiva ParameterExpression |
u | sustitución |
Si el valor del tipo es f, c, o i, los anchos de campo correspondientes lhs o rhs son de 128 bits cada uno. En el caso de los números de tipo «float», el valor literal se codifica como un número de tipo «double» con relleno de ceros, mientras que los números complejos se codifican como parte real seguida de la parte imaginaria, ocupando 64 bits cada una. iEn este caso, el valor se codifica como un entero con signo de 64 bits, rellenado con ceros hasta completar los 128 bits de ancho total. n Se utiliza para representar un None y, por lo general, no se emplea directamente, ya que indica un argumento que no se utiliza. Los datos corresponden al UUID de p , Parameter que puede consultarse en el mapa de símbolos descrito en la map_elements carga útil exterior PARAMETER_EXPR. ParameterExpressionSi el valor del tipo es s , esto marca el inicio de una nueva sección recursiva para un elemento anidado. Por ejemplo, en el siguiente fragmento de código hay un interior expr contenido en final_expr, lo que constituye una expresión anidada:
from qiskit.circuit import Parameter
x = Parameter("x")
y = Parameter("y")
z = Parameter("z")
expr = (x + y) / 2
final_expr = z**2 + exprCuando s aparece, esto indica que, hasta que se e` struct is reached, the next structs are used for a recursive definition. For both ``s encuentren los tipos y e , los valores de los datos no se utilizan y siempre se establecen en 0. El valor de tipo de u se utiliza para representar una llamada de sustitución. Esto solo se utiliza para lhs_type y siempre va acompañado de un rhs_type de n. El valor del dato es el tamaño, en bytes, de una asignación codificada mediante MAPPING de Parameter los nombres a sus valores para la subs() llamada. Los datos de asignación se encuentran justo después de la estructura, y la siguiente estructura comienza inmediatamente después de los datos de asignación.
Versión 12
La versión 12 añade compatibilidad con:
- circuitos que contienen variables
expr.Varvariables.
Cambios en el ENCABEZADO
La estructura HEADER para un circuito individual ha añadido tres uint32_t cuentas de las variables de entrada, capturadas y declaradas localmente en el circuito. El nuevo formulario tiene el siguiente aspecto:
struct {
uint16_t name_size;
char global_phase_type;
uint16_t global_phase_size;
uint32_t num_qubits;
uint32_t num_clbits;
uint64_t metadata_size;
uint32_t num_registers;
uint64_t num_instructions;
uint32_t num_vars;
} HEADER_V12;La estructura HEADER_V12 va seguida inmediatamente del mismo nombre, fase global, metadatos e información de registro que la versión V2 de la cabecera. Inmediatamente después de los registros está num_vars instancias de EXPR_VAR_STANDALONE que definen las variables en este circuito. Después, los datos continúan con definiciones e instrucciones personalizadas como en versiones anteriores de QPY.
EXPR_VAR_DECLARACIÓN
EXPR_VAR_DECLARATION define una instancia expr.Var que es independiente; es decir, representa una posición de memoria propia en lugar de envolver un archivo Clbit o ClassicalRegister. La carga útil es una estructura C:
struct {
char uuid_bytes[16];
char usage;
uint16_t name_size;
}a la que sigue inmediatamente una carga útil EXPR_TYPE y, a continuación, name_size bytes de datos de cadena de codificación UTF-8 que contienen el nombre de la variable.
El código de tipo de uso char toma los siguientes valores:
Código de tipo | Significado |
|---|---|
I | Una variable input al circuito. |
C | Una variable capture al circuito. |
L | Una variable declarada localmente al circuito. |
Cambios en EXPR_VAR
La variable EXPR_VAR ha ganado un nuevo código de tipo y carga útil, además de los preexistentes:
Python clase | Código de tipo | Carga útil |
|---|---|---|
UUID | U | Un uint32_t índice de la variable en la serie de EXPR_VAR_STANDALONE variables que se escribieron inmediatamente después de la cabecera del circuito. |
En particular, este nuevo código de tipo indexa variables predefinidas de la cabecera del circuito, en lugar de redefinir la variable de nuevo en cada lugar en el que se utiliza.
Cambios en EXPRESSION
El código de tipo EXPRESIÓN tiene una nueva entrada posible, i, correspondiente a expr.Index nodos.
Clase Qiskit | Código de tipo | Carga útil | Hijos |
|---|---|---|---|
Index | i | Sin carga adicional. Los hijos son el objetivo y el índice, en ese orden. | 2 |
Versión 11
La versión 11 es idéntica a la versión 10, salvo por lo siguiente. UUID.hexEn primer lugar, los nombres de los bloques CUSTOM_INSTRUCTION tienen un sufijo de la forma "_{uuid_hex}" donde uuid_hex es una cadena hexadecimal UUID como la que devuelve. Por ejemplo: "b3ecab5b4d6a4eb6bc2b2dbf18d83e1e". En segundo lugar, añade compatibilidad con AnnotatedOperation los objetos. La operación base de una operación anotada se almacena mediante el bloque INSTRUCTION, y se añade un valor 'a'``is added to indicate that the custom instruction is an annotated operation. The list of modifiers are stored as instruction parameters using INSTRUCTION_PARAM, with an additional value ``'m' adicional type para indicar que el parámetro es de tipo Modifier. Cada modificador se almacena mediante la estructura MODIFIER.
MODIFIER
Esto representa Modifier
struct {
char type;
uint32_t num_ctrl_qubits;
uint32_t ctrl_state;
double power;
}Esto es suficiente para almacenar los distintos tipos de modificadores necesarios para serializar objetos de tipo AnnotatedOperation. 'i'El campo type puede ser, 'c' o 'p', lo que indica si el modificador es, respectivamente, un modificador inverso, un modificador de control o un modificador de potencia. En el segundo caso, los campos num_ctrl_qubits y ctrl_state especifican la lógica de control de la operación básica, y en el tercer caso, el campo power representa la potencia de la operación básica.
Versión 10
La versión 10 añade compatibilidad con:
- Serialización nativa de symengine para objetos de tipo
ParameterExpression, así como para expresiones simbólicas en los bloques de programación de Pulse. - nuevos campos en la
TranspileLayoutclase añadidos en la versión « 0.45.0 » de Qiskit.
Se añade el campo symbolic_encoding a la cabecera del fichero y se introduce un nuevo tipo de codificación char, asignado a cada biblioteca simbólica de la siguiente manera: p se refiere a la codificación sympy y e a la codificación symengine.
Cambios en FILE_HEADER
El contenido de FILE_HEADER después de V10 se define como un struct C como:
struct {
uint8_t qpy_version;
uint8_t qiskit_major_version;
uint8_t qiskit_minor_version;
uint8_t qiskit_patch_version;
uint64_t num_circuits;
char symbolic_encoding;
} FILE_HEADER_V10;Cambios en el DISEÑO
La estructura LAYOUT se actualiza para tener un campo input_qubit_count adicional. Con la versión 10 la estructura LAYOUT es ahora:
struct {
char exists;
int32_t initial_layout_size;
int32_t input_mapping_size;
int32_t final_layout_size;
uint32_t extra_registers;
int32_t input_qubit_count;
}El resto de los datos de disposición que siguen a la LAYOUT estructura se representan igual que en las versiones anteriores. Si input qubit_count es < 0, eso indica que tanto _input_qubit_count como _output_qubit_list en el TranspileLayout objeto son None.
Versión 9
La versión 9 añade soporte para los clásicos Expr clásicos y sus Types.
EXPRESIÓN
Un nodo Expr está representado por un flujo de datos de ancho variable. Un nodo propiamente dicho está representado por (en orden en el flujo de bytes):
- un discriminador de código de tipo de un byte;
- un objeto EXPR_TYPE;
- una carga útil adicional específica del tipo de código;
- un número específico del código de tipo de cargas útiles de EXPRESIÓN hijas (el número de éstas está implícito en el código de tipo y no se almacena explícitamente).
Cada uno de ellos se describe en el cuadro siguiente:
Clase Qiskit | Código de tipo | Carga útil | Hijos |
|---|---|---|---|
Var | x | Un EXPR_VAR. | 0 |
Value | v | Un EXPR_VALUE. | 0 |
Cast | c | Un _Bool que corresponde al valor de implicit. | 1 |
Unary | u | Un uint8_t con el mismo valor numérico que el Unary.Op. | 1 |
Binary | b | Un uint8_t con el mismo valor numérico que el Binary.Op. | 2 |
EXPR_TIPO
A Type se codifica mediante un ASCII de un byte char que codifica el tipo de tipo, seguido de una carga útil que varía en función del tipo. Los códigos definidos son:
EXPR_VAR
Representa una variable en tiempo de ejecución de un Var nodo. Son un código de tipo, seguido de una carga útil específica del código de tipo:
Python clase | Código de tipo | Carga útil |
|---|---|---|
Clbit | C | Un uint32_t index que es el índice del Clbit en el circuito contenedor. |
ClassicalRegister | R | Un uint16_t reg_name_size, seguido de tantos bytes de datos de cadena UTF-8 del nombre del registro. |
EXPR_VALOR
Representa un objeto literal en el sistema de tipos clásico, como un número entero. Actualmente existen muy pocos literales de este tipo. Se codifican como un código de tipo, seguido de una carga útil específica del código de tipo.
Python tipo | Código de tipo | Carga útil |
|---|---|---|
bool | b | Un _Bool value. |
int | i | Un uint8_t num_bytes, seguido del número entero codificado en tantos bytes (orden de red) en una representación de complemento a dos. |
Cambios en la INSTRUCCIÓN
Para permitir el uso de Expr nodos en los campos IfElseOp.condition, WhileLoopOp.condition y SwitchCaseOp.target, la estructura INSTRUCTION se ha modificado, de forma compatible con la ABI, con respecto a su definición anterior. La nueva estructura es la estructura de C:
struct {
uint16_t name_size;
uint16_t label_size;
uint16_t num_parameters;
uint32_t num_qargs;
uint32_t num_cargs;
uint8_t conditional_key;
uint16_t conditional_reg_name_size;
int64_t conditional_value;
uint32_t num_ctrl_qubits;
uint32_t ctrl_state;
}donde el único cambio es que una entrada uint8_t conditional_key ha sustituido a _Bool has_conditional. Este nuevo conditional_key toma los siguientes valores numéricos, con estos efectos:
Valor | Efectos |
|---|---|
| 0 | El campo .condition de la instrucción es None. Los campos conditional_reg_name_size y conditional_value deben ignorarse. |
| 1 | La instrucción tiene su campo .condition establecido a una pareja de a Clbit o a ClassicalRegistery un número entero de valor conditional_value. La carga útil INSTRUCTION, incluidos los datos finales, se procesa exactamente igual que en las versiones de QPY inferiores a 8. |
| 2 | La instrucción tiene su campo .condition establecido en un Expr nodo. Los campos conditional_reg_name_size y conditional_value deben ignorarse. Los datos que siguen a la estructura van seguidos (como en las versiones de QPY inferiores a la 9) de name_size bytes de datos de cadena UTF-8 para el nombre de la clase y label_size bytes de datos de cadena UTF-8 para la etiqueta (si existe). Entonces, hay un INSTRUCTION_PARAM, que contendrá una EXPRESION. Después, el análisis sintáctico continúa con los structs INSTRUCTION_ARG, como en versiones anteriores de QPY. |
Cambios en INSTRUCTION_PARAM
Se añade un nuevo código de tipo x que define un parámetro EXPRESSION.
Versión 8
La versión 8 incorpora compatibilidad para gestionar un valor TranspileLayout almacenado en el QuantumCircuit.layout atributo. En la versión 8, justo después del bloque de calibraciones, al final de la carga útil del circuito, ahora aparece la LAYOUT estructura. Esta estructura describe el tamaño de los tres atributos de una TranspileLayout clase.
DISEÑO
struct {
char exists;
int32_t initial_layout_size;
int32_t input_mapping_size;
int32_t final_layout_size;
uint32_t extra_registers;
}Si alguno de los valores con signo es -1 indica que el atributo correspondiente es None.
Inmediatamente después de la LAYOUT estructura hay una estructura REGISTERS para extra_registers (concretamente, el formato introducido en la versión 4 ) definiciones de registros independientes que no están presentes en el circuito. Además, hay initial_layout_size INITIAL_LAYOUT_BIT estructuras para definir el TranspileLayout.initial_layout atributo.
DISEÑO INICIAL
struct {
int32_t index;
int32_t register_size;
}Un valor de -1 indica None (es decir, no hay ningún registro asociado al bit). Después de cada estructura INITIAL_LAYOUT_BIT hay register_size bytes para una cadena codificada utf8 para el nombre del registro.
A partir de la versión 18, los qubits anónimos (index == -1) omiten por completo el campo register_size ; la estructura es simplemente un único int32_t. Los qubits con nombre no sufren cambios.
Tras la disposición inicial hay input_mapping_size matriz de uint32_t enteros que representan las posiciones del bit físico de la disposición inicial. Esto permite construir una lista de bits virtuales donde el índice del array es su posición de mapeo de entrada.
Por último, hay una matriz de enteros final_layout_size uint32_t . Cada elemento es un índice en el atributo qubits del circuito que permite construir un mapeado desde la posición inicial del qubit hasta la posición de salida al final del circuito.
Versión 7
La versión 7 añade soporte para la instrucción Reference y la serialización de un programa ScheduleBlock manteniendo su referencia a subrutinas:
from qiskit import pulse
from qiskit import qpy
with pulse.build() as schedule:
pulse.reference("cr45p", "q0", "q1")
pulse.reference("x", "q0")
pulse.reference("cr45p", "q0", "q1")
with open('template_ecr.qpy', 'wb') as fd:
qpy.dump(schedule, fd)El modelo de datos SCHEDULE_BLOCK convencional se conserva, pero en la versión 7 va seguido inmediatamente por un bloque extra MAPPING utf8 bytes que representa los datos de las subrutinas referenciadas.
Se añade un nuevo carácter de clave de tipo al grupo SCHEDULE_BLOCK_INSTRUCTIONS para la instrucción Reference .
y:Referenceinstrucción
Se añade un nuevo carácter de clave de tipo al grupo SCHEDULE_BLOCK_OPERANDS para los operandos de la instrucción Reference , que es una tupla de cadenas, por ejemplo ( “cr45p”, “q0”, “q1” ).
o: cadena (cadena operando)
Tenga en cuenta que esta es la misma codificación con la cadena incorporada Python, sin embargo, la codificación de valor estándar en QPY utiliza s carácter de tipo para datos de cadena, que entra en conflicto con el SymbolicPulse en el ámbito de los operandos de instrucción de pulso. Se reserva un carácter de tipo especial o para los datos de cadena que aparecen en los operandos de la instrucción de impulso.
Además, la versión 7 añade dos nuevas claves de tipo a la estructura INSTRUCTION_PARM. "d" no va seguido de ningún dato y representa el valor literal CASE_DEFAULT para el soporte de declaraciones de conmutación. "R" representa una ClassicalRegister o Clbity va seguido del mismo formato que la descripción de registro o bit clásica que se utiliza en el primer elemento de la condición de un campo INSTRUCTION.
Versión 6
La versión 6 añade compatibilidad con ScalableSymbolicPulse. Estos objetos se guardan y se leen como objetos de SymbolicPulse, y el nombre de la clase se añade a los datos para manejar correctamente la selección de la clase.
SymbolicPulse comienza ahora con la cabecera SYMBOLIC_PULSE_V2 :
struct {
uint16_t class_name_size;
uint16_t type_size;
uint16_t envelope_size;
uint16_t constraints_size;
uint16_t valid_amp_conditions_size;
_bool amp_limited;
}El único cambio con respecto a la versión 5 es la adición de class_name_size. La cabecera va seguida inmediatamente de class_name_size utf8 bytes con el nombre de la clase. Actualmente, se admite SymbolicPulse o ScalableSymbolicPulse. El resto de los datos son idénticos a los de la versión 5.
Versión 5
La versión 5 difiere de la versión 4 en que añade compatibilidad con ScheduleBlock y modifica dos cargas útiles: la carga útil de metadatos INSTRUCTION y el bloque CUSTOM_INSTRUCTION. Ahora cuentan con nuevos campos para tener mejor en cuenta ControlledGate los objetos de un circuito. Además, se define una nueva carga útil, MAP_ITEM, para implementar el bloque MAPPING.
En Qiskit v2.0 se ha eliminado la compatibilidad con la representación de programaciones de pulsos y calibraciones personalizadas. Al cargar cargas útiles QPY, estos campos de datos ahora se ignoran o generan un error al utilizar Qiskit para la deserialización.
En QPY versión 5 y superiores,
struct {
char type;
}sigue inmediatamente al bloque de cabecera del archivo para representar el tipo de programa almacenado en el archivo.
- Cuando
type==c,QuantumCircuitla carga útil sigue - Cuando
type==s,ScheduleBlockcarga útil sigue
No se pueden empaquetar diferentes programas en el mismo archivo. Debe crear archivos diferentes para los distintos tipos de programas. Se pueden guardar varios objetos del mismo tipo en un único archivo.
CALENDARIO_BLOQUE
ScheduleBlock aparece por primera vez en la versión 5 de QPY. Esto permite a los usuarios guardar programas de pulso en el formato binario QPY de la siguiente manera:
from qiskit import pulse, qpy
with pulse.build() as schedule:
pulse.play(pulse.Gaussian(160, 0.1, 40), pulse.DriveChannel(0))
with open('schedule.qpy', 'wb') as fd:
qpy.dump(schedule, fd)
with open('schedule.qpy', 'rb') as fd:
new_schedule = qpy.load(fd)[0]Tenga en cuenta que el circuito y el bloque de programación se serializan y deserializan a través de la misma interfaz QPY. El tipo de datos de entrada se analiza implícitamente y no se requiere ninguna opción adicional para guardar el bloque de programación.
PROGRAMA_BLOQUE_ENCABEZADO
ScheduleBlock comienza con la siguiente cabecera:
struct {
uint16_t name_size;
uint64_t metadata_size;
uint16_t num_element;
}que va seguido inmediatamente por name_size utf8 bytes del nombre del horario y metadata_size utf8 bytes del diccionario de metadatos serializados JSON adjunto al horario.
ALINEACIONES DE BLOQUES DE PROGRAMACIÓN
A continuación, el contexto de alineación del bloque de programación comienza con char que representa el tipo de contexto admitido, seguido del bloque SEQUENCE que representa los parámetros asociados al contexto de alineación AlignmentKind._context_params. El tipo de contexto char se asigna a cada subclase de alineación de la siguiente manera:
l:AlignLeftr:AlignRights:AlignSequentiale:AlignEquispaced
Tenga en cuenta que el contexto AlignFunc no es compatible debido a la función de devolución de llamada almacenada en los parámetros del contexto.
INSTRUCCIONES DEL BLOQUE DE HORARIO
A este bloque de alineación le sigue num_element longitud de elementos de bloque que pueden consistir en bloques de programación anidados e instrucciones de programación. Cada instrucción de programación comienza con char que representa el tipo de instrucción seguido del bloque SECUENCIA que representa la instrucción operands. Obsérvese que la estructura de datos de pulso Instruction está unificada, de modo que la instancia puede determinarse unívocamente por la clase y una tupla de operandos. La asignación del tipo char a la subclase de instrucciones se define como sigue:
a:Acquireinstrucciónp:Playinstrucciónd:Delayinstrucciónf:SetFrequencyinstruccióng:ShiftFrequencyinstrucciónq:SetPhaseinstrucciónr:ShiftPhaseinstrucciónb:RelativeBarrierinstrucciónt:TimeBlockadeinstruccióny:Referenceinstrucción (nueva en la versión 0.7 )
PROGRAMA_BLOQUE_OPERANDOS
Los operandos de estas instancias pueden serializarse a través del mecanismo estándar de serialización de valores de QPY, sin embargo existen tipos de objetos especiales que sólo aparecen en los operandos de programación. Dado que los operandos se serializan como SECUENCIA, cada elemento debe empaquetarse con la estructura INSTRUCTION_PARAM pack struct, donde cada carga útil comienza con un bloque de cabecera formado por el char type y uint64_t size. Los objetos especiales comienzan con la siguiente clave de tipo:
c:Channelw:Waveforms:SymbolicPulseo: cadena (operando cadena, nuevo en la versión 0.7 )
CANAL
El bloque de canal comienza con el subtipo de canal char que asigna los datos de un objeto a la subclase Channel . El mapeo se define del siguiente modo:
d:DriveChannelc:ControlChannelm:MeasureChannela:AcquireChannele:MemorySlotr:RegisterSlot
La clave va seguida inmediatamente del índice del canal serializado como INSTRUCTION_PARAM.
Forma de onda
El bloque de forma de onda comienza con la cabecera WAVEFORM:
struct {
double epsilon;
uint32_t data_size;
_bool amp_limited;
}al que sigue data_size bytes de binario complejo ndarray generado por numpy.save. Esto representa los complejos puntos de datos IQ reproducidos en un dispositivo cuántico. name se guarda después de las muestras en el pack struct INSTRUCTION_PARAM, que puede ser cadena o None.
SymbolicPulse
SymbolicPulse comienza con la cabecera SYMBOLIC_PULSE:
struct {
uint16_t type_size;
uint16_t envelope_size;
uint16_t constraints_size;
uint16_t valid_amp_conditions_size;
_bool amp_limited;
}que va seguido de type_size utf8 bytes de SymbolicPulse.pulse_type cadena que representa una clase de forma de onda, como "gaussiana" o “GaussianSquare”. A continuación, se generan envelope_size, constraints_size, valid_amp_conditions_size utf8 bytes de expresiones simbólicas serializadas para SymbolicPulse.envelope, SymbolicPulse.constraints, y SymbolicPulse.valid_amp_conditions, respectivamente. Dado que la representación en cadena de estas expresiones suele ser larga, el binario de la expresión se genera mediante el módulo zlib de python con compresión de datos.
Para especificar unívocamente una instancia de pulso, también necesitamos almacenar los parámetros asociados, que consisten en duration y el resto de parámetros como un diccionario. Los parámetros del diccionario se vuelcan primero en la forma MAPPING, y después se vuelca duration con la estructura de paquete INSTRUCTION_PARAM. Por último, name se guarda también con el pack struct INSTRUCTION_PARAM, que puede ser cadena o None.
correlacionar
El MAPPING es una representación para un objeto de mapeo arbitrario. Se trata de una SECUENCIA de longitud fija de pares clave-valor representados por la carga útil MAP_ITEM.
Un MAP_ITEM comienza con una cabecera definida como:
struct {
uint16_t key_size;
char type;
uint16_t size;
}al que siguen inmediatamente los key_size utf8 bytes que representan la clave del diccionario en cadena y size utf8 bytes de datos de objeto arbitrario de QPY serializable type.
CALIBRACIONES DE CIRCUITOS
El bloque CIRCUIT_CALIBRATIONS es un diccionario para definir calibraciones de pulsos del conjunto de instrucciones personalizadas. Este bloque comienza con la siguiente cabecera CALIBRACIÓN:
struct {
uint16_t num_cals;
}que es seguido por el num_cals longitud de las entradas de calibración, cada uno comienza con el CALIBRATION_DEF encabezado:
struct {
uint16_t name_size;
uint16_t num_qubits;
uint16_t num_params;
char type;
}La cabecera de definición de calibración va seguida de name_size utf8 bytes del nombre de la puerta, num_qubits longitud de los enteros que representan una secuencia de qubits, y num_params longitud de la carga útil INSTRUCTION_PARAM para los parámetros asociados a la instrucción personalizada. El type indica la clase de programa de impulsos que es, en principio, ScheduleBlock o Schedule. A partir de la versión 5 de QPY, sólo se admite la carga útil ScheduleBlock . Por último, la carga útil SCHEDULE_BLOCK se empaqueta para cada entrada CALIBRATION_DEF.
Instrucción
Se ha modificado el bloque INSTRUCTION para añadir dos nuevos campos num_ctrl_qubits y ctrl_state , que se utilizan para modelar los ControlledGate.num_ctrl_qubits atributos y ControlledGate.ctrl_state . El nuevo formato de la estructura «payload packed» es el siguiente:
struct {
uint16_t name_size;
uint16_t label_size;
uint16_t num_parameters;
uint32_t num_qargs;
uint32_t num_cargs;
_Bool has_conditional;
uint16_t conditional_reg_name_size;
int64_t conditional_value;
uint32_t num_ctrl_qubits;
uint32_t ctrl_state;
}El resto de la carga útil de la instrucción es la misma. Puede consultar las INSTRUCCIONES para conocer los detalles de la carga útil completa.
INSTRUCCIÓN PERSONALIZADA
El bloque CUSTOM_INSTRUCTION de la versión 5 de QPY añade un nuevo campo base_gate_size que se utiliza para definir el tamaño del qiskit.circuit.Instruction objeto almacenado en el ControlledGate.base_gate atributo de un objeto personalizado ControlledGate . Con este cambio, el bloque de metadatos CUSTOM_INSTRUCTION queda así:
struct {
uint16_t name_size;
char type;
uint32_t num_qubits;
uint32_t num_clbits;
_Bool custom_definition;
uint64_t size;
uint32_t num_ctrl_qubits;
uint32_t ctrl_state;
uint64_t base_gate_size
}Inmediatamente después de la estructura CUSTOM_INSTRUCTION se encuentra el nombre codificado en utf8 de tamaño name_size.
Si custom_definition es True , eso significa que los bytes que le siguen size inmediatamente contienen datos de un circuito QPY que pueden utilizarse para la definición personalizada de esa puerta. Si custom_definition es False , entonces la instrucción puede considerarse opaca (es decir, sin definición). Este type campo determina qué tipo de objeto se creará con la definición personalizada. Si es 'g' , será un Gate objeto; 'i' si es, será un Instruction objeto.
A continuación, los siguientes base_gate_size bytes contienen la carga útil de INSTRUCTION para ControlledGate.base_gate.
Además, se añade 'c' un valor adicional para type que se utiliza para indicar que la instrucción personalizada es una ControlledGate.
Versión 4
La versión 4 es idéntica a la 3, salvo que añade 2 nuevas cadenas de tipos a la estructura INSTRUCTION_PARAM, z para representar None (que se codifica como ningún dato), q para representar un QuantumCircuit (que se codifica como un circuito QPY), r para representar un range de enteros (que se codifica como un RANGE ), y t para representar un sequence (que se codifica como se define en SEQUENCE ). Además, la versión 4 cambia el tipo de matriz de asignación de índices de registro de uint32_t a int64_t. Si los valores de cualquiera de los elementos de la matriz son negativos, representan un bit de registro que no está presente en el circuito.
El formato de la cabecera REGISTERS también se ha actualizado para
struct {
char type;
_Bool standalone;
uint32_t size;
uint16_t name_size;
_bool in_circuit;
}que sólo añade el campo in_circuit que representa si el registro forma parte del circuito o no.
RANGO
Un RANGO es una representación de un objeto range . Se define como:
struct {
int64_t start;
int64_t stop;
int64_t step;
}SEQUENCE
Una SECUENCIA es una representación de un objeto secuencial arbitrario. Como las secuencias son sólo contenedores de longitud fija de objetos arbitrarios python su QPY no puede representar completamente cualquier secuencia, pero siempre y cuando el contenido en una secuencia sean otros tipos serializables QPY para la carga útil INSTRUCTION_PARAM el objeto sequence puede ser serializado.
Un parámetro de instrucción de secuencia comienza con una cabecera definida como:
struct {
uint64_t size;
}seguido de size elementos que son cargas útiles INSTRUCTION_PARAM, donde cada uno de estos define un elemento en la secuencia. El objeto de secuencia se convertirá en el tipo adecuado, por ejemplo, tuple, después.
Versión 3
La versión 3 del formato QPY es idéntica a la versión 2, salvo que define un formato de estructura para representar un de forma nativa PauliEvolutionGate en QPY. Para ello, la estructura CUSTOM_DEFINITIONS admite ahora un nuevo tipo de valor 'p' para representar un PauliEvolutionGate. Las entradas de las tablas de instrucciones personalizadas tienen un nombre único generado que comienza con la cadena "###PauliEvolutionGate_" seguida de una cadena UUID (versiones 11-17) o de un contador (versión 18 y posteriores; véase la versión 18 ). Este nombre de puerta está reservado en QPY y, si tienes un objeto Instruction personalizado con un conjunto de definiciones y ese prefijo de nombre, se producirá un error. Si es de tipo, la 'p' carga útil de datos se define de la siguiente manera:
PAULI_EVOLUCIÓN
Esto representa el alto nivel PauliEvolutionGate
struct {
uint64_t operator_count;
_Bool standalone_op;
char time_type;
uint64_t time_size;
uint64_t synthesis_size;
}A continuación, aparecen inmediatamente los operator_count elementos definidos por la carga útil SPARSE_PAULI_OP_LIST_ELEM. A continuación, tenemos time_size los bytes que representan el time atributo. Si standalone_op es True , entonces solo puede haber un único operador. La codificación de estos bytes viene determinada por el valor de time_type. Los valores posibles de time_type son 'f', 'p', y 'e'. Si time_type es 'f' un doble, 'p' define un Parameter objeto representado por un PARÁMETRO; e define un ParameterExpression objeto (que no es un Parameter) representado por un PARÁMETRO_EXPR. A continuación vienen synthesis_size los bytes, que constituyen una carga útil JSON codificada en formato « utf8 » y que representa la EvolutionSynthesis clase utilizada por la puerta.
LISTA_DE_ELEMENTOS_DE_SPARSE_PAULI_OP
Esto constituye un ejemplo de SparsePauliOp.
struct {
uint32_t pauli_op_size;
}a la que le siguen inmediatamente bytes con pauli_op_size datos en formato.npy [2] que representan el SparsePauliOp.
La versión 3 del formato QPY también define un formato de estructura para representar un ParameterVectorElement como una subclase distinta de un Parameter. Esto añade un nuevo tipo de parámetro «char» 'v' para representar un ParameterVectorElement valor que ahora se admite como tipo «string» para un INSTRUCTION_PARAM. La carga útil de estos parámetros se define a continuación como PARAMETER_VECTOR_ELEMENT.
PARÁMETRO_VECTOR_ELEMENTO
Un PARAMETER_VECTOR_ELEMENT representa un ParameterVectorElement objeto que contiene los datos correspondientes a un INSTRUCTION_PARAM. El contenido de PARAMETER_VECTOR_ELEMENT se define de la siguiente manera:
struct {
uint16_t vector_name_size;
uint64_t vector_size;
char uuid[16];
uint64_t index;
}al que sigue inmediatamente vector_name_size utf8 bytes que representan el nombre del vector del parámetro.
Modificado en la versión QPY: 18
El vector se almacena una vez en el circuito y PARAMETER_VECTOR_TABLE esta carga útil se convirtió en una referencia al mismo, uint16_t vector_index seguida de uint64_t index, sin nombre, tamaño ni UUID propios. Véase la versión 18.
PARÁMETRO_EXPR
Además, dado que la versión v3 del formato QPY distingue entre un Parameter y ParameterVectorElement un, es necesario actualizar la carga útil de un ParameterExpression para que distinga entre ambos tipos. A continuación se muestra el formato de carga útil modificado, que es prácticamente idéntico al de la versión 1 y la versión 2, pero en el que simplemente se ha modificado la map_elements estructura para incluir un campo de tipo «symbol».
Un PARAMETER_EXPR representa un ParameterExpression objeto que contiene los datos de un INSTRUCTION_PARAM. El contenido de un PARAMETER_EXPR se define como:
struct {
uint64_t map_elements;
uint64_t expr_size;
}Inmediatamente después de la cabecera hay expr_size bytes de datos utf8 que contienen la cadena de expresión, que es el srepr sympy de la expresión para la expresión del parámetro. A continuación hay un mapa de símbolos que contiene elementos map_elements con el formato
struct {
char symbol_type;
char type;
uint64_t size;
}La symbol_type clave determina el tipo de carga útil de la representación simbólica del elemento. Si es p , representa un Parameter , y si es v , representa un ParameterVectorElement. La estructura del elemento «map» va seguida inmediatamente de la carga útil de la clave «symbol map»; si symbol_type es p , entonces va seguida inmediatamente de un objeto «PARAMETER» (tanto la estructura como los bytes del nombre « utf8 »), y si symbol_type es v , entonces la estructura va seguida inmediatamente de «PARAMETER_VECTOR_ELEMENT» (tanto la estructura como los bytes del nombre « utf8 »). A continuación vienen size los bytes correspondientes a los datos del símbolo. El formato de los datos depende del valor de type. Si type es p , entonces representa un Parameter y el tamaño será 0; el valor será simplemente el mismo que la clave. Del mismo modo, si es v``type , entonces representa un ParameterVectorElement y el tamaño será 0, ya que el valor será exactamente el mismo que la clave. Si type es f , entonces representa un número flotante de doble precisión. Si type es c , representa un número complejo de doble precisión, que se representa mediante el tipo COMPLEX. Por último, si el tipo es i , representa un entero que es un int64_t.
Versión 2
La versión 2 del formato QPY es idéntica a la versión 1, salvo por la sección HEADER, que es ligeramente diferente. Puede consultar la sección Versión 1 para obtener más información sobre el resto del formato de la carga útil.
Cabecera
El contenido de HEADER se define como una estructura C:
struct {
uint16_t name_size;
char global_phase_type;
uint16_t global_phase_size;
uint32_t num_qubits;
uint32_t num_clbits;
uint64_t metadata_size;
uint32_t num_registers;
uint64_t num_instructions;
}A continuación, aparecen inmediatamente bytes de datos « utf8 » correspondientes name_size al nombre del circuito. A continuación, de forma inmediata global_phase_size , vienen los bytes que representan la fase global. El contenido de esos datos viene determinado por el valor de global_phase_type. Si es 'f' el dato es un número de tipo «float» y tiene el tamaño de un double. Si 'p' define un Parameter objeto representado por una estructura PARAM (véase más abajo), e define un ParameterExpression objeto (que no es un Parameter) representado por una estructura PARAM_EXPR (véase más abajo).
Versión 1
Cabecera
Los contenidos de HEADER definidos como una estructura C son:
struct {
uint16_t name_size;
double global_phase;
uint32_t num_qubits;
uint32_t num_clbits;
uint64_t metadata_size;
uint32_t num_registers;
uint64_t num_instructions;
}A esto le sigue inmediatamente name_size bytes de datos utf8 para el nombre del circuito.
metadata
El campo METADATA es una cadena JSON codificada en UTF8. Después de leer el HEADER (que tiene un tamaño fijo al principio del archivo QPY) y la cadena name , se lee el número de bytes metadata_size y se analiza el JSON para obtener los metadatos del circuito.
Registros
El contenido de REGISTERS es un número de objeto REGISTER. Si num_registers es > 0 entonces después de leer METADATA se lee ese número de structs REGISTER definidos como:
struct {
char type;
_Bool standalone;
uint32_t size;
uint16_t name_size;
}type puede ser 'q' o 'c'.
Inmediatamente después de la estructura REGISTER se encuentra el nombre del registro codificado en utf8 de tamaño name_size. Después de los name utf8 bytes hay una matriz de valores int64_t de tamaño size que contiene un mapa del índice del registro al índice del qubit del circuito. Por ejemplo, el valor del elemento de la matriz 0’s es el índice de la posición de register[0]en la lista de qubits del circuito contenedor.
Antes de la versión 4 de QPY, el tipo de los elementos de la matriz era uint32_t. Se ha modificado para permitir valores negativos que representan bits de la matriz no presentes en el circuito
El booleano independiente determina si el registro se construye como un registro independiente que se añadió al circuito o se creó a partir de bits existentes. Un registro se considera independiente si tiene bits construidos únicamente como parte de él, por ejemplo:
qr = QuantumRegister(2)
qc = QuantumCircuit(qr)el registro qr sería un registro independiente. Mientras que algo como:
bits = [Qubit(), Qubit()]
qr2 = QuantumRegister(bits=bits)
qc = QuantumCircuit(qr2)qr2 tendría standalone fijado en False.
DEFINICIONES PERSONALIZADAS
Esta sección especifica definiciones personalizadas para cualquiera de las instrucciones del circuito.
El contenido de CUSTOM_DEFINITION_HEADER se define como:
struct {
uint64_t size;
}Si el tamaño es mayor que 0 significa que el circuito contiene instrucción(es) personalizada(s). Cada instrucción personalizada se define con un bloque CUSTOM_INSTRUCTION definido como:
struct {
uint16_t name_size;
char type;
uint32_t num_qubits;
uint32_t num_clbits;
_Bool custom_definition;
uint64_t size;
}Inmediatamente después de la estructura CUSTOM_INSTRUCTION se encuentra el nombre codificado en utf8 de tamaño name_size.
Si custom_definition es True , eso significa que los bytes que le siguen size inmediatamente contienen datos de un circuito QPY que pueden utilizarse para la definición personalizada de esa puerta. Si custom_definition es False , entonces la instrucción puede considerarse opaca (es decir, sin definición). Este type campo determina qué tipo de objeto se creará con la definición personalizada. Si es 'g' , será un Gate objeto; 'i' si es, será un Instruction objeto.
INSTRUCCIONES
El contenido de INSTRUCTIONS es una lista de objetos de metadatos INSTRUCTION
struct {
uint16_t name_size;
uint16_t label_size;
uint16_t num_parameters;
uint32_t num_qargs;
uint32_t num_cargs;
_Bool has_conditional;
uint16_t conditional_reg_name_size;
int64_t conditional_value;
}A este objeto de metadatos le siguen inmediatamente name_size bytes de utf8 bytes para el name. name aquí es el nombre de la clase Qiskit para la clase Instrucción si está definida en Qiskit. De lo contrario, vuelve al nombre de la instrucción personalizada. Tras los name bytes hay label_size bytes de datos utf8 para la etiqueta si se ha establecido una en la instrucción. Después de los bytes de etiqueta si has_conditional es True entonces hay conditional_reg_name_size bytes de datos utf8 para el nombre del registro condicional. En el caso de condiciones de bit clásico único, el nombre del registro utf8 irá precedido de un carácter nulo “x00” y, a continuación, de un entero de cadena utf8 que representa el índice del bit clásico en el circuito en el que se encuentra la condición.
Modificado en la versión QPY: 18. Esta carga útil es una estructura etiquetada en lugar de una cadena « utf8 »; véase la versión 18.
Esto es seguido inmediatamente por los structs INSTRUCTION_ARG para la lista de argumentos de esa instrucción. Estos están en el orden de todos los argumentos cuánticos (hay num_qargs de estos) seguido de todos los argumentos clásicos (num_cargs de estos).
El contenido de cada INSTRUCTION\ARG es:
struct {
char type;
uint32_t index;
}type puede ser 'q' o 'c'.
Después de todos los argumentos para una instrucción los parámetros se especifican con num_parameters INSTRUCTION_PARAM structs.
El contenido de cada INSTRUCTION_PARAM es:
struct {
char type;
uint64_t size;
}Después de cada INSTRUCTION_PARAM, los siguientes size bytes corresponden a los datos del parámetro. El type campo puede ser 'i', 'f', 'p', 'e', 's', 'c' o 'n' , lo que determina el formato. Si 'i' es un entero, 'f' es un número de tipo «double»; 's' si es una cadena (codificada como « utf8 »), 'c' es un número complejo, y los datos se representan mediante el formato de estructura de la sección PARAMETER_EXPR. 'p' define un Parameter objeto que se representa mediante una estructura PARAMETER; e define un ParameterExpression objeto (que no es un Parameter) que se representa mediante una estructura PARAMETER_EXPR (en la versión 3 del formato QPY, el formato se ha modificado ligeramente; véase: PARAMETER_EXPR ), 'n' representa un objeto de numpy (ya sea un ndarray o un tipo numpy), lo que significa que los datos están en formato.npy [2], y en la versión 3 'v' de QPY representa un ParameterVectorElement que se representa mediante una estructura PARAMETER_VECTOR_ELEMENT.
Parámetro
Un PARÁMETRO representa un Parameter objeto que contiene los datos correspondientes a una INSTRUCTION_PARAM. El contenido del PARÁMETRO se define de la siguiente manera:
struct {
uint16_t name_size;
char uuid[16];
}al que sigue inmediatamente name_size utf8 bytes que representan el nombre del parámetro.
PARÁMETRO_EXPR
Un PARAMETER_EXPR representa un ParameterExpression objeto que contiene los datos de un INSTRUCTION_PARAM. El contenido de un PARAMETER_EXPR se define como:
Los datos de PARAMETER_EXPR comienzan con una cabecera:
struct {
uint64_t map_elements;
uint64_t expr_size;
}Inmediatamente después de la cabecera hay expr_size bytes de datos utf8 que contienen la cadena de expresión, que es el srepr sympy de la expresión para la expresión del parámetro. A continuación hay un mapa de símbolos que contiene elementos map_elements con el formato
struct {
char type;
uint64_t size;
}A lo que le sigue inmediatamente el PARAMETER objeto (tanto los bytes de la estructura como los del nombre « utf8 ») correspondiente a la clave del mapa de símbolos. A continuación vienen size los bytes correspondientes a los datos del símbolo. El formato de los datos depende del valor de type. Si type es p , entonces representa un Parameter y el tamaño será 0; el valor será simplemente el mismo que la clave. Si type es f , entonces representa un número flotante de doble precisión. Si type es c , representa un número complejo de doble precisión, que se representa mediante COMPLEX. Por último, si el tipo es i , representa un entero que es un int64_t.
COMPLEJO
Cuando se representa un valor complejo de doble precisión en QPY se utiliza la siguiente estructura:
struct {
double real;
double imag;
}esto coincide con la representación interna en C del tipo complejo de Python. [3]
Referencias
[1 ]
https://tools.ietf.org/html/rfc1700
https://numpy.org/doc/stable/reference/generated/numpy.lib.format.html
[3 ]