Skip to main content
IBM Quantum Platform

Anotações do circuito

qiskit.circuit.annotation

Este módulo contém a infraestrutura para trabalhar com anotações de circuito personalizadas.

A principal classe voltada para o usuário é a classe base qiskit.circuit.Annotationque também é reexportada desse módulo.


Subclasses de anotação personalizadas

A classe Annotation destina-se a ser uma subclasse. As subclasses devem definir seu campo namespace campo. Isso pode ser específico para uma instância ou estático para toda uma subclasse. O namespace é usado como parte do mecanismo de despacho, conforme descrito em Namespacing.

As verificações de igualdade de circuitos também comparam anotações em objetos de maneira dependente da ordem. Você provavelmente desejará implementar o método __eq__() em qualquer subclasse.

Se você pretende que sua anotação possa ser serializada via QPY ou :ref:` OpenQASM 3 <qiskit-qasm3> `, deverá fornecer implementações separadas dos métodos de serialização e desserialização, conforme discutido em Serialização e desserialização.

Espaço de nomes

O "namespace" de uma anotação é usado como uma chave de pesquisa quando qualquer consumidor está decidindo qual manipulador invocar. Isso inclui os contextos de serialização QPY e OpenQASM 3, mas, em geral, as passagens do transpilador também examinarão os namespaces das anotações para determinar se são relevantes e assim por diante.

Pode ser um identificador padrão do tipo Python (por exemplo, my_namespace), ou uma lista de identificadores separados por pontos (por exemplo, my_namespace.subnamespace). O namespace é utilizado por todos os usuários de anotações para determinar qual manipulador deve ser invocado.

Uma função autônoma permite iterar pelos namespaces e namespaces pai em ordem de prioridade, do mais específico para o menos específico.

iter_namespaces

qiskit.circuit.annotation.iter_namespaces(namespace)

GitHub

Um iterador sobre todos os namespaces que podem ser usados para pesquisar o namespace fornecido.

Isso inclui o namespace e todos os pais, inclusive o namespace raiz de string vazia.

Exemplos:

from qiskit.circuit.annotation import iter_namespaces
assert list(iter_namespaces("hello.world")) == ["hello.world", "hello", ""]

Parâmetros

namespace (str)

Tipo de retorno

Iterador [ str ]

Serialização e desserialização

As anotações representam dados totalmente personalizados, que podem persistir após a compilação. Isso pode incluir dados que devem ser serializados para consumo posterior, como dados adicionais que são interpretados por um compilador de back-end. O formato binário nativo QPY do Qiskit (consulte qiskit.qpy) suporta o conceito de anotações arbitrárias em seus payloads a partir da versão 15. Em OpenQASM 3 (consulte qiskit.qasm3), as anotações são um recurso essencial da linguagem, e o suporte de importação/exportação do Qiskit para OpenQASM 3 inclui a serialização de anotações.

No entanto, como as anotações geralmente são subclasses personalizadas e desconhecidas do Qiskit, não podemos ter suporte integrado para serialização. Na frente da desserialização, o Qiskit, em geral, não terá um objeto Annotation para chamar os métodos de desserialização. Espera-se também que as anotações possam estar relacionadas a algum estado compartilhado desconhecido para o Qiskit em um determinado contexto de circuito.

Por todos esses motivos, a serialização e a desserialização de anotações são tratadas por objetos personalizados, que devem ser passados nos pontos de interface das funções de serialização relevantes. Por exemplo, no QPY, o argumento annotation_factories em qpy.dump() e qpy.load() são usados para passar serializadores.

QPYSerializador

class qiskit.circuit.annotation.QPYSerializer

GitHub

Bases: ABC

A interface para serializadores e desserializadores de objetos Annotation objetos para o QPY.

Para obter mais informações sobre o QPY, consulte qiskit.qpy.

Essa classe de definição de interface foi projetada para ser subclasse. Os métodos individuais descrevem seus contratos e como eles serão chamados.

Durante a serialização e a desserialização do QPY, a lógica principal do QPY chamará uma função de fábrica para criar instâncias de subclasses dessa classe. O valor de retorno de uma determinada função de fábrica será usado em um contexto de serialização ou desserialização, mas não em ambos.

A estrutura das chamadas durante a serialização de um único circuito é a seguinte:

  1. muitas chamadas para dump_annotation(), que compartilharão o mesmo argumento namespace , que sempre será um prefixo (não restrito) de todos os Annotation objetos fornecidos.
  2. uma chamada para dump_state().

A estrutura geral das chamadas durante a desserialização de um único circuito de uma carga útil QPY é:

  1. uma chamada para load_state(), passando um namespace (com o mesmo comportamento de prefixação não restrito da forma de "serialização").
  2. muitas chamadas para load_annotation(), correspondentes a anotações serializadas sob essa pesquisa de prefixo de espaço de nome.

Ao criar essa subclasse, lembre-se de que o QPY deve ter garantias rigorosas de compatibilidade com versões anteriores, e é altamente recomendável que as subclasses de serialização de anotações mantenham isso. Em particular, sugere-se que qualquer serializador não trivial inclua informações de "versão" para o serializador em seu "estado" total (consulte dump_state()), e a desserialização deve fazer todos os esforços para oferecer suporte à compatibilidade com versões anteriores do mesmo serializador.

QPYFromOpenQASM3Serializer

