Skip to main content
IBM Quantum Platform

Transpilador

qiskit.transpiler


Visión general

Nota

Si usted ya está familiarizado con los conceptos de transpilación / compilación de circuitos, es posible que desee saltar hacia adelante a:

La transpilación es el proceso de reescribir un circuito de entrada dado para que se ajuste a la topología de un dispositivo cuántico específico, y/o para optimizar el circuito para su ejecución en sistemas cuánticos.

La mayoría de los circuitos deben someterse a una serie de transformaciones que los hagan compatibles con un determinado dispositivo de destino y los optimicen para reducir los efectos del ruido en los resultados obtenidos. Reescribir los circuitos cuánticos para adaptarlos a las limitaciones del hardware y optimizar su rendimiento puede no ser tarea fácil. El flujo de la lógica en la cadena de herramientas de reescritura no tiene por qué ser lineal, y a menudo puede tener sub-bucles iterativos, ramas condicionales y otros comportamientos complejos. Dicho esto, el flujo de compilación estándar sigue la estructura que se indica a continuación:

El proceso de transpilación toma el circuito de entrada, aplica los pases de transpilación y, a continuación, produce el circuito de salida.

Qiskit utiliza la representación DAGCircuit intermedia (RI) de un circuito en toda la pila del transpilador, en lugar de la representación basada en el árbol QuantumCircuit. Un transpiler pipeline es un PassManager cuyo método PassManager.run() recibe un archivo QuantumCircuit y lo convierte en a DAGCircuity luego somete al IR a una secuencia de pasadas, devolviendo finalmente a QuantumCircuit de vuelta. Un pass es un AnalysisPassque calcula y almacena propiedades sobre el circuito en la base de datos stateful PropertySeto un TransformationPassque modifica el IR para alcanzar un objetivo particular. Se puede pensar en un pipeline dividido en "etapas", donde cada etapa es responsable de una transformación de alto nivel.

Qiskit expone un constructor de tuberías de transpilación por defecto utilizando la función generate_preset_pass_manager(). Esto devuelve una tubería correctamente configurada para la transpilación completa, en un optimization_level elegido (entre 0 y 3, ambos inclusive). A menos que busque algo muy especializado, este es casi con toda seguridad el punto de entrada que desea. Un ejemplo de transpilación tiene este aspecto:

from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService

# Any abstract circuit you want:
abstract = QuantumCircuit(2)
abstract.h(0)
abstract.cx(0, 1)

# Any method you like to retrieve the backend you want to run on:
backend = QiskitRuntimeService().backend("some-backend")

# Create the pass manager for the transpilation ...
pm = generate_preset_pass_manager(backend=backend)
# ... and use it (as many times as you like).
physical = pm.run(abstract)

En los primeros experimentos sobre tolerancia a fallos, la función generate_preset_pass_manager() activa un canal de transpilación especializado cuando la base de destino está formada por puertas Clifford+T; véase clifford_t_pass_manager() la documentación. Por ejemplo:

from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library import QFTGate
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.quantum_info import get_clifford_gate_names

# Any abstract circuit you want:
abstract = QuantumCircuit(4)
abstract.append(QFTGate(4), [0, 1, 2, 3])

# Use all Clifford+T basis gates
basis_gates = get_clifford_gate_names() + ["t", "tdg"]

# Create and run the pass manager
pm = generate_preset_pass_manager(basis_gates=basis_gates)
transpiled = pm.run(abstract)

Para la mayoría de los casos, esto es todo lo que necesitas. No obstante, toda la infraestructura del transpilador de Qiskit es altamente extensible y configurable. El resto de esta página detalla cómo aprovechar las capacidades de bajo nivel de la pila del transpilador.


Administradores de pases preestablecidos

La función generate_preset_pass_manager() crea los "gestores de pases predefinidos". Todos ellos son instancias de PassManagerpor lo que se utilizan pasando un QuantumCircuit al método PassManager.run() al método Más concretamente, los gestores de pases predefinidos son instancias de StagedPassManagerque permiten una mayor configuración de las etapas individuales de una transpilación, incluidos los ganchos previos y posteriores a la etapa.

Un gestor de pases predefinidos tiene hasta seis etapas con nombre. A continuación se resumen, por orden de ejecución, con información más detallada en los siguientes subapartados.

init

Optimizaciones de circuitos abstractos y reducción de operaciones multiqubit a operaciones de uno y dos qubits. Véase Etapa de inicialización para más detalles.

layout

Elige un mapeo inicial de qubits virtuales a qubits físicos, incluyendo la expansión del circuito para contener ancillas explícitas. Esta etapa a veces subsume routing. Para más información, consulte la fase de diseño.

routing

Inserte puertas en el circuito para asegurarse de que se ajusta a las restricciones de conectividad del Target. Las puertas insertadas no tienen por qué coincidir aún con el ISA de destino, por lo que a menudo son sólo instrucciones de swap . Esta etapa a veces se omite, cuando la etapa layout realiza su trabajo. Consulte Etapa de enrutamiento para obtener más detalles.

translation

Convertir todas las puertas del circuito en unas que coincidan con el ISA del Target. Para más información, consulte la fase de traducción.

optimization

Optimizaciones de bajo nivel que tienen en cuenta el hardware. A diferencia de las optimizaciones abstractas de la etapa init , esta etapa actúa sobre un circuito físico. Consulte la etapa de optimización para obtener más detalles.

scheduling

Inserte Delay para hacer explícita la temporización de un circuito. Esto también puede incluir técnicas de reducción de errores en línea conscientes del hardware, como el desacoplamiento dinámico, que dependen del conocimiento de los tiempos del reloj de pared. Véase Etapa de programación para más detalles.

Los conductos de transpilador preestablecidos también pueden configurarse a alto nivel estableciendo un optimization_level. Es un número entero de 0 a 3, ambos inclusive, que indica el esfuerzo relativo que hay que realizar para intentar optimizar el circuito para el hardware. El nivel 0 desactiva todas las optimizaciones innecesarias; sólo se utilizan las transformaciones necesarias para que el circuito sea ejecutable. En el otro extremo, el nivel 3 permite toda una serie de técnicas de optimización, algunas de las cuales pueden resultar muy costosas en tiempo de compilación. Al igual que ocurre con los compiladores clásicos, no siempre se garantiza que el nivel de optimización 3 produzca los mejores resultados. Qiskit utiliza por defecto el nivel de optimización 2, como compensación entre el tiempo de compilación y la cantidad de optimización esperada.

El nivel de optimización afecta a las implementaciones que se utilizan por defecto para una etapa determinada, aunque esto se puede anular pasando argumentos explícitos de <stage>_method="<choice>" a generate_preset_pass_manager().

Reproducibilidad de los procesos predefinidos

La compilación cuántica suele implicar la resolución de problemas cuya complejidad se sabe que no es polinomial y que, por lo tanto, resultan imposibles de optimizar globalmente. En estos casos, los algoritmos estocásticos y heurísticos suelen ser más adecuados. Sin embargo, esto plantea problemas de reproducibilidad.

Los gestores de pases preestablecidos casi siempre incluyen pases estocásticos basados en heurística. Si necesita garantizar la reproducibilidad de una compilación, pase un número entero conocido al argumento seed_transpiler de las funciones generadoras.

