Skip to main content
IBM Quantum Platform

Annotazioni sui circuiti

qiskit.circuit.annotation

Questo modulo contiene l'infrastruttura per lavorare con le annotazioni dei circuiti personalizzati.

qiskit.circuit.AnnotationLa classe principale a cui si rivolgono gli utenti è la classe base, che viene anche riesportata da questo modulo.


Sottoclassi di annotazione personalizzate

La Annotation classe è pensata per essere sottoclassificata. Le sottoclassi devono impostare il proprio namespace campo. Questo può essere specifico per un'istanza oppure statico per un'intera sottoclasse. Lo spazio dei nomi viene utilizzato nell'ambito del meccanismo di invio, come descritto nella sezione "Spazi dei nomi".

I controlli di uguaglianza dei circuiti confrontano anche le annotazioni sugli oggetti in modo dipendente dall'ordine. Probabilmente si vorrà implementare il metodo __eq__() su tutte le sottoclassi.

Se si vuole che la propria annotazione possa essere serializzata tramite QPY o :ref:` OpenQASM 3 <qiskit-qasm3> `, è necessario fornire implementazioni separate dei metodi di serializzazione e deserializzazione, come discusso in Serializzazione e deserializzazione.

Spazi dei nomi

Lo "spazio dei nomi" di un'annotazione è usato come chiave di ricerca quando un consumatore decide quale gestore invocare. Questo include i contesti di serializzazione QPY e OpenQASM 3, ma in generale i passaggi del transpiler guarderanno anche gli spazi dei nomi delle annotazioni per determinare se sono rilevanti e così via.

Può trattarsi di un identificatore standard di tipo Python (ad es. my_namespace), oppure un elenco di identificatori separati da punti (ad es. my_namespace.subnamespace). Lo spazio dei nomi viene utilizzato da tutti gli utenti delle annotazioni per determinare quale gestore debba essere richiamato.

Una funzione indipendente consente di iterare gli spazi dei nomi e gli spazi dei nomi dei genitori in ordine di priorità, dal più specifico al meno specifico.

iter_namespaces

qiskit.circuit.annotation.iter_namespaces(namespace)

GitHub

Un iteratore su tutti gli spazi dei nomi che possono essere usati per cercare lo spazio dei nomi dato.

Questo include lo spazio dei nomi e tutti i genitori, compreso lo spazio dei nomi radice a stringa vuota.

Esempi:

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

Parametri

namespace (str)

Tipo di restituzione

Iteratore [ str ]

Serializzazione e deserializzazione

Le annotazioni rappresentano dati completamente personalizzati, che possono persistere dopo la compilazione. Questo può includere dati che devono essere serializzati per un consumo successivo, come i dati aggiuntivi che vengono interpretati da un backend-compiler. Il formato binario nativo QPY di Qiskit (vedi qiskit.qpy) supporta il concetto di annotazioni arbitrarie nei suoi payload dalla versione 15 in poi. In OpenQASM 3 (vedere qiskit.qasm3), le annotazioni sono una caratteristica fondamentale del linguaggio e il supporto di importazione/esportazione di Qiskit per OpenQASM 3 include la serializzazione delle annotazioni.

Tuttavia, poiché le annotazioni sono generalmente sottoclassi personalizzate e non sono riconosciute da Qiskit, non è possibile disporre di un supporto integrato per la serializzazione. Per quanto riguarda la deserializzazione, Qiskit, in generale, non disporrà di un oggetto esistente Annotation da cui richiamare i metodi di deserializzazione. Si prevede inoltre che le annotazioni possano riferirsi a uno stato condiviso, sconosciuto a Qiskit, all’interno di un determinato contesto circuitale.

Per tutti questi motivi, la serializzazione e la deserializzazione delle annotazioni è gestita da oggetti personalizzati, che devono essere passati ai punti di interfaccia delle funzioni di serializzazione pertinenti. Ad esempio, in QPY, l'argomento annotation_factories in qpy.dump() e qpy.load() sono usati per passare i serializzatori.

QPYSerializer

class qiskit.circuit.annotation.QPYSerializer

GitHub

Basi: ABC

L'interfaccia per i serializzatori e i deserializzatori di Annotation oggetti in QPY.

Per ulteriori informazioni sul QPY, vedere qiskit.qpy.

Questa classe di definizione dell'interfaccia è progettata per essere sottoclassata. I singoli metodi descrivono i loro contratti e il modo in cui verranno richiamati.

