Serialização QPY
qiskit.qpy
O QPY é um formato de serialização binária para objetos QuantumCircuit projetado para ser multiplataforma, independente da versão do Python e compatível com as versões anteriores. O QPY deve ser usado se você precisar de um mecanismo para salvar ou copiar entre sistemas um arquivo QuantumCircuit que preserve a estrutura completa do objeto Qiskit (exceto para atributos personalizados definidos fora do código Qiskit). Isso difere de outros formatos de serialização, como OpenQASM ( 2.0 ou 3.0 ), que tem um modelo de abstração diferente e pode resultar na perda de informações contidas no circuito original (ou não é capaz de representar alguns aspectos dos objetos do Qiskit) ou o pickle do Python, que preservará exatamente o objeto do Qiskit, mas só funcionará para uma única versão do Qiskit (também é potencialmente inseguro ).
Uso básico
O uso do QPY é definido para ser direto e espelhar a API do usuário dos serializadores na biblioteca padrão do Python, pickle e json. Há duas funções voltadas para o usuário: qiskit.qpy.dump() e qiskit.qpy.load() que são usadas para despejar dados QPY em um objeto de arquivo e carregar circuitos de dados QPY em um objeto de arquivo, respectivamente. Por exemplo:
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]A função qiskit.qpy.dump() também permite incluir vários circuitos em um único arquivo QPY:
with open('twenty_bells.qpy', 'wb') as fd:
qpy.dump([qc] * 20, fd)e, em seguida, carregar esse arquivo retornará uma lista com todos os circuitos
with open('twenty_bells.qpy', 'rb') as fd:
twenty_new_bells = qpy.load(fd)documentação da API
load
qiskit.qpy.load(file_obj, metadata_deserializer=None, annotation_factories=None)
Carregar um arquivo binário QPY
Essa função é usada para carregar um arquivo de programa QPY Qiskit serializado e criar QuantumCircuit objetos a partir de seu conteúdo. Por exemplo:
from qiskit import qpy
with open('bell.qpy', 'rb') as fd:
circuits = qpy.load(fd)ou com um arquivo compactado com gzip:
import gzip
from qiskit import qpy
with gzip.open('bell.qpy.gz', 'rb') as fd:
circuits = qpy.load(fd)que lerá o conteúdo do qpy e retornará uma lista de objetos QuantumCircuit objetos do arquivo.
Parâmetros
- file_obj (BinaryIO) – Um objeto semelhante a um arquivo que contém os dados binários QPY para um circuito.
- metadata_deserializer (type[JSONDecoder] | None) – Uma classe JSONDecoder opcional que será usada para o argumento de
clschave (kwarg) na chamadajson.loadinterna utilizada para desserializar a carga JSON usada no.metadataatributo de qualquer programa no arquivo QPY. Se isso não for especificado, os metadados do circuito serão analisados como JSON pela função dajson.load()biblioteca padrão, utilizando a classeJSONDecoderpadrão. - annotation_factories (Mapping[str, Callable[[], annotation.QPYSerializer]] | None) – Mapeamento de namespaces para funções que criam novas instâncias de
annotation.QPUSerializer, para gerenciar o carregamento de objetosAnnotationpersonalizados.
Retorna
A lista de programas Qiskit contidos nos dados QPY. Uma lista é sempre retornada, mesmo que haja apenas um programa nos dados QPY.
Aumentos
- QiskitError - se
file_objnão for um arquivo QPY válido - TypeError - Quando um tipo de dados inválido é carregado.
- MissingOptionalLibraryError - Se a biblioteca do mecanismo
symenginenão estiver instalada ao carregar uma carga útil QPY versão 10, 11 ou 12 que esteja usando a codificação simbólica symengine e contenha instânciasParameterExpressioninstâncias. - QpyError - se um tipo de dados conhecido, mas sem suporte, for carregado.
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)
Gravar dados binários QPY em um arquivo
Essa função é usada para salvar um circuito em um arquivo para uso posterior ou transferência entre máquinas. O formato QPY é compatível com versões anteriores e pode ser carregado com versões futuras do Qiskit.
Por exemplo:
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 daí, você pode gravar os dados do qpy em um arquivo:
with open('bell.qpy', 'wb') as fd:
qpy.dump(qc, fd)ou um arquivo compactado com gzip:
import gzip
with gzip.open('bell.qpy.gz', 'wb') as fd:
qpy.dump(qc, fd)O que salvará o circuito serializado do qpy no arquivo fornecido.
Parâmetros
-
programs (list[QPY_SUPPORTED_TYPES] | QPY_SUPPORTED_TYPES) – O QPY suporta o(s) objeto(s) a ser(em) armazenado(s) no arquivo especificado como objeto. O QPY é compatível com
QuantumCircuit. -
file_obj (BinaryIO) – O arquivo como objeto para gravar os dados QPY também
-
metadata_serializer (type[JSONEncoder] | None) – Uma classe JSONEncoder opcional que receberá o
.metadataatributo de cada item doprogramsdicionário e será usada como argumentoclsde chave na chamadajson.dump ()para serializar esse dicionário em JSON. -
use_symengine (bool) – Esse sinalizador não é mais usado pelas versões do QPY suportadas por essa função e não terá impacto sobre a carga útil do QPY gerada, exceto para definir um campo em um cabeçalho de arquivo do QPY v13 que não é usado.
-
version (int) –
A versão do formato QPY a ser emitida. Por padrão, o padrão é o formato suportado mais recente de
QPY_VERSIONno entanto, por motivos de compatibilidade, se você precisar carregar a carga útil QPY gerada com uma versão mais antiga do Qiskit, também poderá selecionar uma versão mais antiga do formato QPY até a versão mínima de exportação compatível, que só pode ser alterada durante o lançamento de uma versão principal do Qiskit, para gerar uma versão mais antiga do formato QPY. Você pode acessar a versão atual do QPY e a versão mínima compatível comqpy.QPY_VERSIONeqpy.QPY_COMPATIBILITY_VERSIONrespectivamente.NotaSe especificado com uma versão mais antiga do QPY, as limitações e os possíveis erros decorrentes do formato QPY nessa versão persistirão. Isso só deve ser usado se for necessária a compatibilidade com o carregamento da carga útil com uma versão mais antiga do Qiskit.
NotaSe estiver serializando um arquivo
QuantumCircuitque contém objetosParameterExpressioncomversiondefinido como baixo com a intenção de carregar a carga útil usando uma versão histórica do Qiskit, é mais seguro definir o sinalizadoruse_symenginecomoFalse. As versões do Qiskit anteriores a 1.2.4 não podem carregar arquivos QPY que contenham objetos serializados emsymengineParameterExpressiona menos que a versão dosymengineusada entre os ambientes de carregamento e geração seja a mesma. -
annotation_factories (Mapping[str, Callable[[], annotation.QPYSerializer]] | None) – Mapeamento de namespaces para funções que criam novas instâncias de
annotation.QPUSerializer, para lidar com o despejo de objetosAnnotationpersonalizados. A chamada subsequente aload()precisará utilizar objetos serializadores semelhantes, que reconheçam o formato de saída personalizado desses serializadores.
Aumentos
- TypeError - Quando um tipo de dados inválido é inserido.
- ValueError - Quando um número de versão não compatível é passado para o argumento
version.
get_qpy_version
qiskit.qpy.get_qpy_version(file_obj)
Essa função identifica a versão QPY do arquivo.
Essa função lerá o cabeçalho de file_obj e retornará a versão do formato QPY. Ele não avançará o cursor de file_obj. Se estiver usando isso para uma leitura subsequente, como para chamar load()você pode passar file_obj diretamente. Por exemplo:
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) – Um objeto semelhante a um arquivo que contém os dados binários QPY para um circuito.
Retorna
A versão QPY do arquivo especificado.
Tipo de retorno
Essas funções gerarão uma subclasse personalizada de QiskitError se encontrarem problemas durante a serialização ou desserialização.
QpyError
exception qiskit.qpy.QpyError(*message)
Bases: QiskitError
Erros gerados pelo módulo qpy.
Defina a mensagem de erro.
Quando uma versão QPY de destino inferior à máxima é definida para serialização, mas o objeto a ser serializado contém recursos que não podem ser representados nesse formato, uma subclasse de QpyError é criada:
UnsupportedFeatureForVersion
exception qiskit.qpy.UnsupportedFeatureForVersion(feature, required, target)
Bases: QpyError
Erro QPY gerado quando a versão de destino do dump é muito baixa para um recurso que está presente no objeto a ser serializado.
Parâmetros
qiskit.qpy.QPY_VERSION
A versão atual do formato QPY a partir desta versão. Esse é o valor padrão do argumento da palavra-chave version em qpy.dump() e também o limite superior dos valores aceitos para o mesmo argumento. Esse também é o limite superior das versões suportadas pelo qpy.load().
Tipo
qiskit.qpy.QPY_COMPATIBILITY_VERSION
A versão atual do formato QPY de compatibilidade mínima. Essa é a versão mínima que o qpy.dump() aceitará para o argumento da palavra-chave version . qpy.load() será capaz de carregar todas as versões de formato lançadas do QPY (até QPY_VERSION).
Tipo
Compatibilidade com QPY
O formato QPY foi projetado para ser compatível com versões anteriores e posteriores. Isso significa que você deve ser capaz de carregar um QPY com qualquer versão mais recente do Qiskit do que a que o gerou. No entanto, o carregamento de um arquivo QPY com uma versão mais antiga do Qiskit não é suportado e pode não funcionar.
Por exemplo, se você gerou um arquivo QPY usando o qiskit-terra 0.18.1, poderá carregar esse arquivo QPY com o qiskit-terra 0.19.0 e um hipotético qiskit-terra 0.29.0. No entanto, o carregamento desse arquivo QPY com 0.18.0 não é suportado e pode não funcionar.
Observe que os metadados de circuito e os objetos personalizados Annotation são serializados e desserializados por classes fornecidas pelo usuário, uma vez que os próprios objetos são totalmente personalizados pelo usuário, de modo que a compatibilidade com versões anteriores e posteriores desses objetos é limitada pelo que o usuário fornece.
Se um recurso que estiver sendo carregado for obsoleto na versão correspondente do qiskit, o QPY gerará uma mensagem QPYLoadingDeprecatedFeatureWarning informando sobre o período de depreciação e como o recurso será tratado internamente.
QPYLoadingDeprecatedFeatureWarning
exception qiskit.qpy.QPYLoadingDeprecatedFeatureWarning
Bases: QiskitWarning
Aviso de depreciação visível para funções de carregamento QPY sem um ponto estável na pilha de chamadas.
Com versões do Qiskit anteriores a 1.2.4, o argumento use_symengine=True para qpy.dump() poderia causar problemas de compatibilidade com versões anteriores se houvesse objetos ParameterExpression objetos para serializar. Em particular:
- Quando a versão de carregamento do Qiskit é 1.2.4 ou superior, os arquivos QPY gerados com qualquer versão do Qiskit >= 0.46.0 podem ser carregados. Se uma versão do Qiskit entre 0.45.0 e 0.45.3 foi usada para gerar os arquivos, e o argumento não padrão
use_symengine=Truefoi dado aqpy.dump()o arquivo só poderá ser lido se a versão dosymengineusada no ambiente de geração estiver na série 0.11 ou 0.13, mas se o ambiente tiver sido criado durante a janela de suporte do Qiskit 0.45, é provável que osymengine==0.9.2tenha sido usado. - Quando a versão de carregamento do Qiskit estiver entre 0.46.0 e 1.2.2, inclusive, o arquivo só poderá ser lido se a versão instalada de
symengineno ambiente de carregamento corresponder à versão usada no ambiente de geração.
Para recuperar um arquivo QPY que falhou com erros relacionados à versão symengine durante uma chamada para qpy.load()primeiro tente usar o Qiskit >= 1.2.4 para carregar o arquivo. Se isso ainda falhar, é provável que o Qiskit 0.45.x tenha sido usado para gerar o arquivo com use_symengine=True. Nesse caso, use o Qiskit 0.45.3 com symengine==0.9.2 para carregar o arquivo e, em seguida, exporte-o novamente para o QPY configurando use_symengine=False. O arquivo resultante pode então ser carregado por qualquer versão posterior do Qiskit.
A partir da versão do Qiskit 2.0.0, que removeu o módulo Pulse da biblioteca, o QPY oferece suporte limitado para carregar cargas úteis que incluem dados de pulso. Ao carregar uma carga útil de ScheduleBlock , uma QpyError será gerada uma exceção. Ao carregar uma carga útil para um circuito que contenha portas de pulso, o circuito de saída conterá instruções personalizadas sem dados de calibração anexados para cada porta de pulso, deixando-os indefinidos.
Histórico de versões do formato QPY
Se estiver planejando carregar um arquivo QPY entre diferentes versões do Qiskit, é útil saber quais versões estavam disponíveis em uma determinada versão. Como o QPY é compatível com versões anteriores, mas não com versões posteriores, você precisa garantir que uma determinada versão do formato QPY tenha sido lançada na versão que você está chamando load() com. A tabela a seguir lista as versões do QPY que eram suportadas em cada versão do Qiskit (e do qiskit-terra antes do Qiskit 1.0.0 ) desde a introdução do QPY no qiskit-terra 0.18.0.
Versão do Qiskit (qiskit-terra para < 1.0.0 ) | dump() formato(s) versões de saída | load() versão máxima suportada (versões de formatos mais antigos sempre podem ser lidas) |
|---|---|---|
| 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 | 15 |
| 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 | 12 |
| 1.2.3 (arrancado) | 10, 11, 12 | 12 |
| 1.2.2 | 10, 11, 12 | 12 |
| 1.2.1 | 10, 11, 12 | 12 |
| 1.2.0 | 10, 11, 12 | 12 |
| 1.1.0 | 10, 11, 12 | 12 |
| 1.0.2 | 10, 11 | 11 |
| 1.0.1 | 10, 11 | 11 |
| 1.0.0 | 10, 11 | 11 |
| 0.46.1 | 22 | 22 |
| 0.45.3 | 22 | 22 |
| 0.45.2 | 22 | 22 |
| 0.45.1 | 22 | 22 |
| 0.45.0 | 22 | 22 |
| 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
O formato de serialização QPY é um formato de serialização binária portátil e multiplataforma para objetos no Qiskit QuantumCircuit objetos no Qiskit. O formato básico do arquivo é o seguinte:
Um arquivo QPY (ou objeto de memória) sempre começa com a seguinte sequência de 6 bytes UTF8 : QISKIT que é imediatamente seguida pelo cabeçalho geral do arquivo. O conteúdo do cabeçalho do arquivo, conforme definido como uma estrutura C, é:
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, um novo campo é adicionado à estrutura do cabeçalho do arquivo para representar o esquema de codificação usado para expressões 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;
}De V16 em diante, a estrutura do cabeçalho do arquivo é imediatamente seguida por uma tabela de início de circuito que contém os deslocamentos de bytes de cada carga útil de circuito no arquivo. Há num_circuits entradas na tabela de início de circuito, cada uma delas do tipo uint64_t. Em todas as versões anteriores, o cabeçalho do arquivo é imediatamente seguido pelas cargas úteis do circuito em sequência, sem qualquer preenchimento intermediário.
Todos os valores utilizam a ordem de bytes de rede [1] (big-endian) para garantir a compatibilidade entre plataformas. A exceção a isso é que, nas versões do formato QPY <= 17, a codificação de números inteiros e de precisão flutuante é INSTRUCTION_PARAM do tipo little endian.
Cada circuito individual é composto das seguintes peças, na ordem de cima para baixo:
HEADER
METADATA
REGISTERS
ANNOTATION_HEADER
STANDALONE_VARS
CUSTOM_DEFINITIONS
INSTRUCTIONSAlterado na versão QPY: 15 ANNOTATION_HEADER foi adicionado entre REGISTERS e STANDALONE_VARS.
Alterado na versão QPY: 12 STANDALONE_VARS foi adicionado entre REGISTERS e CUSTOM_DEFINITIONS.
Há uma carga útil de circuito para cada circuito (em que o número total é ditado por num_circuits no cabeçalho do arquivo). Não há preenchimento entre os circuitos nos dados.
Versão 17
A versão 17 adiciona suporte para serialização e deserialização PauliEvolutionGate contendo SparseObservable como operador(es).
Alterações ao PAULI_EVOLUTION
O formato do PAULI_EVOLUTION em si permanece inalterado, mas o formato dos operadores que seguem imediatamente o gate de evolução compactado é atualizado. Em vez dos elementos operator_count definidos pelo formato SPARSE_PAULI_OP_LIST_ELEM, a carga útil agora especifica o tipo de cada operador para contabilizar os operadores do tipo SparsePauliOp ou do tipo SparseObservable.
A nova carga útil após PAULI_EVOLUTION agora contém exatamente sequências operator_count de um bool ("!?") seguido pelo operador. Se o bool for True, o operador é um SparseObservable e interpretado com o novo formato SPARSE_OBSERVABLE (veja abaixo). Se for False, o operador é um SparsePauliOp e interpretado de acordo com o formato SPARSE_PAULI_OP_LIST_ELEM existente.
Novo SPARSE_OBSERVABLE
O formato SPARSE_OBSERVABLE representa uma instância de um 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;
}que é imediatamente seguido pelo número de qubits e, em seguida, pelas matrizes de dados dos coeficientes, termos de bits, índices e limites do observável. O formato especifica o número de bytes que cada matriz ocupa. O número de elementos pode ser calculado dividindo o número de bytes pelo tamanho de cada elemento.
- Cada coeficiente é armazenado como dois elementos “!d” consecutivos, primeiro a parte real e depois a parte imaginária.
- Os elementos do termo binário são do tipo “!H” e representa o valor u8 do
SparseObservable.BitTerm- Os elementos dos índices são do tipo “!Eu”.
- Os elementos de limites são do tipo “!Q”.
Versão 16
A versão 16 adiciona uma tabela de início de circuito ao formato de arquivo QPY. Serve como um índice dos offsets de bytes de cada carga útil do circuito no arquivo. A motivação para essa alteração é permitir um carregamento mais eficiente de circuitos usando multi-threading em uma futura implementação Rust do desserializador QPY.
Alterações à DURAÇÃO
Uma nova variante foi adicionada à codificação do tipo DURATION existente para picossegundos. Isso é codificado da seguinte forma e é um acréscimo às variantes suportadas anteriormente.
Classe Qiskit | Código do tipo | Carga Útil |
|---|---|---|
ps | p | Um double value. |
Versão 15
A versão 15 adiciona o conceito de anotações personalizadas ao formato de carga útil. O próprio QPY não especifica como as anotações são serializadas ou desserializadas, pois são objetos de usuário personalizados. No entanto, o formato coopera com subserializadores.
A versão 15 adiciona o campo ANNOTATION_HEADER entre os campos STANDALONE_VARS e CUSTOM_DEFINITIONS no nível superior de uma carga útil de circuito único. Ele modifica a interpretação de um campo da estrutura INSTRUCTION de maneira compatível com a ABI e adiciona um trailer INSTRUCTION_ANNOTATIONS a INSTRUCTION , que está presente condicionalmente em um bit definido na carga útil INSTRUCTION .
Nova ANOTAÇÃO_HEADER
O campo ANNOTATION_HEADER é uma carga útil de tamanho variável no cabeçalho. Ele começa com uma instância de ANNOTATION_HEADER_STATIC, que é a estrutura C:
struct ANNOTATION_HEADER_STATIC {
uint32_t num_namespaces;
}Isso é imediatamente seguido por num_namespaces instâncias do payload ANNOTATION_STATE . A ordem dessas informações é importante e deve ser mantida durante o processo de desserialização, pois as cargas úteis subsequentes do site INSTRUCTION_ANNOTATION serão indexadas a elas.
A carga útil do ANNOTATION_STATE começa com a estrutura C fixa:
struct ANNOTATION_STATE_HEADER {
uint32_t namespace_size;
uint64_t state_size;
}Esse cabeçalho é imediatamente seguido por namespace_size bytes de texto codificado em UTF-8, que compõem o namespace. Esses bytes são imediatamente seguidos por state_size bytes de dados arbitrários. O formato dessa carga útil de "estado" não é definido pelo QPY. Em vez disso, é responsabilidade de um objeto externo associado ao namespace armazenado. O formato não determina como produzir esses objetos; como as anotações são totalmente personalizadas, o usuário deve fornecer os métodos de serialização e desserialização.
Alterações às INSTRUÇÕES
O struct INSTRUCTION é modificado de forma compatível com a ABI em relação à sua definição anterior na versão 9. A nova estrutura é a estrutura C (lembre-se de que não há preenchimento entre os campos, nem no final da estrutura):
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;
}onde o campo uint8_t extras_key substitui o anterior uint8_t conditional_key. A diferença está puramente na interpretação. Os dois bits inferiores do byte ainda são interpretados como definição da condição e de seu tipo. O bit mais alto do byte agora é um sinalizador que indica se um campo INSTRUCTION_ANNOTATIONS_HEADER está presente (se o bit estiver definido) nos dados finais da estrutura INSTRUCTION .
Uma carga útil de instrução completa aparece no fluxo de dados, incluindo objetos à direita e sem nenhum byte de preenchimento entre os 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)As observações a seguir se aplicam:
- se os dois bits baixos do
extras_keytiverem o valor2, indicando que a condição é umEXPRESSION, oconditional_reg_name_sizeserá sempre zero. - esse campo estará presente se, e somente se, os dois bits baixos do
extras_keytiverem o valor2, indicando que a condição é umEXPRESSION. - esse campo estará presente se e somente se o bit alto do
extras_keyestiver definido. Esse campo tem um tamanho variável; consulte New INSTRUCTION_ANNOTATIONS.
Novas INSTRUÇÕES_ANOTAÇÕES
A carga útil do INSTRUCTION_ANNOTATIONS começa com o C struct:
struct INSTRUCTION_ANNOTATIONS_HEADER {
uint32_t num_annotations;
}Essa carga é imediatamente seguida por num_annotations instâncias da carga INSTRUCTION_ANNOTATION , que tem um tamanho variável.
A carga útil de INSRTUCTION_ANNOTATION é definida pela seguinte estrutura em C mais um número final de bytes igual a payload_size, chamado ANNOTATION_PAYLOAD.
struct INSTRUCTION_ANNOTATION {
uint32_t namespace_index;
uint32_t payload_size;
}O namespace_index é um índice inteiro na lista de objetos ANNOTATION_NAMESPACE definidos no ANNOTATION_HEADER. O namespace de serialização de uma anotação é a cadeia de caracteres codificada em UTF-8 no payload relevante.
O formato do objeto ANNOTATION_PAYLOAD não é especificado pelo QPY. Ele é definido por um objeto de serialização externo associado ao namespace referido pelo namespace_index e seu estado de serializador associado no ANNOTATION_HEADER.
Mudanças dentro d PARAM_EXPR_ELEM_V13
A estrutura em si não foi alterada. Entretanto, para um PARAM_EXPR_ELEM_V13 que representa uma ParameterExpression.subs() chamada (com op_code = 15 e, portanto, lhs_type = 'p' e rhs_type = 'n'), o MAPPING à direita agora mapeia as chaves dos bytes brutos dos Parameter UUIDs para os valores substituídos. Anteriormente (nas versões 13 e 14 do QPY), esse mapeamento armazenava os nomes dos parâmetros como chaves.
Versão 14
A versão 14 adiciona um novo tipo de núcleo DURATION, suporte para Type classes Float e Duratione um novo tipo de nó de expressão Stretch.
Duração
Um Duration é codificado por um ASCII char de byte único que codifica o tipo de tipo, seguido por uma carga útil que varia de acordo com o tipo. Os códigos definidos são:
Classe Qiskit | Código do tipo | Carga Útil |
|---|---|---|
dt | t | Um unsigned long long value. |
ns | n | Um double value. |
us | u | Um double value. |
ms | m | Um double value. |
s | s | Um double value. |
Alterações em EXPR_VAR_DECLARATION
O tipo EXPR_VAR_DECLARATION agora é usado para representar Var variáveis autônomas e Stretch identificadores. Para dar suporte a essa alteração, o código do tipo de uso tem duas novas entradas possíveis, além das existentes:
Código do tipo | Significado |
|---|---|
A | Um capture trecho para o circuito. |
O | Um trecho declarado localmente para o circuito. |
Alterações à EXPRESSÃO
O código do tipo EXPRESSION tem uma nova entrada possível, s, correspondente a expr.Stretch nós.
Classe Qiskit | Código do tipo | Carga Útil | Crianças |
|---|---|---|---|
Stretch | s | Um unsigned short var_index | 0 |
Alterações em EXPR_TYPE
A tabela a seguir mostra as novas classes de tipos adicionadas na versão:
Alterações em EXPR_VALUE
O sistema de tipos da expressão clássica agora suporta novos tipos de codificação para literais de valor, além das codificações existentes para int e bool. As novas codificações de tipo de valor estão abaixo:
Python tipo | Código do tipo | Carga Útil |
|---|---|---|
float | f | Um double value. |
Duration | t | Um DURATION. |
Versão 13
A versão 13 adicionou uma representação de serialização nativa do Qiskit para ParameterExpression. As versões anteriores do QPY dependiam de sympy ou symengine para serializar a expressão simbólica subjacente. A partir da versão 13, o QPY agora representa a sequência de chamadas de API usadas para criar o arquivo ParameterExpression.
A principal alteração no formato de serialização está na carga útil PARAMETER_EXPR. Os bytes expr_size que seguem o cabeçalho agora contêm uma matriz de structs PARAM_EXPR_ELEM_V13 . A intenção é que essa matriz seja lida um struct por vez, em que cada struct descreve uma das chamadas a serem feitas para reconstruir o ParameterExpression.
PARAM_EXPR_ELEM_V13
O formato da estrutura é definido como:
struct {
unsigned char op_code;
char lhs_type;
char lhs[16];
char rhs_type;
char rhs[16];
} PARAM_EXPR_ELEM_V13;O campo op_code é usado para definir a operação adicionada ao arquivo ParameterExpression. O valor do pode 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() |
| 22 | exp() |
| 11 | log() |
| 12 | sign() |
| 13 | gradient() |
| 14 | conjugate() |
| 15 | subs() |
| 16 | abs() |
| 17 | arctan() |
| 255 | NULL |
O valor NULL de 255 é usado apenas para preencher o campo de código de operação para entradas que não são operações reais, mas indicam definições recursivas. Em seguida, os campos lhs_type e rhs_type são usados para descrever os tipos de operando e podem ser um dos seguintes caracteres codificados em UTF-8 :
Valor | Tipo |
|---|---|
n | None |
p | Parameter |
f | float |
c | complex |
i | int |
s | Recursivo ParameterExpression definição start |
e | Recursivo ParameterExpression definição stop |
u | substituição |
Se o valor do tipo for f, c ou i, as larguras de campo correspondentes em lhs ou rhs serão de 128 bits cada. No caso de floats, o valor literal é codificado como um double com 0 de preenchimento, enquanto os números complexos são codificados como parte real seguida da parte imaginária, ocupando 64 bits cada. Para i, o valor é codificado como um número inteiro assinado de 64 bits com 0 de preenchimento para a largura total de 128 bits. n é usado para representar um None e, normalmente, não é usado diretamente, pois indica um argumento que não é usado. Para p , os dados são o UUID para o Parameter que pode ser pesquisado no mapa de símbolos descrito no payload externo PARAMETER_EXPR do site map_elements . Se o valor do tipo for s , isso marca o início de uma nova seção recursiva para uma seção aninhada ParameterExpression. Por exemplo, no trecho a seguir, há um expr interno contido em final_expr, que constitui uma expressão aninhada:
from qiskit.circuit import Parameter
x = Parameter("x")
y = Parameter("y")
z = Parameter("z")
expr = (x + y) / 2
final_expr = z**2 + exprQuando o endereço s é encontrado, isso indica que, até que os tipos e` struct is reached, the next structs are used for a recursive definition. For both ``s e e sejam encontrados, os valores dos dados não são usados e são sempre definidos como 0. O valor de tipo u é usado para representar uma chamada de substituição. Isso é usado somente para lhs_type e é sempre combinado com um rhs_type de n. O valor dos dados é o tamanho em bytes de um mapeamento codificado por MAPPING de Parameter nomes para seu valor para a subs() chamada. Os dados de mapeamento estão imediatamente após a estrutura, e a próxima estrutura começa imediatamente após os dados de mapeamento.
Versão 12
A versão 12 adiciona suporte para:
- circuitos que contêm variáveis que possuem memória
expr.Varvariáveis.
Alterações no HEADER
A estrutura HEADER de um circuito individual adicionou três uint32_t contagens das variáveis de entrada, capturadas e declaradas localmente no circuito. O novo formulário tem a seguinte aparência:
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;A estrutura HEADER_V12 é seguida imediatamente pelo mesmo nome, fase global, metadados e informações de registro que a versão V2 do cabeçalho. Imediatamente após os registros, há num_vars instâncias de EXPR_VAR_STANDALONE que definem as variáveis nesse circuito. Depois disso, os dados continuam com definições e instruções personalizadas, como nas versões anteriores do QPY.
EXPR_VAR_DECLARAÇÃO
O site EXPR_VAR_DECLARATION define uma instância expr.Var que é autônoma, ou seja, representa uma localização de memória própria, em vez de envolver uma instância Clbit ou ClassicalRegister. A carga útil é uma estrutura C:
struct {
char uuid_bytes[16];
char usage;
uint16_t name_size;
}que é imediatamente seguido por uma carga útil EXPR_TYPE e, em seguida, name_size bytes de dados de cadeia de caracteres de codificação UTF-8 contendo o nome da variável.
O código do tipo de uso char assume os seguintes valores:
Código do tipo | Significado |
|---|---|
I | Uma variável input do circuito. |
C | Uma variável capture para o circuito. |
L | Uma variável declarada localmente para o circuito. |
Alterações em EXPR_VAR
A variável EXPR_VAR ganhou um novo código de tipo e carga útil, além dos já existentes:
Python classe | Código do tipo | Carga Útil |
|---|---|---|
UUID | U | Um uint32_t índice da variável na série de EXPR_VAR_STANDALONE variáveis que foram escritas imediatamente após o cabeçalho do circuito. |
Notavelmente, esse novo código de tipo indexa variáveis predefinidas do cabeçalho do circuito, em vez de redefinir a variável novamente em cada local em que ela é usada.
Alterações à EXPRESSÃO
O código do tipo EXPRESSION tem uma nova entrada possível, i, correspondente a expr.Index nós.
Classe Qiskit | Código do tipo | Carga Útil | Crianças |
|---|---|---|---|
Index | i | Nenhuma carga útil adicional. Os filhos são o alvo e o índice, nessa ordem. | 2 |
Versão 11
A versão 11 é idêntica à versão 10, exceto pelo seguinte. Primeiro, os nomes nos blocos CUSTOM_INSTRUCTION têm um sufixo do formato "_{uuid_hex}" , em que uuid_hex é uma cadeia hexadecimal de uuid, como a retornada por UUID.hex. Por exemplo: "b3ecab5b4d6a4eb6bc2b2dbf18d83e1e". Em segundo lugar, ele adiciona suporte a AnnotatedOperation objetos. A operação básica de uma operação anotada é armazenada usando o bloco INSTRUCTION, e um valor type adicional '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' é adicionado para indicar que o parâmetro é do tipo Modifier. Cada modificador é armazenado usando a estrutura MODIFIER.
Modificador
Isso representa Modifier
struct {
char type;
uint32_t num_ctrl_qubits;
uint32_t ctrl_state;
double power;
}Isso é suficiente para armazenar diferentes tipos de modificadores necessários para serializar objetos do tipo AnnotatedOperation. O campo type é 'i', 'c' ou 'p', representando se o modificador é, respectivamente, um modificador inverso, um modificador de controle ou um modificador de potência. No segundo caso, os campos num_ctrl_qubits e ctrl_state especificam a lógica de controle da operação de base e, no terceiro caso, o campo power representa a potência da operação de base.
Versão 10
A versão 10 adiciona suporte para:
- serialização nativa de symengine para objetos do tipo
ParameterExpressionbem como expressões simbólicas em blocos de programação Pulse. - novos campos na classe
TranspileLayoutadicionados na versão do Qiskit 0.45.0.
O campo symbolic_encoding é adicionado ao cabeçalho do arquivo e um novo tipo de codificação char é introduzido, mapeado para cada biblioteca simbólica da seguinte forma: p refere-se à codificação sympy e e refere-se à codificação symengine.
Alterações em FILE_HEADER
O conteúdo do FILE_HEADER após V10 é definido como uma estrutura C:
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;Alterações no LAYOUT
A estrutura LAYOUT é atualizada para ter um campo input_qubit_count adicional. Com a versão 10, a estrutura LAYOUT agora é:
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;
}O restante dos dados do layout após a estrutura LAYOUT é representado como nas versões anteriores. Se input qubit_count for < 0, isso indica que tanto _input_qubit_count quanto _output_qubit_list no TranspileLayout são None.
VERSION 9
A versão 9 adiciona suporte para nós clássicos Expr clássicos e seus respectivos nós Types.
Expressão
Um Expr é representado por um fluxo de dados de largura variável. O próprio nó é representado por (em ordem no fluxo de bytes):
- um discriminador de código de tipo de um byte;
- um objeto EXPR_TYPE;
- uma carga útil adicional específica do código de tipo;
- um número específico de código de tipo de cargas úteis EXPRESSION secundárias (o número delas está implícito no código de tipo e não é armazenado explicitamente).
Cada um deles está descrito na tabela a seguir:
Classe Qiskit | Código do tipo | Carga Útil | Crianças |
|---|---|---|---|
Var | x | Um EXPR_VAR. | 0 |
Value | v | Um EXPR_VALUE. | 0 |
Cast | c | Um _Bool que corresponde ao valor de implicit. | 1 |
Unary | u | Um uint8_t com o mesmo valor numérico que o Unary.Op. | 1 |
Binary | b | Um uint8_t com o mesmo valor numérico que o Binary.Op. | 2 |
EXPR_TYPE
A Type é codificado por um ASCII de byte único char que codifica o tipo de tipo, seguido por uma carga útil que varia de acordo com o tipo. Os códigos definidos são:
EXPR_VAR
Isso representa uma variável de tempo de execução de um Var nó. Eles são um código de tipo, seguido de uma carga útil específica do código de tipo:
Python classe | Código do tipo | Carga Útil |
|---|---|---|
Clbit | C | Um uint32_t index que é o índice do Clbit no circuito que o contém. |
ClassicalRegister | R | Um uint16_t reg_name_size, seguido por essa quantidade de bytes de dados da cadeia UTF-8 do nome do registro. |
EXPR_VALOR
Isso representa um objeto literal no sistema de tipos clássico, como um número inteiro. Atualmente, há muito poucos literais desse tipo. Eles são codificados como um código de tipo, seguido de uma carga útil específica do código de tipo.
Python tipo | Código do tipo | Carga Útil |
|---|---|---|
bool | b | Um _Bool value. |
int | i | Um uint8_t num_bytes, seguido pelo número inteiro codificado nesse número de bytes (ordem de rede) em uma representação de complemento de dois. |
Alterações às INSTRUÇÕES
Para dar suporte ao uso de Expr nós nos campos IfElseOp.condition, WhileLoopOp.condition e SwitchCaseOp.target, a estrutura INSTRUCTION é alterada de forma compatível com a ABI para sua definição anterior. A nova estrutura é a estrutura 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;
}onde a única alteração é que uma entrada uint8_t conditional_key substituiu _Bool has_conditional. Esse novo conditional_key assume os seguintes valores numéricos, com os seguintes efeitos:
Valor | Efeitos |
|---|---|
| 0 | A instrução tem seu campo .condition definido como None. Os campos conditional_reg_name_size e conditional_value devem ser ignorados. |
| 1 | A instrução tem seu campo .condition definido como um tuplo de 2 a Clbit ou a ClassicalRegistere um número inteiro de valor conditional_value. A carga útil da INSTRUÇÃO, incluindo seus dados finais, é analisada exatamente como seria nas versões do QPY inferiores a 8. |
| 2 | A instrução tem seu campo .condition definido como um Expr nó. Os campos conditional_reg_name_size e conditional_value devem ser ignorados. Os dados que seguem a estrutura são seguidos (como nas versões do QPY inferiores a 9) por name_size bytes de dados de cadeia UTF-8 para o nome da classe e label_size bytes de dados de cadeia UTF-8 para o rótulo (se houver). Em seguida, há um INSTRUCTION_PARAM, que conterá uma EXPRESSION. Depois disso, a análise continua com os structs INSTRUCTION_ARG, como nas versões anteriores do QPY. |
Alterações em INSTRUÇÃO_PARÂMETRO
Foi adicionado um novo código de tipo x que define um parâmetro EXPRESSION.
Versão 8
A versão 8 adiciona suporte para lidar com um TranspileLayout armazenado no atributo QuantumCircuit.layout atributo. Na versão 8, imediatamente após o bloco de calibrações no final da carga útil do circuito, há agora a estrutura LAYOUT . Essa estrutura descreve o tamanho dos três atributos de uma TranspileLayout classe.
LAYOUT
struct {
char exists;
int32_t initial_layout_size;
int32_t input_mapping_size;
int32_t final_layout_size;
uint32_t extra_registers;
}Se algum dos valores assinados for -1 , isso indica que o atributo correspondente é None.
Imediatamente após a estrutura LAYOUT , há uma estrutura REGISTERS para extra_registers (especificamente o formato introduzido na Versão 4 ) definições de registro autônomo que não estão presentes no circuito. Em seguida, há initial_layout_size INITIAL_LAYOUT_BIT structs para definir o TranspileLayout.initial_layout atributo.
BIT DE LAYOUT INICIAL
struct {
int32_t index;
int32_t register_size;
}Quando um valor de -1 indica None (como se nenhum registro estivesse associado ao bit). Após cada estrutura INITIAL_LAYOUT_BIT , há register_size bytes para uma cadeia de caracteres codificada em utf8 para o nome do registro.
Após o layout inicial, há uma matriz input_mapping_size de uint32_t inteiros que representam as posições do bit físico do layout inicial. Isso permite a construção de uma lista de bits virtuais em que o índice da matriz é sua posição de mapeamento de entrada.
Por fim, há uma matriz de final_layout_size uint32_t inteiros. Cada elemento é um índice no atributo qubits do circuito, que permite criar um mapeamento da posição inicial do qubit para a posição de saída no final do circuito.
Versão 7
A versão 7 adiciona suporte à instrução Reference e à serialização de um programa ScheduleBlock , mantendo sua referência a sub-rotinas:
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)O modelo de dados SCHEDULE_BLOCK convencional é preservado, mas na versão 7 ele é imediatamente seguido por um bloco extra de MAPEAMENTO utf8 bytes que representa os dados das sub-rotinas referenciadas.
Um novo caractere de chave de tipo foi adicionado ao grupo SCHEDULE_BLOCK_INSTRUCTIONS para a instrução Reference .
y:Referenceinstrução
Um novo caractere de chave de tipo é adicionado ao grupo SCHEDULE_BLOCK_OPERANDS para os operandos da instrução Reference , que é uma tupla de cadeias de caracteres, por exemplo, ( “cr45p”, “q0”, “q1” ).
o: string (string do operando)
Observe que essa é a mesma codificação da cadeia de caracteres incorporada Python. No entanto, a codificação de valor padrão no QPY usa o caractere do tipo s para dados de cadeia de caracteres, o que entra em conflito com o SymbolicPulse no escopo dos operandos da instrução de pulso. Um caractere de tipo especial o é reservado para os dados de cadeia que aparecem nos operandos da instrução de pulso.
Além disso, a versão 7 adiciona duas novas chaves de tipo à estrutura INSTRUCTION_PARM. "d" é seguido por nenhum dado e representa o valor literal CASE_DEFAULT para suporte a instruções de alternância. "R" representa um ClassicalRegister ou Clbite é seguido pelo mesmo formato da descrição do registro ou bit clássico usado no primeiro elemento da condição de um campo de INSTRUÇÃO.
Versão 6
A versão 6 adiciona suporte para ScalableSymbolicPulse. Esses objetos são salvos e lidos como objetos SymbolicPulse, e o nome da classe é adicionado aos dados para tratar corretamente a seleção de classe.
SymbolicPulse agora começa com o cabeçalho 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;
}A única alteração em relação à Versão 5 é a adição da classe_name_size. O cabeçalho é imediatamente seguido por class_name_size utf8 bytes com o nome da classe. Atualmente, há suporte para SymbolicPulse ou ScalableSymbolicPulse. O restante dos dados é idêntico à Versão 5.
Versão 5
A versão 5 foi modificada em relação à versão 4, adicionando suporte para ScheduleBlock e alterando duas cargas úteis: a carga útil de metadados INSTRUCTION e o bloco CUSTOM_INSTRUCTION. Eles agora têm novos campos para melhor considerar os ControlledGate objetos em um circuito. Além disso, o novo payload MAP_ITEM é definido para implementar o bloco MAPPING.
O suporte para representar programações de pulsos e calibrações personalizadas foi removido no Qiskit v2.0. Ao carregar cargas úteis QPY, esses campos de dados agora são ignorados ou geram um erro ao usar o Qiskit para deserialização.
No QPY versão 5 e superior,
struct {
char type;
}segue imediatamente o bloco de cabeçalho do arquivo para representar o tipo de programa armazenado no arquivo.
- Quando
type==c,QuantumCircuita carga útil segue - Quando
type==s, a carga útil deScheduleBlocké a seguinte
Programas diferentes não podem ser agrupados no mesmo arquivo. Você deve criar arquivos diferentes para tipos de programas diferentes. Vários objetos com o mesmo tipo podem ser salvos em um único arquivo.
CRONOGRAMA_BLOCO
ScheduleBlock é suportado pela primeira vez na versão 5 do QPY. Isso permite que os usuários salvem programas de pulso no formato binário QPY da seguinte forma:
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]Observe que o circuito e o bloco de programação são serializados e desserializados por meio da mesma interface QPY. O tipo de dados de entrada é analisado implicitamente e nenhuma opção extra é necessária para salvar o bloco de programação.
CHEDULE_BLOCK_HEADER
ScheduleBlock o bloco começa com o seguinte cabeçalho:
struct {
uint16_t name_size;
uint64_t metadata_size;
uint16_t num_element;
}que é imediatamente seguido por name_size utf8 bytes do nome do agendamento e metadata_size utf8 bytes do dicionário de metadados serializados JSON anexado ao agendamento.
ALINHAMENTOS DE BLOCOS DE AGENDAMENTO
Em seguida, o contexto de alinhamento do bloco de programação começa com char representando o tipo de contexto suportado, seguido pelo bloco SEQUENCE representando os parâmetros associados ao contexto de alinhamento AlignmentKind._context_params. O tipo de contexto char é mapeado para cada subclasse de alinhamento da seguinte forma:
l:AlignLeftr:AlignRights:AlignSequentiale:AlignEquispaced
Observe que não há suporte para o contexto AlignFunc devido à função de retorno de chamada armazenada nos parâmetros de contexto.
INSTRUÇÕES DO BLOCO DE AGENDA
Esse bloco de alinhamento é seguido por num_element elementos de bloco de comprimento que podem consistir em blocos de programação aninhados e instruções de programação. Cada instrução de programação começa com char representando o tipo de instrução, seguido pelo bloco SEQUENCE representando a instrução operands. Observe que a estrutura de dados do pulso Instruction é unificada para que a instância possa ser determinada exclusivamente pela classe e por uma tupla de operandos. O mapeamento do tipo char para a subclasse de instrução é definido da seguinte forma:
a:Acquireinstruçãop:Playinstruçãod:Delayinstruçãof:SetFrequencyinstruçãog:ShiftFrequencyinstruçãoq:SetPhaseinstruçãor:ShiftPhaseinstruçãob:RelativeBarrierinstruçãot:TimeBlockadeinstruçãoy: InstruçãoReference(nova na versão 0.7 )
PROGRAMAÇÃO_BLOCO_OPERANDOS
Os operandos dessas instâncias podem ser serializados por meio do mecanismo padrão de serialização de valores do QPY; no entanto, há tipos de objetos especiais que só aparecem nos operandos de programação. Como os operandos são serializados como SEQUENCE, cada elemento deve ser empacotado com a estrutura de empacotamento INSTRUCTION_PARAM, em que cada carga útil começa com um bloco de cabeçalho que consiste nos caracteres type e uint64_t size. Os objetos especiais começam com a seguinte chave de tipo:
c:Channelw:Waveforms:SymbolicPulseo: string (string de operando, novo na versão 0.7 )
Canal
O bloco de canais começa com o subtipo de canal char que mapeia os dados de um objeto para a subclasse Channel . O mapeamento é definido da seguinte forma:
d:DriveChannelc:ControlChannelm:MeasureChannela:AcquireChannele:MemorySlotr:RegisterSlot
A chave é imediatamente seguida pelo índice do canal serializado como INSTRUCTION_PARAM.
Forma de onda
O bloco de forma de onda começa com o cabeçalho WAVEFORM:
struct {
double epsilon;
uint32_t data_size;
_bool amp_limited;
}que é seguido por data_size bytes de ndarray binário complexo gerado por numpy.save. Isso representa os pontos de dados complexos de QI reproduzidos em um dispositivo quântico. name é salvo após as amostras na estrutura do pacote INSTRUCTION_PARAM, que pode ser uma string ou None.
SymbolicPulse
SymbolicPulse o bloco começa com o cabeçalho 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 é seguido por type_size utf8 bytes de SymbolicPulse.pulse_type string que representa uma classe de forma de onda, como "Gaussian" ou “GaussianSquare”. Em seguida, envelope_size, constraints_size, valid_amp_conditions_size utf8 bytes de expressões simbólicas serializadas são gerados para SymbolicPulse.envelope, SymbolicPulse.constraints e SymbolicPulse.valid_amp_conditions, respectivamente. Como a representação em cadeia dessas expressões geralmente é longa, a expressão binária é gerada pelo módulo zlib do python com compactação de dados.
Para especificar com exclusividade uma instância de pulso, também precisamos armazenar os parâmetros associados, que consistem em duration e o restante dos parâmetros como um dicionário. Os parâmetros do dicionário são primeiro despejados no formulário MAPPING e, em seguida, duration é despejado com a estrutura de pacote INSTRUCTION_PARAM. Por fim, name é salvo também com a estrutura do pacote INSTRUCTION_PARAM, que pode ser uma cadeia de caracteres ou None.
mapeamento
O MAPPING é uma representação de um objeto de mapeamento arbitrário. Trata-se de uma SEQUÊNCIA de comprimento fixo do par chave-valor representado pela carga útil MAP_ITEM.
Um MAP_ITEM começa com um cabeçalho definido como:
struct {
uint16_t key_size;
char type;
uint16_t size;
}que é imediatamente seguido pelos bytes key_size utf8 que representam a chave do dicionário em string e size utf8 bytes de dados de objeto arbitrário do QPY serializável type.
CALIBRAÇÕES DO CIRCUITO
O bloco CIRCUIT_CALIBRATIONS é um dicionário para definir calibrações de pulso do conjunto de instruções personalizadas. Esse bloco começa com o seguinte cabeçalho CALIBRATION:
struct {
uint16_t num_cals;
}que é seguido pelo comprimento num_cals das entradas de calibração, cada uma começa com o cabeçalho CALIBRATION_DEF:
struct {
uint16_t name_size;
uint16_t num_qubits;
uint16_t num_params;
char type;
}O cabeçalho da definição de calibração é seguido por name_size utf8 bytes do nome da porta, num_qubits comprimento dos inteiros que representam uma sequência de qubits e num_params comprimento da carga útil INSTRUCTION_PARAM para os parâmetros associados à instrução personalizada. O type indica a classe do programa de pulso que é, em princípio, ScheduleBlock ou Schedule. A partir da versão 5 do QPY, somente a carga útil ScheduleBlock é compatível. Por fim, a carga útil de SCHEDULE_BLOCK é empacotada para cada entrada CALIBRATION_DEF.
Instrução
O bloco INSTRUCTION foi modificado para adicionar dois novos campos num_ctrl_qubits e ctrl_state , que são usados para modelar o ControlledGate.num_ctrl_qubits e ControlledGate.ctrl_state atributos. O novo formato de estrutura compactada de carga útil é:
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;
}O restante da carga útil da instrução é o mesmo. Você pode consultar as INSTRUÇÕES para obter detalhes sobre a carga útil total.
INSTRUÇÃO PERSONALIZADA
O bloco CUSTOM_INSTRUCTION na versão 5 do QPY adiciona um novo campo base_gate_size que é usado para definir o tamanho do objeto armazenado no atributo qiskit.circuit.Instruction objeto armazenado no atributo ControlledGate.base_gate para um objeto personalizado ControlledGate personalizado. Com essa alteração, o bloco de metadados CUSTOM_INSTRUCTION se torna:
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
}Imediatamente após a estrutura CUSTOM_INSTRUCTION está o nome codificado utf8 de tamanho name_size.
Se custom_definition for True , isso significa que os bytes size imediatamente seguintes contêm dados de circuito QPY que podem ser usados para a definição personalizada dessa porta. Se custom_definition for False , a instrução poderá ser considerada opaca (ou seja, sem definição). O campo type determina que tipo de objeto será criado com a definição personalizada. Se for 'g' , será um objeto Gate objeto, 'i' será um Instruction objeto.
Em seguida, os próximos bytes base_gate_size contêm a carga útil INSTRUCTION para o ControlledGate.base_gate.
Além disso, um valor adicional para type é adicionado a 'c' , que é usado para indicar que a instrução personalizada é uma instrução personalizada ControlledGate.
Versão 4
A versão 4 é idêntica à versão 3, exceto pelo fato de adicionar duas novas cadeias de caracteres de tipo ao struct INSTRUCTION_PARAM, z para representar None (que é codificado como nenhum dado), q para representar um QuantumCircuit (que é codificado como um circuito QPY), r para representar um range de inteiros (que é codificado como um RANGE ) e t para representar um sequence (que é codificado conforme definido por SEQUENCE ). Além disso, a versão 4 altera o tipo de matriz de mapeamento de índice de registro de uint32_t para int64_t. Se os valores de qualquer um dos elementos da matriz forem negativos, eles representam um bit de registro que não está presente no circuito.
O formato do cabeçalho REGISTERS também foi atualizado para
struct {
char type;
_Bool standalone;
uint32_t size;
uint16_t name_size;
_bool in_circuit;
}que apenas adiciona o campo in_circuit que representa se o registro faz parte do circuito ou não.
INTERVALO
Um RANGE é uma representação de um objeto range . Ele é definido como:
struct {
int64_t start;
int64_t stop;
int64_t step;
}SEQUENCE
Uma SEQUENCE é uma representação de um objeto de sequência arbitrária. Como as sequências são apenas contêineres de comprimento fixo de objetos python arbitrários, o QPY delas não pode representar totalmente nenhuma sequência, mas desde que o conteúdo de uma sequência seja de outros tipos serializáveis do QPY para a carga útil INSTRUCTION_PARAM, o objeto sequence pode ser serializado.
Um parâmetro de instrução de sequência começa com um cabeçalho definido como:
struct {
uint64_t size;
}seguido por size elementos que são cargas úteis de INSTRUCTION_PARAM, em que cada um deles define um elemento na sequência. O objeto de sequência será convertido em um tipo adequado, por exemplo, tuple, depois.
Versão 3
A versão 3 do formato QPY é idêntica à versão 2, exceto pelo fato de definir um formato de estrutura para representar um PauliEvolutionGate nativamente no QPY. Para isso, a estrutura CUSTOM_DEFINITIONS agora suporta um novo tipo de valor 'p' para representar um PauliEvolutionGate. Os registros nas tabelas de instruções personalizadas têm nomes exclusivos gerados que começam com a sequência, "###PauliEvolutionGate_" seguida por uma sequência UUID. Esse nome de porta está reservado no QPY e, se você tiver um objeto Instruction personalizado com um conjunto de definições e esse prefixo de nome, ocorrerá um erro. Se for do tipo, 'p' a carga útil de dados é definida da seguinte forma:
PAULI_EVOLUÇÃO
Isso representa o alto nível PauliEvolutionGate
struct {
uint64_t operator_count;
_Bool standalone_op;
char time_type;
uint64_t time_size;
uint64_t synthesis_size;
}Isso é imediatamente seguido por operator_count elementos definidos pela carga útil SPARSE_PAULI_OP_LIST_ELEM. Em seguida, temos time_size bytes que representam o atributo time . Se standalone_op é True , então deve haver apenas um único operador. A codificação desses bytes é determinada pelo valor de time_type. Os valores possíveis de time_type são 'f', 'p' e 'e'. Se time_type é 'f' , é um double, 'p' define um Parameter que é representado por um PARÂMETRO, e define um objeto ParameterExpression (que não é um Parameter) que é representado por um PARAMETER_EXPR. Em seguida, vem synthesis_size bytes, que é um payload json codificado em utf8 que representa a EvolutionSynthesis classe usada pelo portão.
LISTA_OPERACIONAL_SPARSE_PAULI_ELEM
Isso representa uma instância de SparsePauliOp.
struct {
uint32_t pauli_op_size;
}que é imediatamente seguido por pauli_op_size bytes que são dados no formato.npy [2] que representam os dados SparsePauliOp.
A versão 3 do formato QPY também define um formato struct para representar um ParameterVectorElement como uma subclasse distinta de um Parameter. Isso adiciona um novo tipo de parâmetro char 'v' para representar um ParameterVectorElement que agora é suportado como um valor de cadeia de caracteres de tipo para um INSTRUCTION_PARAM. A carga útil desses parâmetros é definida abaixo como PARAMETER_VECTOR_ELEMENT.
PARÂMETRO_VETOR_ELEMENTO
Um PARAMETER_VECTOR_ELEMENT representa um ParameterVectorElement objeto, os dados de uma INSTRUÇÃO_PARAM. O conteúdo do PARAMETER_VECTOR_ELEMENT é definido como:
struct {
uint16_t vector_name_size;
uint64_t vector_size;
char uuid[16];
uint64_t index;
}que é imediatamente seguido por vector_name_size utf8 bytes que representam o nome do vetor do parâmetro.
PARÂMETRO_EXPR
Além disso, como a versão do formato QPY v3 faz distinção entre um Parameter e ParameterVectorElement a carga útil de a ParameterExpression precisa ser atualizada para distinguir entre os tipos. A seguir, o formato de carga útil modificado, que é praticamente idêntico ao formato da Versão 1 e da Versão 2, mas apenas modifica a estrutura map_elements para incluir um campo de tipo de símbolo.
Um PARAMETER_EXPR representa um ParameterExpression objeto que contém os dados de uma INSTRUÇÃO_PARAM. O conteúdo de um PARAMETER_EXPR é definido como:
struct {
uint64_t map_elements;
uint64_t expr_size;
}Imediatamente após o cabeçalho, há expr_size bytes de dados utf8 que contêm a string de expressão, que é a resposta simpática da expressão para a expressão do parâmetro. Em seguida, há um mapa de símbolos que contém map_elements elementos com o formato
struct {
char symbol_type;
char type;
uint64_t size;
}A symbol_type chave determina o tipo de carga útil da representação simbólica do elemento. Se for p , representa um Parameter ; e se for, v representa um ParameterVectorElement. A estrutura do elemento de mapa é seguida imediatamente pela carga útil da chave do mapa de símbolos; se symbol_type for p , ela é seguida imediatamente por um objeto PARAMETER (tanto os bytes da estrutura quanto os bytes do nome de utf8 ), e se symbol_type for v , a estrutura é seguida imediatamente por PARAMETER_VECTOR_ELEMENT (tanto os bytes da estrutura quanto os bytes do nome de utf8 ). Em seguida, vêm size os bytes correspondentes aos dados do símbolo. O formato dos dados depende do valor de type. Se type for p , então representa um Parameter e o tamanho será 0; o valor será simplesmente o mesmo que a chave. Da mesma forma, se for v``type , então representa um ParameterVectorElement e o tamanho será 0, pois o valor será exatamente igual à chave. Se type for, f então representa um número de precisão dupla. Se type for c , representa um número complexo de precisão dupla, que é representado pelo tipo COMPLEX. Por fim, se o tipo for, i ele representa um inteiro que é um int64_t.
Versão 2
A versão 2 do formato QPY é idêntica à versão 1, exceto pelo fato de a seção HEADER ser ligeiramente diferente. Consulte a seção Versão 1 para obter detalhes sobre o restante do formato do payload.
Cabeçalho
O conteúdo do HEADER é definido como uma estrutura 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;
}Isso é imediatamente seguido por name_size bytes de dados utf8 para o nome do circuito. Em seguida, há imediatamente global_phase_size bytes que representam a fase global. O conteúdo desses dados é ditado pelo valor de global_phase_type. Se for 'f' , os dados serão um float e terão o tamanho de um double. Se o site 'p' definir um objeto Parameter que é representado por uma estrutura PARAM (veja abaixo), e define um objeto ParameterExpression objeto (que não é um Parameter) que é representado por uma estrutura PARAM_EXPR (veja abaixo).
Versão 1
Cabeçalho
O conteúdo do HEADER, conforme definido como uma estrutura C, é:
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;
}Isso é imediatamente seguido por name_size bytes de dados utf8 para o nome do circuito.
Metadados
O campo METADATA é uma cadeia de caracteres JSON codificada em UTF8. Depois de ler o HEADER (que tem um tamanho fixo no início do arquivo QPY) e a cadeia de caracteres name , você lê o número de bytes metadata_size e analisa o JSON para obter os metadados do circuito.
Registros
O conteúdo de REGISTERS é um número de objeto REGISTER. Se num_registers for > 0, depois de ler METADATA, você lerá o número de estruturas REGISTER definidas como:
struct {
char type;
_Bool standalone;
uint32_t size;
uint16_t name_size;
}type pode ser 'q' ou 'c'.
Imediatamente após a estrutura REGISTER está o nome do registro codificado em utf8 de tamanho name_size. Após os bytes name utf8, há uma matriz de valores int64_t de tamanho size que contém um mapa do índice do registro para o índice de qubit do circuito. Por exemplo, o elemento de matriz 0’s é o índice da posição register[0]na lista de qubits do circuito que o contém.
Antes do QPY versão 4, o tipo de elementos da matriz era uint32_t. Isso foi alterado para permitir valores negativos que representam bits na matriz não presentes no circuito
O booleano autônomo determina se o registro é construído como um registro autônomo que foi adicionado ao circuito ou criado a partir de bits existentes. Um registro é considerado autônomo se tiver bits construídos exclusivamente como parte dele, por exemplo:
qr = QuantumRegister(2)
qc = QuantumCircuit(qr)o registro qr seria um registro autônomo. Enquanto algo como:
bits = [Qubit(), Qubit()]
qr2 = QuantumRegister(bits=bits)
qc = QuantumCircuit(qr2)qr2 teria standalone definido como False.
DEFINIÇÕES PERSONALIZADAS
Esta seção especifica definições personalizadas para qualquer uma das instruções do circuito.
O conteúdo do CUSTOM_DEFINITION_HEADER é definido como:
struct {
uint64_t size;
}Se o tamanho for maior que 0, isso significa que o circuito contém instruções personalizadas. Cada instrução personalizada é definida com um bloco 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;
}Imediatamente após a estrutura CUSTOM_INSTRUCTION está o nome codificado utf8 de tamanho name_size.
Se custom_definition for True , isso significa que os bytes size imediatamente seguintes contêm dados de circuito QPY que podem ser usados para a definição personalizada dessa porta. Se custom_definition for False , a instrução poderá ser considerada opaca (ou seja, sem definição). O campo type determina que tipo de objeto será criado com a definição personalizada. Se for 'g' , será um objeto Gate objeto, 'i' será um Instruction objeto.
INSTRUÇÕES
O conteúdo do INSTRUCTIONS é uma lista de objetos de metadados do 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;
}Esse objeto de metadados é imediatamente seguido por name_size bytes de utf8 bytes para o name. name aqui está o nome da classe Qiskit para a classe Instruction, se ela estiver definida no Qiskit. Caso contrário, ele volta para o nome da instrução personalizada. Após os bytes name , há bytes label_size de dados utf8 para o rótulo, caso tenha sido definido na instrução. Após os bytes de rótulo, se has_conditional for True , haverá conditional_reg_name_size bytes de dados de utf8 para o nome do registro condicional. No caso de condições de bit clássico único, os dados do nome do registro utf8 serão prefixados com um caractere nulo “x00” e, em seguida, com um número inteiro da cadeia utf8 que representa o índice do bit clássico no circuito em que a condição está.
Isso é imediatamente seguido pelos structs INSTRUCTION_ARG para a lista de argumentos dessa instrução. Eles estão na ordem de todos os argumentos quânticos (há num_qargs deles) seguidos por todos os argumentos clássicos (num_cargs deles).
O conteúdo de cada INSTRUCTION_ARG é:
struct {
char type;
uint32_t index;
}type pode ser 'q' ou 'c'.
Após todos os argumentos de uma instrução, os parâmetros são especificados com as estruturas num_parameters INSTRUCTION_PARAM.
O conteúdo de cada INSTRUCTION_PARAM é:
struct {
char type;
uint64_t size;
}Após cada INSTRUCTION_PARAM, os próximos size bytes são os dados do parâmetro. O campo type pode ser 'i', 'f', 'p', 'e', 's', 'c' ou 'n' , que determinam o formato. Para 'i' é um inteiro, 'f' é um duplo, 's' se for uma cadeia de caracteres (codificada como utf8 ), 'c' é um complexo e os dados são representados pelo formato struct na seção PARAMETER_EXPR. 'p' define um Parameter que é representado por uma estrutura PARAMETER, e define um objeto ParameterExpression (que não é um Parameter) que é representado por um PARAMETER_EXPR struct (no formato QPY Versão 3, o formato é ligeiramente ajustado, consulte: PARAMETER_EXPR ), 'n' representa um objeto de numpy (um ndarray ou um tipo numpy), o que significa que os dados são dados de formato.npy [2] e, no QPY Versão 3, 'v' representa um ParameterVectorElement que é representado por uma estrutura PARAMETER_VECTOR_ELEMENT.
Parâmetro
Um PARÂMETRO representa um Parameter objeto, os dados de uma INSTRUÇÃO_PARAM. O conteúdo do PARÂMETRO é definido como:
struct {
uint16_t name_size;
char uuid[16];
}que é imediatamente seguido por name_size utf8 bytes que representam o nome do parâmetro.
PARÂMETRO_EXPR
Um PARAMETER_EXPR representa um ParameterExpression objeto que contém os dados de uma INSTRUÇÃO_PARAM. O conteúdo de um PARAMETER_EXPR é definido como:
Os dados do PARAMETER_EXPR começam com um cabeçalho:
struct {
uint64_t map_elements;
uint64_t expr_size;
}Imediatamente após o cabeçalho, há expr_size bytes de dados utf8 que contêm a string de expressão, que é a resposta simpática da expressão para a expressão do parâmetro. Em seguida, há um mapa de símbolos que contém map_elements elementos com o formato
struct {
char type;
uint64_t size;
}Que é seguido imediatamente pelo objeto PARAMETER (tanto a estrutura quanto os bytes de nome utf8 ) para a chave do mapa de símbolos. Isso é seguido por size bytes para os dados do símbolo. O formato dos dados depende do valor de type. Se type for p , ele representa um Parameter e o tamanho será 0, o valor será apenas o mesmo da chave. Se type for f , ele representará um float de precisão dupla. Se type for c , ele representa um complexo de precisão dupla, que é representado por COMPLEX. Por fim, se o tipo for i , ele representará um número inteiro que é um int64_t.
COMPLEXO
Ao representar um valor complexo de precisão dupla no QPY, a seguinte estrutura é usada:
struct {
double real;
double imag;
}isso corresponde à representação interna em C do tipo complexo de Python. [3]
Referências
[ 1 ]
https://tools.ietf.org/html/rfc1700
https://numpy.org/doc/stable/reference/generated/numpy.lib.format.html
[3 ]