Todos los complementos integrados en Qiskit deben generar sus análisis y modificar el código DAGCircuit de forma determinista si su aleatorización (en su caso) tiene una semilla, de modo que la compilación pueda repetirse posteriormente. Hay algunas limitaciones al respecto:

  • Todos los pases incorporados con componentes estocásticos deben proporcionar una forma de sembrar la aleatoriedad, y si se siembran, deben respetar las reglas de la salida determinista.
  • Todos los pases incorporados sin componentes estocásticos deben respetar las reglas de salida determinista para una entrada idéntica. Es permisible mantener un caché por eficiencia, pero dado el mismo conjunto de entradas, los retornos del pase deben ser los mismos si el pase es llamado múltiples veces, a menos que algo fuera del control del pase mute una de sus entradas en el lugar (por ejemplo, el pase BasisTranslator utiliza el valor SessionEquivalenceLibrary por defecto en los gestores de pases predefinidos, y no es un error obtener resultados diferentes si se añaden nuevas entradas a la biblioteca de equivalencias). Una "salida" es cualquier cosa que el pase escribe para su posterior consumo; puede ser el valor explícito return del pase, pero también incluye propiedades destinadas a su posterior consumo en el archivo PropertySet.
  • La salida de un pase debe ser determinista en una máquina dada para una versión dada de Qiskit y un entorno congelado, no importa cuántos hilos están disponibles para el pase. Muchos pases incorporados en Qiskit utilizan concurrencia de hilos, y no se les permite tener un comportamiento diferente basado en el número de hilos.
  • No se requiere que la salida de un pase para una semilla fija sea igual si alguna parte del entorno subyacente Python cambia (como la actualización de un paquete dependiente), o si las bibliotecas matemáticas del sistema cambian (como la disponibilidad de una implementación diferente de BLAS).
  • No es necesario que la salida de un pase para una semilla fija sea igual entre sistemas operativos (aunque normalmente es la implementación de la biblioteca matemática del sistema la causa principal de las diferencias relacionadas con el sistema operativo).
  • No se requiere que la salida de un pase para una semilla fija sea la misma entre dos máquinas que tengan diferentes instrucciones de CPU disponibles; se espera que diferentes implementaciones de núcleos matemáticos centrales puedan producir diferentes comportamientos si se dispone de diferentes instrucciones de CPU, tales como instrucciones fusionadas de multiplicar y sumar que tengan diferentes características de redondeo a dos instrucciones separadas de multiplicar y sumar en coma flotante.
  • La salida de un pase para una semilla fija debe ser la misma, independientemente del número de hilos que se le permita utilizar, a menos que el usuario opte específicamente por este comportamiento. Por ejemplo, en los gestores de paso preestablecidos, los métodos de trazado y enrutamiento de Sabre necesitan ejecutar el mismo número de pruebas por defecto, sin importar si hay un único hilo permitido o incluso más hilos que pruebas, aunque este comportamiento puede ser anulado explícitamente estableciendo la variable de entorno QISKIT_SABRE_ALL_THREADS para optar por ser sensible al recuento de hilos.
  • Todas las reglas anteriores se aplican incluso entre sesiones separadas del intérprete Python, aunque no se haya configurado explícitamente PYTHONHASHSEED .

En general, un usuario del DAGCircuit debe poder dar por sentado que, tras la ejecución de cualquier combinación de pasadas de Qiskit integradas —con valores iniciales, si procede— con entradas fijas, el resultado exacto de todos DAGCircuit() los métodos es determinista. Esto incluye el orden de salida incluso de los métodos que no ofrecen ninguna garantía sobre dicho orden; aunque no se puede confiar en la semántica ni en el orden exacto, sí se puede confiar en su determinismo para entradas fijas.

Los autores de pases de transpilador deben consultar Aleatoriedad y determinismo para saber cómo hacer que un pase de transpilador sea determinista.

Selección de implementaciones de etapas preestablecidas

Qiskit incluye varias implementaciones de las etapas anteriores, y se pueden instalar más como "plugins" independientes. Para controlar qué implementación de una etapa se utiliza, pase su nombre al argumento de palabra clave <stage>_method de las dos funciones, como translation_method="translator". Para obtener más información sobre la implementación de estos plugins externos para un escenario, consulte qiskit.transpiler.preset_passmanagers.plugin.

Por ejemplo, para generar un gestor de pases preestablecido en el nivel de optimización 1 que utilice explícitamente el método trivial para el trazado con el método sabre para el enrutamiento, haríamos lo siguiente:

from qiskit.transpiler import generate_preset_pass_manager
from qiskit.providers.fake_provider import GenericBackendV2

# Whatever backend you like:
backend = GenericBackendV2(num_qubits=5)

pass_manager = generate_preset_pass_manager(
    optimization_level=1,
    backend=backend,
    layout_method="trivial",
    routing_method="sabre",
)
Nota

El conjunto integrado de plugins disponibles para cada etapa forma parte de la API pública de Qiskit y está sujeto a todas las garantías de estabilidad. Esto incluye los efectos lógicos de alto nivel de ese método (por ejemplo, routing_method="sabre" siempre utilizará un algoritmo derivado de Sabre). La construcción interna exacta del PassManager que representa la etapa no lo es, sin embargo; el orden de los pases podría cambiar entre versiones menores, o podrían introducirse nuevos pases.

Para cualquier etapa que tenga uno, el método denominado "default" es el más sujeto a cambios. Qiskit normalmente sólo hace cambios algorítmicos completos en el método por defecto a través de un límite de versión mayor, pero puede reequilibrar la heurística y añadir nuevas pasadas a los métodos por defecto entre versiones menores.

Dado que la salida de generate_preset_pass_manager() es un StagedPassManagertambién se puede modificar el gestor de pases después de su creación para proporcionar una implementación de etapas totalmente personalizada. Por ejemplo, si desea ejecutar una etapa de programación personalizada utilizando el desacoplamiento dinámico (utilizando la función PadDynamicalDecoupling pass) y también añadir una optimización lógica inicial antes del enrutamiento, haría algo como lo siguiente (basándose en el ejemplo anterior):

import numpy as np
from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.circuit import library as lib
from qiskit.transpiler import PassManager, generate_preset_pass_manager
from qiskit.transpiler.passes import (
    ALAPScheduleAnalysis,
    InverseCancellation,
    PadDynamicalDecoupling,
)

backend = GenericBackendV2(num_qubits=5)
dd_sequence = [lib.XGate(), lib.XGate()]
scheduling_pm = PassManager(
    [
        ALAPScheduleAnalysis(target=backend.target),
        PadDynamicalDecoupling(target=backend.target, dd_sequence=dd_sequence),
    ]
)
inverse_gate_list = [
    lib.CXGate(),
    lib.HGate(),
    (lib.RXGate(np.pi / 4), lib.RXGate(-np.pi / 4)),
    (lib.PhaseGate(np.pi / 4), lib.PhaseGate(-np.pi / 4)),
    (lib.TGate(), lib.TdgGate()),
]
logical_opt = PassManager([InverseCancellation(inverse_gate_list)])

pass_manager = generate_preset_pass_manager(optimization_level=0)
# Add pre-layout stage to run extra logical optimization
pass_manager.pre_layout = logical_opt
# Set scheduling stage to custom pass manager
pass_manager.scheduling = scheduling_pm

Ahora, cuando se ejecute el gestor de pases por etapas mediante el método run() el gestor de pases logical_opt será llamado antes de la etapa layout , y el gestor de pases scheduling_pm será utilizado para la etapa scheduling en lugar del predeterminado.