Durante la serializzazione e la deserializzazione di QPY, la logica principale di QPY richiama una funzione di fabbrica per creare istanze di sottoclassi di questa classe. Il valore di ritorno di una data funzione di fabbrica sarà utilizzato in un contesto di serializzazione o di deserializzazione, ma non in entrambi.

La struttura delle chiamate durante la serializzazione di un singolo circuito è la seguente:

  1. molte chiamate a dump_annotation(), che condivideranno tutte lo stesso namespace argomento, il quale sarà sempre un prefisso (non stretto) di tutti gli Annotation oggetti specificati.
  2. una chiamata a dump_state().

La struttura generale delle chiamate durante la deserializzazione di un singolo circuito da un payload QPY è la seguente:

  1. una chiamata a load_state(), passando un namespace (con lo stesso comportamento di prefisso non rigido della forma "serializzazione").
  2. molte chiamate a load_annotation(), corrispondenti alle annotazioni serializzate sotto il prefisso dello spazio dei nomi.

Quando si sottoclasse, si tenga presente che QPY è destinato ad avere rigorose garanzie di retrocompatibilità e si raccomanda vivamente che le sottoclassi di serializzazione delle annotazioni le mantengano. In particolare, si suggerisce che qualsiasi serializzatore non banale includa informazioni sulla "versione" del serializzatore nel suo "stato" totale (vedere dump_state()) e che la deserializzazione faccia ogni sforzo per supportare la retrocompatibilità con versioni precedenti dello stesso serializzatore.

QPYFromOpenQASM3Serializer

class qiskit.circuit.annotation.QPYFromOpenQASM3Serializer(inner)

GitHub

Basi: QPYSerializer

Un adattatore che converte un OpenQASM3Serializer in un QPYSerializer.

Questo funziona perché i serializzatori di annotazioni di OpenQASM 3 devono essere stateless e restituire UTF-8-encoded singole righe di testo, che è un sottoinsieme di ciò che QPY permette.

Di solito si crea uno di questi metodi usando il metodo as_qpy() di un serializzatore di annotazioni OpenQASM 3.

Esempi:

Le istanze di questa classe possono essere chiamate come una funzione a zero argomenti e restituire se stesse. Ciò consente di utilizzarli direttamente come funzione di fabbrica per i punti di ingresso di QPY, come ad esempio:

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()})

Questo è sicuro, senza restituire istanze separate, perché i serializzatori di base di OpenQASM 3 sono necessariamente stateless.

Parametri

inner (OpenQASM3Serializer) – il serializzatore OpenQASM 3 da cui è derivato.

OpenQASM3Serializer

class qiskit.circuit.annotation.OpenQASM3Serializer

GitHub

Basi: ABC

L'interfaccia per i serializzatori e i deserializzatori di Annotation oggetti in OpenQASM e 3.

Per ulteriori informazioni sul supporto di OpenQASM 3 in Qiskit, vedere qiskit.qasm3.

Questa classe di definizione dell'interfaccia è progettata per essere sottoclassata. OpenQASM 3 Le annotazioni sono prive di stato all'interno di un programma, pertanto una sottoclasse non deve tenere traccia dello stato.


Esempi

Un passaggio del transpiler per la raccolta dei blocchi

Un obiettivo principale del framework di annotazione è quello di consentire la memorizzazione di analisi e comandi personalizzati sui circuiti in modo locale alle istruzioni, sia da parte dell'utente all'ingresso del compilatore, sia per un passaggio del compilatore per memorizzare le informazioni da utilizzare successivamente.

Ad esempio, possiamo scrivere un semplice passaggio di transpiler che raccolga sequenze di operazioni su un singolo qubit, inserisca ciascuna sequenza in un BoxOp, calcoli l’azione unitaria totale e la aggiunga come annotazione personalizzata, in modo che non sia necessario ripetere la stessa analisi in seguito, anche se i meccanismi interni di ciascun blocco vengono ottimizzati.

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

Per serializzare l'annotazione in OpenQASM 3, dobbiamo definire una logica personalizzata, poiché l'analisi stessa è interamente personalizzata. La serializzazione è separata dall'annotazione; ci possono essere circostanze in cui la serializzazione deve essere fatta in modo diverso.

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))

Infine, il tutto può essere messo insieme, mostrando l'output 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];
}
Questa pagina è stata utile?
Segnala un bug, un errore di battitura o richiedi contenuti su GitHub.