class qiskit.circuit.annotation.QPYFromOpenQASM3Serializer(inner)

GitHub

Bases: QPYSerializer

Um adaptador que converte um OpenQASM3Serializer em um QPYSerializer.

Isso funciona porque os serializadores de anotações do OpenQASM 3 devem ser sem estado e retornar UTF-8-encoded linhas únicas de texto, que é um subconjunto do que o QPY permite.

Normalmente, você cria um deles usando o método as_qpy() de um serializador de anotações OpenQASM 3.

Exemplos:

As instâncias dessa classe podem ser chamadas como uma função de argumento zero e retornar a si mesmas. Isso permite que você os use diretamente como uma função de fábrica para os pontos de entrada do QPY, como:

import io
from qiskit.circuit import OpenQASM3Serializer, Annotation
from qiskit import qpy

class MyAnnotation(Annotation):
    namespace = "my_namespace"

class MySerializer(OpenQASM3Serializer):
    def dump(self, annotation):
        if not isinstance(annotation, MyAnnotation):
            return NotImplemented
        return ""

    def load(self, namespace, payload):
        assert namespace == "my_namespace"
        assert payload == ""
        return MyAnnotation()

qc = QuantumCircuit(2)
with qc.box(annotations=[MyAnnotation()]):
    qc.cx(0, 1)

with io.BytesIO() as fptr:
    qpy.dump(fptr, qc, annotation_serializers = {"my_namespace": MySerializer().as_qpy()})

Isso é seguro, sem retornar instâncias separadas, porque os serializadores da base OpenQASM 3 são necessariamente sem estado.

Parâmetros

inner (OpenQASM3Serializer) – o serializador OpenQASM 3 do qual ele é derivado.

OpenQASM3Serializer

class qiskit.circuit.annotation.OpenQASM3Serializer

GitHub

Bases: ABC

A interface para serializadores e desserializadores de Annotation objetos para OpenQASM 3.

Para obter mais informações sobre o suporte ao OpenQASM 3 no Qiskit, consulte qiskit.qasm3.

Essa classe de definição de interface foi projetada para ser subclasse. OpenQASM 3 são stateless em um programa, portanto, uma subclasse não deve rastrear o estado.


Exemplos

Uma passagem do transpiler de coleção de blocos

Um dos principais objetivos da estrutura de anotação é permitir que análises e comandos personalizados sejam armazenados em circuitos de maneira local, seja pelo usuário na entrada do compilador ou para que uma passagem do compilador armazene informações para consumo posterior.

Por exemplo, podemos escrever uma passagem de transpilador simples que coleta execuções de operações de um único qubit e coloca cada execução em um BoxOpe, em seguida, calcula a ação unitária total e a anexa como uma anotação personalizada, de modo que a mesma análise não precise ser repetida posteriormente, mesmo que os componentes internos de cada bloco sejam otimizados.

from qiskit.circuit import annotation, QuantumCircuit, BoxOp
from qiskit.quantum_info import Operator
from qiskit.transpiler import TransformationPass

class PerformsUnitary(annotation.Annotation):
    namespace = "unitary"
    def __init__(self, matrix):
        self.matrix = matrix

class Collect1qRuns(TransformationPass):
    def run(self, dag):
        for run in dag.collect_1q_runs():
            block = QuantumCircuit(1)
            for node in run:
                block.append(node.op, [0], [])
            box = BoxOp(block, annotations=[PerformsUnitary(Operator(block).data)])
            dag.replace_block_with_op(run, box, {run[0].qargs[0]: 0})
        return dag

Para serializar a anotação em OpenQASM 3, precisamos definir uma lógica personalizada, pois a análise em si é totalmente personalizada. A serialização é separada da anotação; pode haver circunstâncias em que a serialização deva ser feita de forma diferente.

import ast
import numpy as np

class Serializer(annotation.OpenQASM3Serializer):
    def dump(self, annotation):
        if annotation.namespace != "unitary":
            return NotImplemented
        line = lambda row: "[" + ", ".join(repr(x) for x in row) + "]"
        return "[" + ", ".join(line(row) for row in annotation.matrix.tolist()) + "]"

    def load(self, namespace, payload):
        if namespace != "unitary":
            return NotImplemented
        return PerformsUnitary(np.array(ast.literal_eval(payload), dtype=complex))

Por fim, tudo isso pode ser montado, mostrando o resultado OpenQASM 3.

from qiskit import qasm3

qc = QuantumCircuit(3)
qc.s(0)
qc.t(0)
qc.y(1)
qc.x(1)
qc.h(2)
qc.s(2)
collected = Collect1qRuns()(qc)

handlers = {"unitary": Serializer()}
dumped = qasm3.dumps(collected, annotation_handlers=handlers)
print(dumped)
OPENQASM 3.0;
include "stdgates.inc";
qubit[3] q;
@unitary[[(1+0j), 0j], [0j, (-0.7071067811865475+0.7071067811865475j)]]
box {
  s q[0];
  t q[0];
}
@unitary[[1j, 0j], [0j, -1j]]
box {
  y q[1];
  x q[1];
}
@unitary[[(0.7071067811865475+0j), (0.7071067811865475+0j)], [0.7071067811865475j, -0.7071067811865475j]]
box {
  h q[2];
  s q[2];
}
Esta página foi útil?
Relate um bug, erro de digitação ou solicite conteúdo no GitHub.