Si está construyendo etapas personalizadas para los gestores de pases predefinidos, puede que algunas de las funciones de ayuda de bajo nivel de qiskit.transpiler.preset_passmanagers son útiles.

Etapa de inicialización

Consulte también

Explicación de la fase inicial

Explicación de alto nivel para el usuario de la etapa init en la guía IBM Quantum.

La etapa init se encarga de las optimizaciones lógicas de alto nivel en circuitos abstractos y de reducir las operaciones multiqubit (3+) a una serie de operaciones de uno y dos qubits. Como se trata de la ejecución de la primera etapa, su entrada es un circuito totalmente abstracto. La etapa init debe ser capaz de manejar puertas personalizadas definidas por el usuario, y todos los objetos abstractos de descripción de circuitos de alto nivel, tales como AnnotatedOperation.

La salida de la etapa init es un circuito abstracto que sólo contiene operaciones de uno y dos qubits.

Al escribir plugins de escenario, el punto de entrada para init es qiskit.transpiler.init. Los plugins incorporados son:

Método
Resumen
valor predeterminadoDesenrollado integrado de operaciones multiqubit y optimizaciones abstractas.

Complemento default integrado

En el nivel de optimización 0, no se realiza ninguna optimización abstracta. El complemento predeterminado simplemente «despliega» las operaciones con más de tres qubits accediendo a sus campos definition jerárquicos.

En los niveles de optimización 1 y superiores, el plugin por defecto también realiza una simple cancelación de puertas inversas adyacentes, como dos puertas cx consecutivas.

En los niveles de optimización 2 y 3, el plugin por defecto permite una gama mucho más amplia de optimizaciones abstractas. Esto incluye:

  • "Elisión de permutación virtual" (véase ElidePermutations), donde las operaciones explícitas que inducen permutaciones se eliminan y en su lugar se efectúan como reasignación de qubits virtuales.
  • Análisis de la estructura de conmutación del IR para encontrar pares de puertas que puedan anularse.
  • Desdoblamiento numérico de operaciones de dos qubits que pueden expresarse como una serie de operaciones separables de un qubit.
  • Eliminación de operaciones imperceptibles, como rotaciones Pauli de ángulos diminutos y operaciones diagonales inmediatamente anteriores a las mediciones.

Etapa de diseño

Consulte también

Explicación de la fase de diseño

Explicación de alto nivel para el usuario de la etapa de maquetación en la guía IBM Quantum.

La etapa de diseño se encarga de realizar un mapeo inicial entre los qubits virtuales del circuito de entrada y los qubits hardware del objetivo. Esto incluye ampliar el circuito de entrada con ancillas explícitas para que tenga tantos qubits como el objetivo, y reescribir todas las operaciones en términos de qubits de hardware. Es posible que también vea este problema denominado problema de "colocación" en otros conjuntos de herramientas o bibliografía.

La etapa de diseño debe establecer las propiedades layout y original_qubit_indices en el archivo PropertySet.

Nota

Todos los plugins incorporados para la etapa de maquetación darán prioridad a una maquetación explícita seleccionada mediante el argumento initial_layout de generate_preset_pass_manager() o transpile().

En cualquier punto dado de un circuito, podemos identificar una correspondencia entre los qubits "virtuales" actualmente activos del circuito de entrada y los qubits de hardware del backend. Un qubit físico sólo puede representar un único qubit virtual en un momento dado, pero la asignación puede variar a lo largo del circuito. En principio, algunos qubits virtuales podrían no ser mapeados en todos los puntos de la ejecución del circuito, si el tiempo de vida de un estado qubit virtual puede ser acortado, aunque los pipelines incorporados de Qiskit no utilizan esto actualmente.

Ilustración de cómo los qubits virtuales de un circuito de entrada podrían asignarse a qubits de hardware en el mapa de conectividad de un dispositivo backend.

La etapa de diseño no es responsable de garantizar que la conectividad del destino se respete en todo el circuito, ni de que todas las operaciones sean válidas para la ejecución directa en el destino; estas son las responsabilidades de las etapas de enrutamiento y traducción, respectivamente.

La elección del trazado inicial es uno de los factores más importantes que afectan a la calidad del circuito de salida. La etapa de diseño suele ser la más costosa desde el punto de vista computacional en los pipelines por defecto; el plugin por defecto para el diseño incluso prueba varios algoritmos diferentes (descritos con más detalle en Built-in default plugin ).

La situación ideal para la etapa de trazado es encontrar un trazado "perfecto", en el que todas las operaciones respeten las restricciones de conectividad del Target de forma que no sea necesaria la etapa de trazado. Esto no suele ser posible para circuitos de entrada arbitraria, pero cuando lo es, el VF2Layout pass puede utilizarse para encontrar un trazado inicial válido. Si se encuentran varios diseños perfectos, se utiliza una heurística de puntuación basada en las tasas de error estimadas para decidir cuál se utiliza.

En todos los plugins integrados, si se pasa el argumento generate_preset_pass_manager()initial_layout hace que se utilice literalmente el diseño dado, omitiendo la lógica de "elección" individual. Todos los plugins incorporados también se encargan de incrustar el circuito en toda la anchura del dispositivo, incluida la asignación de ancillas.

Si escribe su propio plugin de maquetación, puede que le resulte útil generate_embed_passmanager() para automatizar la etapa de "incrustación" de la aplicación de diseño.

Al escribir plugins de escenario, el punto de entrada para layout es qiskit.transpiler.layout. Los plugins incorporados son:

Método
Resumen
valor predeterminadoEn los niveles de optimización más altos, intenta encontrar un trazado perfecto y, a continuación, intenta una pasada combinada de trazado y enrutamiento basada en Sabre.
densaBusca el subgrafo más denso (en términos de grados de enlace qubit) del backend para utilizarlo como qubits iniciales.
TrivialAsigna el qubit virtual 0 al qubit físico 0, y así sucesivamente.
sableUtiliza el algoritmo de diseño Sabre mejorado de Qiskit.

En todos los niveles de optimización, el método de diseño por defecto es default, aunque la estructura de esta etapa cambia drásticamente en función del nivel.

Complemento default integrado

Una amalgama de varias técnicas de maquetación.

En el nivel de optimización 0, se elige el trazado trivial.

En los niveles de optimización superiores a 0, se produce un proceso en dos fases:

  1. En primer lugar, utilice VF2Layout para intentar encontrar un diseño "perfecto". El número máximo de llamadas al evaluador de isomorfismos aumenta con el nivel de optimización. En el caso de objetivos enormes y complejos, no tenemos la garantía de encontrar trazados perfectos aunque existan, pero la probabilidad aumenta con el nivel de optimización.
  2. Si no se encuentra la disposición perfecta, se utiliza SabreLayout para elegir una disposición inicial, con un número de pruebas de disposición inicial, pruebas de mapa de intercambio e iteraciones hacia delante y hacia atrás que aumenta con el nivel de optimización.

Además, el nivel de optimización 1 también prueba el diseño trivial antes de la versión VF2-based, por compatibilidad histórica hacia atrás.

Complemento dense integrado

Utiliza el DenseLayout pass para elegir el diseño. Este paso encuentra el subgrafo conectado más denso del grafo de conectividad objetivo completo, donde "más denso" significa que se prefieren los qubits de hardware con el mayor número de conexiones disponibles. El mapeo virtual a hardware se completa asignando los qubits virtuales de mayor grado a los qubits hardware de mayor grado.

