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 argumento de
clsclave-valor (kwarg) en la llamadajson.loadinterna empleada para deserializar la carga JSON utilizada para el.metadataatributo de cualquier programa del archivo QPY. Si no se especifica, los metadatos del circuito se analizarán como JSON mediante la función de la bibliotecajson.load()estándar, utilizando la claseJSONDecoderpredeterminada. - 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 de objetosAnnotationpersonalizados.
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 biblioteca del motor
symengineno está instalada cuando se carga una carga útil QPY versión 10, 11 o 12 que está utilizando codificación simbólica symengine y contieneParameterExpressioninstancias. - QpyError - si se carga un tipo de datos conocido pero no admitido.
Tipo de retorno
list[ QPY_SUPPORTED_TYPES]
dump
qiskit.qpy.dump(programs, file_obj, metadata_serializer=None, use_symengine=False, version=17, 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) – QPY admite el almacenamiento de objetos en el archivo especificado, como el 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 elemento delprogramsdiccionario y que se utilizará como argumentoclsclave en la llamada ajson.dump ()para serializar dicho diccionario a JSON. -
use_symengine (bool) – Esta bandera ya no es utilizada por las versiones de QPY soportadas por esta función y no tendrá ningún impacto en la carga útil QPY generada, excepto para establecer un campo en una cabecera de 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.
NotaSi se serializa un archivo
QuantumCircuitque contieneParameterExpressionconversionbajo con la intención de cargar la carga utilizando una versión histórica de Qiskit, lo más seguro es establecer la banderause_symengineaFalse. Las versiones de Qiskit anteriores a 1.2.4 no pueden cargar archivos QPY que contengan objetos serializados ensymenginea menos que la versión de utilizada entre los entornos de carga y generación coincidaParameterExpressiona menos que la versión desymengineutilizada entre los entornos de carga y generación coincida. -
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 sean compatibles con 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 compatible para el 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.
Tenga en cuenta que los metadatos del circuito y los objetos Annotation son serializados y deserializados por clases proporcionadas por el usuario, ya que los objetos en sí son completamente personalizados por el usuario, por lo que la compatibilidad hacia adelante y hacia atrás de estos está limitada por lo que el usuario proporciona.
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.
Con versiones de Qiskit anteriores a 1.2.4, el argumento use_symengine=True de qpy.dump() podría causar problemas de retrocompatibilidad si hubiera 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.4.2 | 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 17
La versión 17 añade compatibilidad con la serialización y deserialización PauliEvolutionGate que contiene SparseObservable como operador(es).
Cambios en PAULI_EVOLUTION
El formato de PAULI_EVOLUTION en sí mismo permanece sin cambios, pero se actualiza el formato de los operadores que siguen inmediatamente a la puerta de evolución empaquetada. En lugar de los elementos operator_count definidos por el formato SPARSE_PAULI_OP_LIST_ELEM, la carga útil ahora especifica el tipo de cada operador para tener en cuenta los operadores de tipo SparsePauliOp o de SparseObservable.
La nueva carga útil tras PAULI_EVOLUTION contiene ahora exactamente secuencias operator_count de un bool ("!?") seguido 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). Si es así False, el operador es un SparsePauliOp e interpretado según el formato SPARSE_PAULI_OP_LIST_ELEM existente.
Nuevo SPARSE_OBSERVABLE
El formato SPARSE_OBSERVABLE representa una instancia de un SparseObservable, dado 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, términos binarios, índices y límites de la observable. El formato especifica el número de bytes que ocupa cada matriz. El número de elementos se puede calcular dividiendo el número de bytes por el tamaño de cada elemento.
- Cada coeficiente se almacena como dos elementos «!d» consecutivos, primero la parte real y luego la parte imaginaria.
- Los elementos del término binario son de tipo «!«H» y representa el valor e u8 e del
SparseObservable.BitTerm- 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 la carga útil de una instrucción completa, incluidos los objetos de seguimiento y sin bytes de relleno entre los elementos, como:
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 cambia. Sin embargo, para 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 mapea ahora las claves de los bytes brutos 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
La versión 13 añadió una representación nativa de serialización Qiskit para ParameterExpression. Las versiones anteriores de QPY utilizaban sympy o 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 archivo ParameterExpression.
El principal cambio en el formato de serialización se produce en la carga útil PARAMETER_EXPR. Los bytes expr_size que siguen a la cabecera contienen ahora una matriz de structs PARAM_EXPR_ELEM_V13 . La intención es que esta matriz sea leída una estructura a la vez, donde cada estructura describe una de las llamadas a realizar para reconstruir el archivo 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 campo op_code se utiliza para definir la operación que se añade al archivo 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 | Recursivo ParameterExpression definición inicio |
e | Recursivo ParameterExpression definición stop |
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 flotantes, el valor literal se codifica como un doble con 0 de relleno, mientras que los números complejos se codifican como parte real seguida de parte imaginaria, ocupando 64 bits cada una. Para i, el valor se codifica como un entero con signo de 64 bits con 0 de relleno para el ancho completo de 128 bits. n se utiliza para representar un None y normalmente no se utiliza directamente, ya que indica un argumento que no se utiliza. Para p los datos son el UUID para el Parameter que puede buscarse en el mapa de símbolos descrito en la carga útil map_elements outer PARAMETER_EXPR. Si el valor del tipo es s esto marca el comienzo de una nueva sección recursiva para un anidado ParameterExpression. Por ejemplo, en el siguiente fragmento hay un expr interno contenido en final_expr, 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 se encuentra s , esto indica que hasta que un e` struct is reached, the next structs are used for a recursive definition. For both ``s y e tipos, los valores de los datos no se utilizan, y siempre se establece en 0. El valor de tipo u se utiliza para representar una llamada de sustitución. Sólo se utiliza para lhs_type y siempre va emparejado con un rhs_type de n. El valor de los datos es el tamaño en bytes de una asignación codificada MAPPING de Parameter nombres a su valor para la subs() llamada. Los datos de asignación aparecen inmediatamente 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 10, salvo por lo siguiente. En 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 devuelta por UUID.hex. Por ejemplo: "b3ecab5b4d6a4eb6bc2b2dbf18d83e1e". En segundo lugar, añade soporte para AnnotatedOperation objetos. La operación base de una operación anotada se almacena utilizando el bloque INSTRUCTION, y se añade un valor adicional type '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' para indicar que el parámetro es del tipo Modifier. Cada modificador se almacena utilizando 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 diferentes tipos de modificadores necesarios para serializar objetos de tipo AnnotatedOperation. El campo type es 'i', 'c' o 'p', y representa 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 base, y en el tercer caso el campo power representa la potencia de la operación base.
Versión 10
La versión 10 añade compatibilidad con:
- serialización nativa de symengine para objetos de tipo
ParameterExpressionasí como expresiones simbólicas en bloques de programación Pulse. - nuevos campos de la clase
TranspileLayoutañadidos en la versión de Qiskit 0.45.0.
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 diseño después de la estructura LAYOUT se representa como en 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 apoyar el uso de Expr nodos en los campos IfElseOp.condition, WhileLoopOp.condition y SwitchCaseOp.target, la estructura INSTRUCTION se modifica de forma compatible con ABI a su definición anterior. La nueva estructura es la estructura 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 añade soporte para manejar un TranspileLayout almacenado en el atributo QuantumCircuit.layout atributo. En la versión 8, inmediatamente después del bloque de calibración, al final de la carga útil del circuito, se encuentra la estructura LAYOUT . Esta estructura indica 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 estructura LAYOUT hay una estructura REGISTERS para extra_registers (específicamente el formato introducido en la versión 4 ) definiciones de registros independientes que no están presentes en el circuito. Luego hay initial_layout_size INITIAL_LAYOUT_BIT structs 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.
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 cambia con respecto a la 4 al añadir compatibilidad con ScheduleBlock y cambiar dos cargas útiles: la carga útil de metadatos INSTRUCTION y el bloque CUSTOM_INSTRUCTION. Ahora disponen de nuevos campos para ControlledGate objetos en un circuito. Además, se define la 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
El bloque INSTRUCTION se modificó para añadir dos nuevos campos num_ctrl_qubits y ctrl_state , que se utilizan para modelar los parámetros ControlledGate.num_ctrl_qubits y ControlledGate.ctrl_state atributos. El nuevo formato de estructura de carga útil es:
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 objeto almacenado en el atributo qiskit.circuit.Instruction objeto almacenado en el atributo ControlledGate.base_gate para un objeto ControlledGate personalizado. Con este cambio, el bloque de metadatos CUSTOM_INSTRUCTION pasa a ser:
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 significa que los bytes size inmediatamente siguientes contienen datos de un circuito QPY que pueden utilizarse para la definición personalizada de esa puerta. Si custom_definition es False , la instrucción puede considerarse opaca (es decir, sin definición). El campo type determina qué tipo de objeto se creará con la definición personalizada. Si es 'g' será un Gate objeto, 'i' 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 un valor adicional para type 'c' que se utiliza para indicar que la instrucción personalizada es una instrucción personalizada 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 PauliEvolutionGate de forma nativa 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. Este nombre de puerta está reservado en QPY, por lo que si tienes un objeto Instruction personalizado con un conjunto de definiciones que incluya ese prefijo, se producirá un error. Si es de tipo, 'p' la 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;
}Esto es seguido inmediatamente por operator_count elementos definidos por la carga útil SPARSE_PAULI_OP_LIST_ELEM. A continuación tenemos time_size bytes que representan el atributo time . Si standalone_op es True entonces sólo debe haber un único operador. La codificación de estos bytes viene determinada por el valor de time_type. Los posibles valores de time_type son 'f', 'p' y 'e'. Si time_type es 'f' es un double, 'p' define un Parameter objeto que está representado por un PARÁMETRO, e define un ParameterExpression objeto (que no es un Parameter) que está representado por un PARÁMETRO_EXPR. A continuación hay synthesis_size bytes que es una carga útil json codificada en utf8 que representa la clase EvolutionSynthesis clase utilizada por la puerta.
LISTA_DE_ELEMENTOS_DE_SPARSE_PAULI_OP
Esto representa una instancia de SparsePauliOp.
struct {
uint32_t pauli_op_size;
}que es seguido inmediatamente por pauli_op_size bytes que son datos en formato.npy [2 ] que representa el archivo SparsePauliOp.
La versión 3 del formato QPY también define un formato struct para representar una ParameterVectorElement como una subclase distinta de Parameter. Esto añade un nuevo parámetro de tipo char 'v' para representar un ParameterVectorElement que ahora se admite como un valor de cadena de tipo para un INSTRUCTION_PARAM. La carga útil de estos parámetros se define a continuación como PARÁMETRO_VECTOR_ELEMENTO.
PARÁMETRO_VECTOR_ELEMENTO
Un PARAMETER_VECTOR_ELEMENT representa un ParameterVectorElement objeto los datos de un INSTRUCTION_PARAM. El contenido del elemento PARÁMETRO VECTOR se define como sigue:
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.
PARÁMETRO_EXPR
Además, dado que el formato QPY versión v3 distingue entre a Parameter y ParameterVectorElement la carga útil de a ParameterExpression debe actualizarse para distinguir entre los tipos. El siguiente es el formato de carga útil modificado que es prácticamente idéntico al formato de la versión 1 y la versión 2, pero solo modifica el map_elements Estructura para incluir un campo de tipo símbolo.
Un PARAMETER_EXPR representa un ParameterExpression objeto que 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 los bytes de la estructura como los del nombre « utf8 »); y si symbol_type es v , entonces la estructura va seguida inmediatamente de «PARAMETER_VECTOR_ELEMENT» (tanto los bytes de la estructura como los 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 esto le sigue inmediatamente name_size bytes de datos utf8 para el nombre del circuito. Inmediatamente después viene global_phase_size bytes que representan la fase global. El contenido de esos datos viene dictado por el valor de global_phase_type. Si es 'f' el dato es un float y tiene el tamaño de un double. Si es 'p' define un Parameter objeto que está representado por una estructura PARAM (véase más adelante), e define un objeto ParameterExpression objeto (que no es un Parameter) que está representado por una estructura PARAM_EXPR (véase más adelante).
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 significa que los bytes size inmediatamente siguientes contienen datos de un circuito QPY que pueden utilizarse para la definición personalizada de esa puerta. Si custom_definition es False , la instrucción puede considerarse opaca (es decir, sin definición). El campo type determina qué tipo de objeto se creará con la definición personalizada. Si es 'g' será un Gate objeto, 'i' 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.
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 son los datos del parámetro. El campo type puede ser 'i', 'f', 'p', 'e', 's', 'c' o 'n' que dictan el formato. Para 'i' es un entero, 'f' es un doble, 's' si es una cadena (codificada como utf8 ), 'c' es un complejo y los datos se representan mediante el formato struct en la sección PARAMETER_EXPR. 'p' define un Parameter representado por una estructura PARAMETER, e define un objeto ParameterExpression (que no es un Parameter) que está representado por una estructura PARAMETER_EXPR (en la versión 3 de 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 son datos en formato.npy [2], y en la versión 3 de QPY 'v' representa un objeto ParameterVectorElement que está representado por una estructura PARAMETER_VECTOR_ELEMENT.
Parámetro
Un PARÁMETRO representa un Parameter objeto los datos de un INSTRUCTION_PARAM. El contenido del PARÁMETRO se define como:
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 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;
}Que es seguido inmediatamente por PARAMETER objeto (tanto la estructura y utf8 nombre de bytes) para la clave del mapa de símbolos. A continuación, size bytes para 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á el mismo que la clave. Si type es f entonces representa un flotador de doble precisión. Si type es c representa un complejo de doble precisión, que se representa por COMPLEX. Por último, si el tipo es i representa un número 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 ]