Se trata de una heurística relativamente barata para elegir un trazado inicial, pero suele tener una calidad de salida mucho peor que los métodos basados en Sabre. El plugin de diseño por defecto utiliza la asignación inicial seleccionada por DenseLayout como uno de sus diseños iniciales para sembrar el algoritmo Sabre.

Complemento trivial integrado

Utiliza el TrivialLayout pass para elegir el diseño. Esta es la asignación más sencilla, en la que cada qubit virtual se asigna al qubit hardware con el mismo índice, por lo que el qubit virtual 0 se asigna al qubit hardware 0, y así sucesivamente.

Este método es muy útil para los experimentos de caracterización de hardware, en los que el circuito "abstracto" entrante ya está completo en el dispositivo, sus operaciones corresponden a operaciones físicas y el transpilador sólo se invoca para formalizar la creación de un circuito físico QuantumCircuit.

Complemento sabre integrado

Utiliza el SabreLayout para elegir un diseño inicial, utilizando el algoritmo de enrutamiento Sabre modificado de Qiskit como subrutina para mapear el circuito candidato tanto hacia delante como hacia atrás.

En resumen, el componente de diseño del algoritmo Sabre original elige un diseño inicial de forma arbitraria, luego intenta "mejorarlo" ejecutando el enrutamiento en el circuito, invirtiendo el circuito y ejecutando el enrutamiento en el circuito invertido con la asignación virtual a hardware "final" anterior como estado inicial. El nivel de optimización configurado decide cuántas iteraciones de este vaivén hay que hacer, y cuántas disposiciones iniciales aleatorias diferentes hay que probar.

La principal diferencia con la etapa por defecto en niveles de optimización distintos de 0 es que este plugin sólo ejecuta el algoritmo basado en Sabre. No trata de encontrar un trazado perfecto, ni intenta el trazado trivial.

Etapa de enrutamiento

Consulte también

Explicación de la etapa de encaminamiento

Explicación de mayor nivel para el usuario de la etapa de enrutamiento en la guía IBM Quantum.

La etapa de enrutamiento garantiza que el gráfico de conectividad virtual del circuito sea compatible con el gráfico de conectividad de hardware del objetivo. En términos más sencillos, la etapa de enrutamiento se asegura de que todas las puertas de dos qubits en el circuito se asignan a qubits de hardware que tienen una operación de dos qubits definida en la ISA de destino. Es posible que en otras herramientas o publicaciones también se haga referencia a este problema como "mapeo" o "mapeo de intercambio".

Los algoritmos de enrutamiento suelen hacerlo insertando puertas swap en el circuito y modificando la asignación virtual a hardware de los qubits a lo largo de la ejecución del circuito.

La etapa de enrutamiento no necesita asegurarse de que todas las puertas del circuito son válidas para el ISA de destino. Por ejemplo, un plugin de enrutamiento puede dejar puertas swap literales en el circuito, incluso si el Target no contenga SwapGate. Sin embargo, debe haber al menos una puerta de dos qubits definida en el Target para cualquier par de qubits hardware que tenga una puerta aplicada en el circuito.

La etapa de enrutamiento debe establecer las propiedades final_layout y virtual_permutation_layout en el archivo PropertySet si se ha realizado el enrutamiento.

Todas las etapas de enrutamiento incorporadas de Qiskit ejecutarán adicionalmente el VF2PostLayout después del enrutamiento. Esto podría reasignar la disposición inicial, si se pueden encontrar qubits con menos errores. Este pase es muy similar a la clase VF2Layout que utiliza el plugin de diseño por defecto, excepto que en VF2PostLayout podemos garantizar que hay al menos un subgrafo inducido isomorfo de la topología objetivo que coincide con la topología del circuito.

Nota

Todos los plugins de enrutamiento incorporados en Qiskit asumen generalmente que todos los pares de qubits con un enlace definido de dos qubits tienen un conjunto universal de puertas definidas para esos dos qubits. El hardware no tiene necesariamente que respetar esto (por ejemplo, si la única puerta de dos qubits definida es swap, entonces no se pueden realizar operaciones de enredo como cx ), pero Qiskit aún no considera esta posibilidad.

Nota

Se sabe que encontrar el número mínimo de intercambios a insertar es un problema no polinómico. Esto significa que es prohibitivamente caro de intentar, por lo que muchos de los algoritmos incorporados de Qiskit son estocásticos, y usted puede ver grandes variaciones entre diferentes compilaciones. Si necesita reproducibilidad, asegúrese de establecer el argumento seed_transpiler de generate_preset_pass_manager() o transpile().

Al escribir plugins de escenario, el punto de entrada para routing es qiskit.transpiler.routing. Los plugins incorporados son:

Método
Resumen
valor predeterminadoUtilizar un método de enrutamiento por defecto elegido por Qiskit.
sableValor predeterminado. Utiliza el algoritmo de enrutamiento Sabre modificado de Qiskit para intercambiar el mapa.
ningunoDesactivar enrutamiento. Genera un error si se requiere enrutamiento.
básicoInserción de intercambio codicioso para encaminar una sola operación a la vez.
mirar hacia delanteBúsqueda por orden de prioridad con poda heurística para encontrar intercambios que hagan que las puertas sean ejecutables.

Complemento default integrado

Utilizar un método predeterminado elegido por Qiskit para el enrutamiento. A partir de Qiskit 2.0, el algoritmo elegido es el mismo que el del complemento Sabre integrado, aunque en la práctica, normalmente el complemento de etapa de diseño predeterminado integrado ejecutará el algoritmo de enrutamiento basado en Sabre, y la etapa de enrutamiento solo se usará para ejecutar VF2PostLayout.

Complemento none integrado

Un plugin ficticio utilizado para desactivar el enrutamiento por completo. Esto puede ser útil ocasionalmente para experimentos de configuración de hardware, o en ciertos casos especiales de compilación parcial.

Complemento basic integrado

Utiliza el algoritmo de intercambio e inserción codicioso BasisSwap . Esto es conceptualmente muy sencillo; para cada operación en orden topológico, inserte los intercambios de camino más corto necesarios para que la conexión sea ejecutable en el dispositivo.

El nivel de optimización sólo afecta a la cantidad de trabajo que el paso VF2PostLayout para intentar mejorar el diseño inicial después del enrutamiento.

Este método suele dar resultados de baja calidad.

Complemento lookahead integrado

Utiliza el LookaheadSwap algoritmo para enrutar. En esencia, se trata de una búsqueda exhaustiva para producir una red de intercambios, en la que el árbol explorado se reduce a un pequeño número de intercambios candidatos en cada profundidad.

Este algoritmo es similar a la heurística basic del plugin "sabre ", excepto que también considera los siguientes efectos de cada intercambio a una pequeña profundidad.

El nivel de optimización afecta a la profundidad de búsqueda, la cantidad de poda por profundidad y la cantidad de trabajo realizado por VF2PostLayout para post-optimizar el diseño inicial.

En la práctica, el complemento "sabre" funciona varios órdenes de magnitud más rápido y produce mejores resultados.

Complemento sabre integrado

Utiliza el SabreSwap algoritmo para enrutar. Utiliza la versión mejorada de Qiskit del algoritmo de enrutamiento Sabre original.

Este algoritmo de enrutamiento se ejecuta con paralelismo de hilos para considerar varias posibilidades diferentes de enrutamiento, eligiendo la que minimiza el número de intercambios insertados.

El nivel de optimización afecta a cuántas semillas estocásticas diferentes se intentan para el trazado completo, y a la cantidad de trabajo realizado por VF2PostLayout para post-optimizar el trazado inicial.

Este es casi invariablemente el plugin incorporado de mejor rendimiento, y el que Qiskit utiliza por defecto en todos los casos en los que es necesario el enrutamiento.

Etapa de traducción

Consulte también

Explicación de la fase de traducción

Explicación de alto nivel para el usuario de la etapa de traducción en la guía IBM Quantum.

La etapa de traducción se encarga de reescribir todas las puertas del circuito para convertirlas en puertas compatibles con la ISA de destino. Por ejemplo, si se solicita una cx en los qubits hardware 0 y 1, pero la ISA sólo contiene una operación cz en esos qubits, la etapa de traducción debe encontrar una forma de representar la puerta cx utilizando la cz y las puertas de un qubit disponibles.

La etapa de traducción se ejecuta antes de entrar en la etapa de optimización. Los plugins de optimización (incluyendo los plugins incorporados de Qiskit) también pueden utilizar la etapa de traducción como una etapa de "corrección" después del bucle de optimización, si el bucle de optimización devuelve un circuito que incluye puertas no-ISA. Esta última situación es bastante común; el bucle de optimización puede sólo estar preocupado por minimizar propiedades como "número de puertas de dos qubits", y dejará su salida en términos de puertas localmente equivalentes, que la etapa de traducción puede reescribir fácilmente sin afectar a las propiedades de optimización objetivo. Esto permite separar más fácilmente las preocupaciones entre las dos etapas. Algunos plugins de optimización pueden ser más estrictos en sus resultados, por lo que este seguimiento de la fase de traducción puede dejar de ser necesario.

Al escribir plugins de escenario, el punto de entrada para translation es qiskit.transpiler.translation. Los plugins incorporados son:

Método
Resumen
valor predeterminadoUtilizar un método de traducción por defecto elegido por Qiskit.
traductorTraducción simbólica de puertas a la base de destino utilizando equivalencias conocidas.
síntesisRecoge cada serie de puertas de uno y dos qubits en una representación matricial, y resintetiza a partir de ahí.

Complemento default integrado

Utilizar un método por defecto elegido por Qiskit para la traducción. A partir de Qiskit 2.0, esto es lo mismo que el plugin traductor incorporado, pero el algoritmo elegido puede cambiar durante la serie 2.x, ya sea para todos los destinos, o sólo para ciertas clases de destino.

Complemento synthesis integrado

Recopila ejecuciones de puertas en los mismos qubits en forma de matriz, y luego resintetiza usando el pase UnitarySynthesis (con la configuración unitary_synthesis_method). Esto es, en gran parte, similar al propio bucle de optimización a altos niveles de optimización.

La recopilación en matrices suele ser más cara que las traducciones sin matrices, pero en principio la calidad de las traducciones puede ser mejor. En la práctica, esto requiere un algoritmo de síntesis adaptado al ISA objetivo, lo que hace que este método sea menos general que otros. Puede producir resultados de mayor calidad cuando se dirige a ISA sencillas que coinciden con las rutinas de síntesis que ya están en Qiskit.

Si se utiliza este método, es posible que no necesite el bucle de optimización.

El nivel de optimización no tiene ningún efecto sobre este plugin.

Complemento translator integrado

Utiliza el BasisTranslator para traducir simbólicamente las puertas a la base de destino. A un alto nivel, se parte del conjunto de puertas solicitadas por el circuito, y se utilizan reglas de un determinado EquivalenceLibrary (normalmente el SessionEquivalenceLibrary) para avanzar hacia el ISA.

Este es el método de traducción por defecto.

El nivel de optimización no tiene ningún efecto sobre este plugin.

Etapa de optimización

Consulte también

Explicación de la fase de optimización

Explicación de la etapa de optimización en la guía IBM Quantum.

La etapa de optimización es para optimizaciones de bajo nivel conscientes del hardware. A diferencia de la etapa init, la entrada a esta etapa es un circuito que ya es compatible con ISA, por lo que se puede adaptar un plugin de optimización de bajo nivel para un ISA en particular.

Hay muy pocos requisitos para un plugin de optimización, aparte de que acepta circuitos compatibles con ISA y devuelve circuitos compatibles con ISA. Un plugin de optimización contendrá a menudo un bucle, como el DoWhileControllery puede incluir la etapa de traducción configurada como un canal de corrección.

Los plugins de optimización incorporados en Qiskit son generales y se aplican bien a la mayoría de los ISA del mundo real para dispositivos sin corrección de errores. Los plugins incorporados son menos adecuados para los ISA que no tienen una puerta de un solo qubit parametrizado de forma continua.

Al escribir plugins de escenario, el punto de entrada para optimization es qiskit.transpiler.optimization. Los plugins incorporados son:

Método
Resumen
valor predeterminadoUn conjunto predeterminado de pases de optimización. Esto varía significativamente entre los niveles de optimización.

Complemento default integrado

Esto varía considerablemente en función del nivel de optimización.

Los detalles de este proceso pueden variar entre las distintas versiones de Qiskit. A continuación se describen los principios generales.

En el nivel de optimización 0, el escenario está vacío.

En el nivel de optimización 1, la etapa realiza resíntesis matricial de ejecuciones de puertas de un solo qubit, y cancelación inversa simbólica muy simple de puertas de dos qubits, si aparecen consecutivamente. Esto se ejecuta en bucle hasta que se fijan el tamaño y la profundidad del circuito.

En el nivel de optimización 2, además de las optimizaciones del nivel 1, el bucle contiene análisis de conmutación de conjuntos de puertas para ampliar la gama de puertas que pueden considerarse para la cancelación. Antes del bucle, las ejecuciones de las puertas de uno y dos qubits se someten a una única resíntesis matricial.

En el nivel de optimización 3, la resíntesis basada en matrices de dos qubits se ejecuta dentro del bucle de optimización. La condición de bucle de optimización también intenta múltiples ejecuciones y elige el punto mínimo en caso de salida fluctuante; esto es necesario porque la resíntesis basada en matrices es relativamente inestable en términos de puertas concretas.

El nivel de optimización 3 suele ser muy caro para los circuitos grandes.

Etapa de programación

Consulte también

Programación de circuitos

Una explicación a modo de guía de los conceptos de programación.

La etapa de programación, si se solicita, es responsable de insertar instrucciones explícitas para hacer explícitos los períodos de inactividad de los qubits Delay para hacer explícitos los periodos de inactividad de los qubits. Los plugins pueden optar por realizar transformaciones sensibles al tiempo de pared, como insertar secuencias de desacoplamiento dinámico.

La entrada a la etapa de programación es un circuito compatible con ISA. La salida de la etapa de programación también debe ser un circuito compatible con ISA, con instrucciones explícitas que satisfagan la información de temporización del hardware, si procede Delay instrucciones que satisfagan la información de temporización del hardware, si procede.

La etapa de programación debe establecer la propiedad node_start_time en el archivo PropertySet.

Al escribir plugins de escenario, el punto de entrada para scheduling es qiskit.transpiler.scheduling. Los plugins incorporados son:

Método
Resumen
valor predeterminadoIntentar satisfacer las restricciones de alineación temporal sin programar de otro modo.
alapPrograme el circuito, prefiriendo que las operaciones se realicen lo más tarde posible.
Lo antes posibleProgramar el circuito, prefiriendo que las operaciones sean lo antes posible.

Complemento default integrado

No hacer nada, a menos que el circuito ya contenga instrucciones con temporización explícita. Si hay operaciones temporizadas explícitamente en el circuito, inserte relleno adicional para garantizar que estos tiempos satisfacen la alineación y otras restricciones de hardware.

Complemento alap integrado

Programar explícitamente todas las operaciones utilizando una estrategia "lo más tarde posible". Esto utiliza el ALAPScheduleAnalysis algoritmo para decidir dónde colocar las puertas.

Complemento asap integrado

Programar explícitamente todas las operaciones utilizando una estrategia "lo antes posible". Esto utiliza el ASAPScheduleAnalysis algoritmo para decidir dónde colocar las puertas.


Administradores de pases personalizados

Además de modificar los gestores de pases preestablecidos, también es posible construir un gestor de pases para crear una canalización totalmente personalizada para transformar los circuitos de entrada. Puede utilizar la clase StagedPassManager directamente para hacerlo. Puede definir nombres de escenario arbitrarios y rellenarlos con una PassManager instancia. Por ejemplo, el siguiente código crea un nuevo StagedPassManager que tiene dos etapas, init y translation.

from qiskit.transpiler.passes import (
    UnitarySynthesis,
    Collect2qBlocks,
    ConsolidateBlocks,
    UnitarySynthesis,
    Unroll3qOrMore,
)
from qiskit.transpiler import PassManager, StagedPassManager

basis_gates = ["rx", "ry", "rxx"]
init = PassManager([UnitarySynthesis(basis_gates, min_qubits=3), Unroll3qOrMore()])
translate = PassManager(
    [
        Collect2qBlocks(),
        ConsolidateBlocks(basis_gates=basis_gates),
        UnitarySynthesis(basis_gates),
    ]
)

staged_pm = StagedPassManager(
    stages=["init", "translation"], init=init, translation=translate
)

No hay límite en el número de etapas que puede poner en un StagedPassManager. No es necesario que las etapas se correspondan con las etapas utilizadas por los conductos preestablecidos de Qiskit.

Las funciones generadoras de escenarios pueden ser útiles para la construcción de instancias personalizadas StagedPassManager personalizadas. Generan gestores de pases que proporcionan funcionalidades comunes utilizadas en muchas etapas. Por ejemplo generate_embed_passmanager() genera un PassManager para "incrustar" una inicial seleccionada Layout de un pase de maquetación al dispositivo de destino especificado.


Escribir pasadas de transpilador personalizadas

Qiskit está diseñado para ampliarse con pases de transpilador personalizados y especializados.

Existen dos tipos de pases de transpilador: los pases de "análisis" (AnalysisPass), que leen un circuito y escriben las propiedades globales de análisis en PropertySet; y los pases de "transformación" (TransformationPass), que modifican un circuito DAGCircuit en su lugar, o devuelven un nuevo DAGCircuit. Históricamente, Qiskit intentaba separar fuertemente estos dos tipos. En el Qiskit moderno, sin embargo, es bastante común incluir tanto el análisis como las modificaciones en un solo programa independiente TransformationPassen lugar de intentar dividirlo todo. Si su pase se basa puramente en el análisis, sigue siendo apropiado utilizar AnalysisPass.

Principios generales sobre la autoría de pases

Si desea modificar o crear un nuevo DAGCircuitdebe escribir un archivo TransformationPass. Si sólo desea escribir en la página PropertySet y no modificar el archivo DAGCircuitdebe escribir un archivo AnalysisPass. Si desea hacer ambas cosas, escriba un TransformationPass. En ambos casos, el único método necesario es BasePass.run(), que es la carne de su pase. Debe aceptar un único argumento (distinto de self), dag: DAGCircuit. Si a TranspilerPass, debería devolver DAGCircuit (que puede ser la entrada, si se modifica en el lugar), mientras que si a AnalysisPassdebe devolver None.

Si su pase tiene un inicializador, debe llamar a super().__init__().

Normalmente, su pass debería aceptar un Target en su inicializador, que describe el hardware cuántico para el que está compilando. Se desaconseja aceptar restricciones "laxas" como un mapa de acoplamiento independiente y una lista de puertas base; la Target describe mejor el hardware heterogéneo general.

Durante la ejecución de un PassManager pipeline, cuando se llama al método run() de su pase, puede acceder al atributo self.property_set para obtener el estado actual PropertySet de la transpilación. Usted debe leer y escribir en este lugar. Su pase debe documentar claramente qué atributos del conjunto de propiedades, si los hay, lee y escribe.

Aleatoriedad y determinismo

La compilación cuántica a menudo implica resolver problemas que son intratables de optimizar globalmente. En estos casos, los algoritmos estocásticos y heurísticos suelen ser más apropiados. Sin embargo, esto plantea problemas de reproducibilidad.

No existe ningún requisito formal para que un pase personalizado sea determinista bajo el mismo conjunto de reglas que deben seguir los pases Qiskit incorporados. Sin embargo, le recomendamos encarecidamente que siga estas reglas en sus propios pases; la ciencia se nutre de la reproducibilidad, y la depuración es una pesadilla cuando no se puede reproducir un comportamiento observado previamente.

Al escribir un pase de transpilador, puede confiar en que los siguientes ejemplos (representativos y no exhaustivos) sean deterministas, aunque la semántica exacta del orden no esté completamente especificada, y los pases no deberían depender de ningún orden en particular:

  • Los nodos de orden se encuentran en DAGCircuit.op_nodes(). Por el contrario, topological_op_nodes() incluye por defecto una clave de ordenación que hace que su orden no se vea afectado en absoluto por el orden de eliminación/inserción de nodos, por lo que es totalmente determinista siempre que se especifique el mismo conjunto de nodos con el mismo flujo de datos, aunque se haya construido en un orden diferente.
  • Los bordes de orden se encuentran en DAGCircuit.edges().
  • El orden en que se devuelven las ejecuciones de DAGCircuit.collect_2q_runs()y el orden exacto en que se encuentran los nodos de una ejecución.
  • El orden en que se encuentran los nodos en los métodos de orden-degeneración como predecessors(), bfs_successors()etc.

En general, el requisito es que se realicen todas las mismas modificaciones en el circuito, exactamente en el mismo orden. Por ejemplo, si hay que añadir, contraer o eliminar nodos, el orden de estas modificaciones debe hacerse en un orden determinista, y las sustituciones deben especificarse de forma determinista.

Algunos consejos para garantizarlo son

  • Ten mucho cuidado al iterar sobre contenedores basados en hash. La iteración sobre Python 's set no es determinista debido a la aleatoriedad de la semilla de hash. En Rust, la iteración sobre los contenedores basados en hash de la biblioteca estándar, incluyendo los equivalentes de hashbrown con sus hashers por defecto, no es determinista.

    Nota

    La iteración sobre Python 's dict es determinista, y se garantiza que está en orden de inserción si no ha habido eliminaciones, y en orden arbitrario pero determinista si ha habido eliminaciones deterministas.

    En Python, si necesita crear un archivo set y luego iterar sobre él, considere en su lugar utilizar a dict con todos los valores siendo None como sustituto. Utilizar un set únicamente para comprobar la afiliación no supone ningún problema.

    En Rust, utilice indexmap y sus structs IndexMap y IndexSet como sustitutos de HashMap y HashSet, respectivamente; tienen propiedades de iteración determinista similares a las de Pythondict.

  • Si tu función tiene componentes estocásticos, asegúrate de aceptar un seed valor de entrada y haz que tu salida sea pura si este se proporciona como un número entero. Por lo general, esto implica guardar la semilla e instanciar un nuevo objeto pRNG a partir de dicha semilla al inicio de cada llamada a BasePass.run().

  • Si utiliza el paralelismo de hilos, tenga cuidado de que su salida no dependa del orden en que los hilos realizan su trabajo o devuelven sus resultados parciales. Por ejemplo, si se distribuye el trabajo a través de un grupo de hilos y se recogen los resultados al final, hay que asegurarse de que la salida se organiza en un orden correspondiente a la entrada. En Python, funciones como concurrent.futures.ThreadPoolExecutor.map() se encargan de ello. De forma similar, en Rust, los iteradores paralelos de rayonrecogerán su salida en el mismo orden que la entrada.

    Tenga en cuenta que las reducciones paralelas, tales como "aplicar una función a cada elemento de este iterador, y elegir el que minimiza alguna métrica", son típicamente muy susceptibles a la no determinación de hilos, en el caso de degeneraciones en la métrica. Por ejemplo, si dos elementos del iterador producen una salida no igual que, sin embargo, tiene la misma clave de comparación, la elegida en un entorno con hilos no es determinista. Para evitarlo, aplique un desempate determinista que elimine la degeneración, por ejemplo, enumerando la entrada y utilizando el número de secuencia como clave de desempate, de forma que si dos elementos tienen la misma puntuación, se elija de forma fiable el que corresponda a una entrada anterior.


Representación de ordenadores cuánticos

Para poder compilar un QuantumCircuit para un backend específico, el transpilador necesita una representación especializada de ese backend, incluidas sus restricciones, conjunto de instrucciones, propiedades de los qubits, etc., para poder compilar y optimizar eficazmente. Aunque la clase BackendV2 define una interfaz para consultar e interactuar con backends, su ámbito de aplicación va más allá de las necesidades del transpilador, incluyendo la gestión del envío de trabajos y la posibilidad de interactuar con servicios remotos. La información específica que necesita el transpilador se describe en la clase Target clase

Por ejemplo, para construir un objeto Target se pueden ir añadiendo descripciones de las instrucciones que admite:

from qiskit.circuit import Parameter, Measure
from qiskit.transpiler import Target, InstructionProperties
from qiskit.circuit.library import UGate, RZGate, RXGate, RYGate, CXGate, CZGate

target = Target(num_qubits=3)
target.add_instruction(CXGate(), {(0, 1): InstructionProperties(error=.0001, duration=5e-7)})
target.add_instruction(
    UGate(Parameter('theta'), Parameter('phi'), Parameter('lam')),
    {
        (0,): InstructionProperties(error=.00001, duration=5e-8),
        (1,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RZGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RYGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RXGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    CZGate(),
    {
        (1, 2): InstructionProperties(error=.0001, duration=5e-7),
        (2, 0): InstructionProperties(error=.0001, duration=5e-7)
    }
)
target.add_instruction(
    Measure(),
    {
        (0,): InstructionProperties(error=.001, duration=5e-5),
        (1,): InstructionProperties(error=.002, duration=6e-5),
        (2,): InstructionProperties(error=.2, duration=5e-7)
    }
)
print(target)
Target
Number of qubits: 3
Instructions:
    cx
        (0, 1):
            Duration: 5e-07 sec.
            Error Rate: 0.0001
    u
        (0,):
            Duration: 5e-08 sec.
            Error Rate: 1e-05
        (1,):
            Duration: 6e-08 sec.
            Error Rate: 2e-05
    rz
        (1,):
            Duration: 5e-08 sec.
            Error Rate: 1e-05
        (2,):
            Duration: 6e-08 sec.
            Error Rate: 2e-05
    ry
        (1,):
            Duration: 5e-08 sec.
            Error Rate: 1e-05
        (2,):
            Duration: 6e-08 sec.
            Error Rate: 2e-05
    rx
        (1,):
            Duration: 5e-08 sec.
            Error Rate: 1e-05
        (2,):
            Duration: 6e-08 sec.
            Error Rate: 2e-05
    cz
        (1, 2):
            Duration: 5e-07 sec.
            Error Rate: 0.0001
        (2, 0):
            Duration: 5e-07 sec.
            Error Rate: 0.0001
    measure
        (0,):
            Duration: 5e-05 sec.
            Error Rate: 0.001
        (1,):
            Duration: 6e-05 sec.
            Error Rate: 0.002
        (2,):
            Duration: 5e-07 sec.
            Error Rate: 0.2

Este Target representa un backend de 3 qubits que soporta CXGate entre qubits 0 y 1, UGate en qubits 0 y 1, RZGate, RXGatey RYGate en los qubits 1 y 2 CZGate entre los qubits 1 y 2, y entre los qubits 2 y 0, y Measure en todos los qubits.

También existen estructuras de datos específicas para representar un subconjunto concreto de información del Target. Por ejemplo, la clase CouplingMap se utiliza únicamente para representar las restricciones de conectividad de un backend como un grafo dirigido. Se puede generar un mapa de acoplamiento a partir de un Target utilizando el método Target.build_coupling_map() método. Estas estructuras de datos suelen ser anteriores a la clase Target pero siguen siendo utilizadas por algunos transpiladores que aún no trabajan de forma nativa con una instancia Target o cuando se trabaja con backends que no utilizan la interfaz BackendV2 más reciente.

Por ejemplo, si quisiéramos visualizar el CouplingMap para el ejemplo de 3 qubit Target de arriba:

from qiskit.circuit import Parameter, Measure
from qiskit.transpiler import Target, InstructionProperties
from qiskit.circuit.library import UGate, RZGate, RXGate, RYGate, CXGate, CZGate

target = Target(num_qubits=3)
target.add_instruction(CXGate(), {(0, 1): InstructionProperties(error=.0001, duration=5e-7)})
target.add_instruction(
    UGate(Parameter('theta'), Parameter('phi'), Parameter('lam')),
    {
        (0,): InstructionProperties(error=.00001, duration=5e-8),
        (1,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RZGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RYGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RXGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    CZGate(),
    {
        (1, 2): InstructionProperties(error=.0001, duration=5e-7),
        (2, 0): InstructionProperties(error=.0001, duration=5e-7)
    }
)
target.add_instruction(
    Measure(),
    {
        (0,): InstructionProperties(error=.001, duration=5e-5),
        (1,): InstructionProperties(error=.002, duration=6e-5),
        (2,): InstructionProperties(error=.2, duration=5e-7)
    }
)

target.build_coupling_map().draw()

Esto muestra la conectividad global del Target que es la combinación de los qubits soportados para CXGate y CZGate. Para ver la conectividad individual, puede pasar el nombre de la operación a CouplingMap.build_coupling_map():

from qiskit.circuit import Parameter, Measure
from qiskit.transpiler import Target, InstructionProperties
from qiskit.circuit.library import UGate, RZGate, RXGate, RYGate, CXGate, CZGate

target = Target(num_qubits=3)
target.add_instruction(CXGate(), {(0, 1): InstructionProperties(error=.0001, duration=5e-7)})
target.add_instruction(
    UGate(Parameter('theta'), Parameter('phi'), Parameter('lam')),
    {
        (0,): InstructionProperties(error=.00001, duration=5e-8),
        (1,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RZGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RYGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RXGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    CZGate(),
    {
        (1, 2): InstructionProperties(error=.0001, duration=5e-7),
        (2, 0): InstructionProperties(error=.0001, duration=5e-7)
    }
)
target.add_instruction(
    Measure(),
    {
        (0,): InstructionProperties(error=.001, duration=5e-5),
        (1,): InstructionProperties(error=.002, duration=6e-5),
        (2,): InstructionProperties(error=.2, duration=5e-7)
    }
)

target.build_coupling_map('cx').draw()
from qiskit.circuit import Parameter, Measure
from qiskit.transpiler import Target, InstructionProperties
from qiskit.circuit.library import UGate, RZGate, RXGate, RYGate, CXGate, CZGate

target = Target(num_qubits=3)
target.add_instruction(CXGate(), {(0, 1): InstructionProperties(error=.0001, duration=5e-7)})
target.add_instruction(
    UGate(Parameter('theta'), Parameter('phi'), Parameter('lam')),
    {
        (0,): InstructionProperties(error=.00001, duration=5e-8),
        (1,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RZGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RYGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RXGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    CZGate(),
    {
        (1, 2): InstructionProperties(error=.0001, duration=5e-7),
        (2, 0): InstructionProperties(error=.0001, duration=5e-7)
    }
)
target.add_instruction(
    Measure(),
    {
        (0,): InstructionProperties(error=.001, duration=5e-5),
        (1,): InstructionProperties(error=.002, duration=6e-5),
        (2,): InstructionProperties(error=.2, duration=5e-7)
    }
)

target.build_coupling_map('cz').draw()

Programación de circuitos

Consulte también

Fase de programación

Cómo configurar las etapas de programación de los gestores de pases prefijados.

Una vez que el circuito se ha traducido a la base de destino, se ha asignado al dispositivo y se ha optimizado, puede aplicarse una fase de programación para contabilizar opcionalmente todo el tiempo de inactividad del circuito. A un alto nivel, la programación puede considerarse como la inserción de retardos en el circuito para tener en cuenta el tiempo de inactividad de los qubits entre la ejecución de las instrucciones. Por ejemplo, si empezamos con un circuito como:

Diagrama que ilustra el circuito descrito anteriormente.

entonces podemos llamar a transpile() con scheduling_method :

from qiskit import QuantumCircuit, transpile
from qiskit.providers.fake_provider import GenericBackendV2

backend = GenericBackendV2(5)

ghz = QuantumCircuit(5)
ghz.h(0)
ghz.cx(0,range(1,5))

circ = transpile(ghz, backend, scheduling_method="asap")
circ.draw(output='mpl')
Diagrama del circuito generado por el código anterior.

Puedes ver aquí que el transpilador insertó Delay instrucciones para tener en cuenta el tiempo de inactividad en cada qubit. Para hacernos una mejor idea de la temporización del circuito también podemos mirarlo con la función timeline.draw() :

Salida del cajón temporizador del circuito.

La programación de un circuito consta de dos partes: el análisis y la asignación de restricciones, seguidos de una pasada de relleno. La primera parte requiere ejecutar un pase de análisis de programación como ALAPSchedulingAnalysis o ASAPSchedulingAnalysis que analiza el circuito y registra la hora de inicio de cada instrucción en el circuito utilizando un algoritmo de programación ("lo más tarde posible" para ALAPSchedulingAnalysis y "lo más pronto posible" para ASAPSchedulingAnalysis) en el conjunto de propiedades. Una vez que el circuito tiene una programación inicial, se pueden ejecutar pases adicionales para tener en cuenta cualquier restricción de tiempo en el backend de destino, como las restricciones de alineación. Esto se hace normalmente con el pase ConstrainedReschedule que ajustará la programación establecida en el conjunto de propiedades a las restricciones del backend de destino. Una vez finalizada toda la programación y los ajustes/reprogramaciones, se realiza un pase de relleno, como por ejemplo PadDelay o PadDynamicalDecoupling para insertar las instrucciones en el circuito, lo que completa la programación.


API del transpilador

Descripción del hardware

Target([descripción, num_qubits, dt,...] )La intención del objeto Target es informar al compilador de Qiskit sobre las restricciones de un backend en particular para que el compilador pueda compilar un circuito de entrada a algo que funcione y esté optimizado para un dispositivo.
InstructionProperties([duración, error] )Una representación de las propiedades de la implementación de una puerta.
WrapAngleRegistry()Registro de la función Angle Wrapping

Definición de gestor de contraseñas

StagedPassManager([etapas] )Una canalización de gestores de pases construida a partir de etapas individuales.
PassManager([pases, max_iteración] )Gestor de un conjunto de Pases y su programación durante la transpilación.
PassManagerConfig([diseño_inicial,...] )Configuración de Pass Manager.
generate_preset_pass_manager([...])Generar un preajuste PassManager

Diseño y topología

Layout([input_dict] )Dictado bidireccional para representar un Layout.
CouplingMap([lista de acoplamiento, descripción] )Grafo dirigido que especifica el acoplamiento fijo.
TranspileLayout(diseño_inicial,...[,...] )Atributos de diseño para el circuito de salida del transpilador.

Planificación

InstructionDurations([instrucción_duraciones, dt] )Clase auxiliar para proporcionar duraciones de instrucciones para la programación.

Pases abstractos

TransformationPass(*args, **kwargs)Una pasada de transformación: cambiar el DAG, no el conjunto de propiedades.
AnalysisPass(*args, **kwargs)Un pase de análisis: cambiar el conjunto de propiedades, no el DAG.

Excepciones

TranspilerError

exception qiskit.transpiler.TranspilerError(*message)

GitHub

Bases: TranspilerAccessError

Excepciones planteadas durante la transpilación.

Establece el mensaje de error.

TranspilerAccessError

exception qiskit.transpiler.TranspilerAccessError(*message)

GitHub

Bases: PassManagerError

DEPRECIADO: Excepción de error de acceso en los pases del transpilador.

Establece el mensaje de error.

CouplingError

exception qiskit.transpiler.CouplingError(*msg)

GitHub

Bases: QiskitError

Clase base para los errores generados por el objeto gráfico de acoplamiento.

Establece el mensaje de error.

LayoutError

exception qiskit.transpiler.LayoutError(*msg)

GitHub

Bases: QiskitError

Errores generados por el objeto de diseño.

Establece el mensaje de error.

CircuitTooWideForTarget

exception qiskit.transpiler.CircuitTooWideForTarget(*message)

GitHub

Bases: TranspilerError

Se produce un error si el circuito es demasiado ancho para el objetivo.

Establece el mensaje de error.

InvalidLayoutError

exception qiskit.transpiler.InvalidLayoutError(*message)

GitHub

Bases: TranspilerError

Se produce un error cuando el diseño proporcionado por el usuario no es válido.

Establece el mensaje de error.

Métrica de optimización

OptimizationMetric(*valores)Métrica de optimización considerada durante la transpilación.
¿Le ha resultado útil esta página?
Informe de un error, de una errata o solicite contenido en GitHub.