Transpilador
qiskit.transpiler
Visión general
Si usted ya está familiarizado con los conceptos de transpilación / compilación de circuitos, es posible que desee saltar hacia adelante a:
- Descripciones de los gestores de pases preconfigurados. Incluye descripciones de las etapas y de todos los plugins incorporados disponibles.
- Documentación sobre la creación de un pase de transpilador personalizado.
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:
QuantumCircuitQiskit utiliza la representación intermedia (IR) basada DAGCircuit en grafos de un circuito en toda la pila del transpilador, en lugar de la basada en árboles. Un proceso de transpilación es un PassManager objeto cuyo PassManager.run() método toma un QuantumCircuit y lo convierte en un DAGCircuit, somete a la representación en lenguaje intermedio (IR) a una secuencia de pasadas y, finalmente, devuelve un QuantumCircuit . Una pasada puede ser un AnalysisPass, que calcula y almacena propiedades del circuito en el PropertySet, o un TransformationPass, que modifica el IR para alcanzar un objetivo concreto. Puedes imaginarte un proceso como si estuviera dividido en «etapas», en las que cada una se encarga de una transformación de alto nivel.
Qiskit ofrece un generador predeterminado de flujos de trabajo de transpilación mediante la función generate_preset_pass_manager(). Esto devuelve un pipeline correctamente configurado para una transpilación completa, con un valor elegido optimization_level (entre 0 y 3, ambos inclusive). A menos que busques algo muy especializado, este es, sin duda, el punto de partida que necesitas. Un ejemplo de transpilación sería el siguiente:
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, las funciones generate_preset_pass_manager() y transpile() activan un proceso de transpilación especializado cuando la base de destino está formada por puertas Clifford+T; véase generate_preset_clifford_t_pass_manager() para obtener más información. Se recomienda utilizar esta última opción para una configuración detallada de los flujos de trabajo de Clifford+T. Por ejemplo, la precisión de síntesis de « » no se puede configurar a través de "unitary_synthesis_method" en generate_preset_pass_manager() sino que solo se puede establecer de forma global mediante el "approximation_degree". Sin embargo, generate_preset_clifford_t_pass_manager() pone "rz_synthesis_config" de manifiesto esta cuestió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». Todas ellas son instancias de PassManager, por lo que se utilizan pasando un QuantumCircuit al método PassManager.run() . Más concretamente, los gestores de pasadas predefinidos son instancias de StagedPassManager, lo que permite una mayor configuración de las etapas individuales de una transpilación, incluidos los hooks previos y posteriores a cada 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
Inserta puertas lógicas en el circuito para asegurarte de que cumple con las restricciones de conectividad del Target. No es necesario que las puertas insertadas coincidan todavía con la ISA de destino, por lo que a menudo son simplemente swap instrucciones. A veces se omite esta etapa, cuando la layout etapa en cuestión cumple con su función. Consulta la sección «Etapa de enrutamiento» para obtener más detalles.
translation
Convierte todas las puertas lógicas del circuito a unas que se ajusten a la arquitectura ISA del Target. Consulta la sección «Fase de traducción» para obtener más detalles.
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 determina qué implementaciones se utilizan por defecto en una etapa determinada, aunque esto se puede anular pasando argumentos explícitos <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) está inicializada, 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.
- Todas las funciones integradas que no contengan componentes estocásticos deben respetar las reglas de salida determinista para entradas idénticas. Está permitido mantener una caché por motivos de eficiencia, pero, dado un mismo conjunto de entradas, los resultados de la pasada deben ser los mismos si esta se invoca varias veces, a menos que algún factor ajeno al control de la pasada modifique in situ una de sus entradas (por ejemplo, en los gestores de pasadas predefinidos se
BasisTranslatorutilizaSessionEquivalenceLibrarypor defecto la biblioteca de equivalencias, y no se considera un error obtener resultados diferentes si se añaden nuevas entradas a dicha biblioteca). Se entiende por «salida» cualquier elemento que la etapa genere para su uso posterior; puede tratarse del valor explícitoreturnde la etapa, pero también incluye propiedades destinadas a ser utilizadas posteriormente en elPropertySet. - 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_THREADSpara 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 debería poder dar por hecho 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",
)El conjunto integrado de complementos 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). Sin embargo, la estructura interna exacta de la PassManager fase que representa la etapa no es fija; el orden de las pasadas podría cambiar entre versiones menores, o podrían introducirse nuevas pasadas.
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 StagedPassManager, también puedes modificar el gestor de pasadas tras su creación para proporcionar una implementación de etapa totalmente personalizada. Por ejemplo, si quisieras ejecutar una etapa de programación personalizada utilizando el desacoplamiento dinámico (mediante el PadDynamicalDecoupling pass) y añadir además una optimización lógica inicial antes del enrutamiento, harías algo como lo siguiente (partiendo del 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_pmAhora, cuando se ejecute el gestor de pases por etapas mediante el run() método, se llamará al logical_opt gestor de pases antes de la layout etapa, y se utilizará dicho scheduling_pm gestor para la scheduling etapa en lugar del gestor 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
Explicación de la fase inicial
Explicación de alto nivel para el usuario de la etapa init en la guía IBM Quantum.
Esta init etapa se encarga de realizar optimizaciones lógicas de alto nivel en circuitos abstractos, así como de reducir las operaciones de varios qubits (3+) a una serie de operaciones de uno y dos qubits. Dado que se trata de la primera ejecución de la etapa, su entrada es un circuito totalmente abstracto. La init etapa debe ser capaz de gestionar puertas personalizadas definidas por el usuario, así como todos los objetos abstractos de alto nivel para la descripción de circuitos, 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 predeterminado | Desenrollado integrado de operaciones multiqubit y optimizaciones abstractas. |
Complemento default integrado
En el nivel de optimización 0, no se lleva a cabo ninguna optimización abstracta. El complemento predeterminado simplemente «despliega» las operaciones con más de tres qubits accediendo a sus campos jerárquicos definition .
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), en la que se eliminan las operaciones explícitas que inducen permutaciones y, en su lugar, se llevan a cabo mediante una reasignación de los 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
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.
PropertySetEn la etapa de diseño deben configurarse las propiedades layout y original_qubit_indices en el. del proceso.
Todos los complementos integrados para la fase de maquetación darán prioridad a una maquetación explícita seleccionada mediante el initial_layout argumento 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.
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 ).
Lo ideal en la fase de diseño es encontrar una disposición «perfecta», en la que todas las operaciones respeten las restricciones de conectividad del circuito Target , de modo que no sea necesaria la fase de trazado. Por lo general, esto no es posible para circuitos de entrada arbitrarios, pero cuando sí lo es, se puede utilizar esta VF2Layout etapa para encontrar una disposición inicial válida. Si se encuentran varias distribuciones perfectas, se utiliza una heurística de puntuación basada en las tasas de error estimadas para decidir cuál de ellas se va a utilizar.
En todos los complementos integrados, al pasar el generate_preset_pass_manager() argumento initial_layout se hace que se utilice el diseño indicado tal cual, sin aplicar la lógica de «selección» individual. Todos los complementos integrados también se encargan de integrar el circuito en todo el ancho del dispositivo, incluida la asignación de componentes auxiliares.
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 predeterminado | En 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. |
| densa | Busca el subgrafo más denso (en términos de grados de enlace qubit) del backend para utilizarlo como qubits iniciales. |
| Trivial | Asigna el qubit virtual 0 al qubit físico 0, y así sucesivamente. |
| sable | Utiliza 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:
- En primer lugar, utiliza
VF2Layoutpara intentar encontrar una disposición «perfecta». 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 hay garantía de que encontremos distribuciones perfectas, aunque existan, pero la probabilidad aumenta con el nivel de optimización. - Si no se encuentra una disposición perfecta, utiliza
SabreLayoutpara elegir una disposición inicial, de modo que el número de intentos de disposición inicial, de intentos de permutación y de iteraciones hacia adelante y hacia atrás aumente a medida que aumenta 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. Esta pasada busca el subgrafo conectado más denso del grafo de conectividad completo de destino, donde «más denso» significa que se da preferencia a los qubits de hardware con el mayor número de conexiones disponibles. La correspondencia entre los qubits virtuales y los de hardware se completa asignando los qubits virtuales de mayor grado a los qubits de hardware de mayor grado.
Se trata de una heurística relativamente económica para elegir una disposición inicial, pero suele ofrecer una calidad de resultado mucho peor que los métodos basados en Sabre. El complemento de diseño predeterminado utiliza la asignación inicial seleccionada por DenseLayout como uno de sus diseños iniciales para inicializar 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 de hardware con el mismo índice, de modo que el qubit virtual 0 se asigna al qubit de 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 una disposición inicial, empleando el algoritmo de enrutamiento Sabre modificado de Qiskit como subrutina para realizar el swap-map del circuito candidato tanto en sentido directo como inverso.
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
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.
En la fase de enrutamiento no es necesario garantizar que todas las puertas del circuito sean válidas para la ISA de destino. Por ejemplo, un complemento de enrutamiento puede dejar puertas literales swap en el circuito, aunque este Target no contenga SwapGate. Sin embargo, debe haber al menos una puerta de dos qubits definida en el Target circuito para cualquier par de qubits de hardware al que se le aplique una puerta en el circuito.
La etapa de enrutamiento debe establecer las final_layout propiedades y virtual_permutation_layout en el PropertySet si se ha llevado a cabo el enrutamiento.
Todas las etapas de enrutamiento integradas en Qiskit ejecutarán además la etapa VF2PostLayout «pass» tras el enrutamiento. Esto podría modificar la disposición inicial, si se encuentran qubits con menos errores. Esta pasada es muy similar a la VF2Layout clase que utiliza el complemento de diseño predeterminado, salvo que en VF2PostLayout este caso podemos garantizar que existe al menos un subgrafo inducido isomórfico de la topología de destino que coincide con la topología del circuito.
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.
Se sabe que determinar el número mínimo de intercambios que hay que insertar es un problema no polinomial. Esto significa que intentarlo resulta prohibitivamente caro, por lo que muchos de los algoritmos integrados en Qiskit son estocásticos, y es posible que se observen grandes variaciones entre las distintas compilaciones. Si necesitas que los resultados sean reproducibles, asegúrate de establecer el seed_transpiler argumento 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 predeterminado | Utilizar un método de enrutamiento por defecto elegido por Qiskit. |
| sable | Valor predeterminado. Utiliza el algoritmo de enrutamiento Sabre modificado de Qiskit para intercambiar el mapa. |
| ninguno | Desactivar enrutamiento. Genera un error si se requiere enrutamiento. |
| básico | Inserción de intercambio codicioso para encaminar una sola operación a la vez. |
| mirar hacia delante | Búsqueda por orden de prioridad con poda heurística para encontrar intercambios que hagan que las puertas sean ejecutables. |
Complemento default integrado
Utiliza el 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; sin embargo, en la práctica, normalmente el complemento predeterminado integrado para la etapa de diseño ejecutará el algoritmo de enrutamiento basado en Sabre, y la etapa de enrutamiento solo se utilizará 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 solo afecta a la cantidad de trabajo que realiza el VF2PostLayout paso para intentar mejorar la disposición inicial tras el trazado.
Este método suele dar resultados de baja calidad.
Complemento lookahead integrado
Utiliza el LookaheadSwap algoritmo para calcular la ruta. Se trata, en esencia, de una búsqueda en anchura para generar una red de intercambios, en la que el árbol que se explora se reduce a un pequeño número de intercambios candidatos en cada nivel de 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 influye en la profundidad de búsqueda, el grado de poda por profundidad y la cantidad de trabajo que realiza VF2PostLayout para optimizar posteriormente la disposición 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 calcular la ruta. Esto 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 influye en el número de semillas estocásticas diferentes que se prueban para el enrutamiento completo, así como en la cantidad de trabajo que VF2PostLayout realiza para optimizar posteriormente la disposición 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
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 predeterminado | Utilizar un método de traducción por defecto elegido por Qiskit. |
| traductor | Traducción simbólica de puertas a la base de destino utilizando equivalencias conocidas. |
| síntesis | Recoge 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
Agrupa las secuencias de puertas de los mismos qubits en forma de matriz y, a continuación, vuelve a sintetizarlas utilizando el UnitarySynthesis paso (con la configuración unitary_synthesis_method). Esto es, en gran medida, similar al propio bucle de optimización en niveles altos 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 algoritmo para traducir simbólicamente las puertas a la base de destino. A grandes rasgos, el proceso parte del conjunto de puertas solicitadas por el circuito y utiliza las reglas de un determinado EquivalenceLibrary (normalmente el SessionEquivalenceLibrary) para avanzar hacia la 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
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.
Los requisitos que debe cumplir un complemento de optimización son muy pocos: basta con que acepte circuitos compatibles con ISA y devuelva circuitos compatibles con ISA. Un complemento de optimización suele contener un bucle, como el DoWhileController, y puede incluir la etapa de traducción configurada como un proceso 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 predeterminado | Un 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
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.
PropertySetEn la fase de programación se debe establecer la node_start_time propiedad en el. del proceso.
Al escribir plugins de escenario, el punto de entrada para scheduling es qiskit.transpiler.scheduling. Los plugins incorporados son:
Método | Resumen |
|---|---|
| valor predeterminado | Intentar satisfacer las restricciones de alineación temporal sin programar de otro modo. |
| alap | Programe el circuito, prefiriendo que las operaciones se realicen lo más tarde posible. |
| Lo antes posible | Programar 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
Programa explícitamente todas las operaciones siguiendo la estrategia de «lo más tarde posible». Esto utiliza el ALAPScheduleAnalysis algoritmo para decidir dónde colocar las puertas.
Complemento asap integrado
Programa explícitamente todas las operaciones siguiendo una estrategia de «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 pasos predefinidos, también es posible crear un gestor de pasos para diseñar un proceso totalmente personalizado destinado a transformar los circuitos de entrada. Puedes utilizar la StagedPassManager clase directamente para hacerlo. Puedes definir nombres de etapas arbitrarios y asignarles 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 ningún límite en cuanto al número de etapas que puedes incluir en un StagedPassManager. No es necesario que las etapas se correspondan con las que utilizan los flujos de trabajo predefinidos de Qiskit.
Las funciones del generador Stage pueden resultar útiles para la creación de instancias personalizadas StagedPassManager . Generan gestores de pases que proporcionan funcionalidades comunes utilizadas en muchas etapas. Por ejemplo, generate_embed_passmanager() genera un PassManager para «integrar» un inicial Layout seleccionado de una pasada de diseño en el dispositivo de destino especificado.
Escribir pasadas de transpilador personalizadas
Qiskit está diseñado para ampliarse con pases de transpilador personalizados y especializados.
Hay dos tipos de pasadas del transpilador: las pasadas de «análisis» (AnalysisPass), que leen un circuito y escriben propiedades de análisis globales en el PropertySet; y las pasadas de «transformación» (TransformationPass), que o bien modifican un DAGCircuit in situ, o bien devuelven un nuevo DAGCircuit. Históricamente, Qiskit intentó separar claramente estos dos tipos. TransformationPassSin embargo, en la versión actual de Qiskit es bastante habitual incluir tanto el análisis como las modificaciones en un único archivo independiente, en lugar de intentar separarlo todo. Aunque tu pase se base exclusivamente en el análisis, sigue siendo adecuado utilizar AnalysisPass.
Principios generales sobre la autoría de pases
DAGCircuitSi quieres modificar o crear uno nuevo, debes escribir un TransformationPass. Si solo quieres escribir en el PropertySet y no modificar el DAGCircuit, deberías escribir un AnalysisPass. Si quieres hacer ambas cosas, escribe un TransformationPass. En ambos casos, el único método obligatorio es BasePass.run(), que constituye la parte fundamental de tu paso. Esto debería aceptar un único argumento (que no sea self), dag: DAGCircuit. AnalysisPassTranspilerPassSi a, debe devolver un DAGCircuit (que puede ser la entrada, si se modifica in situ), mientras que si a, debe devolver None.
Si su pase tiene un inicializador, debe llamar a super().__init__().
Normalmente, tu «pass» debería aceptar un Target en su inicializador, que describe el hardware cuántico para el que estás compilando. No se recomienda aceptar restricciones «vagas», como un mapa de acoplamiento independiente y una lista de puertas básicas; el Target describe de forma más precisa el hardware heterogéneo en general.
Durante la ejecución de un PassManager pipeline, cuando se invoca el run() método de tu paso, puedes acceder al atributo self.property_set para obtener el estado actual PropertySet de la transpilación. Debes leer y escribir en este archivo sin modificarlo. Tu pase debe indicar claramente qué atributos, si los hay, del conjunto de propiedades 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 ordenación se encuentran en
DAGCircuit.op_nodes(). Por el contrario,topological_op_nodes()incluye de forma predeterminada una clave de ordenación que hace que su orden no se vea afectado en absoluto por el orden de eliminación o inserción de los nodos, por lo que es totalmente determinista siempre que se especifique el mismo conjunto de nodos con el mismo flujo de datos, incluso si se ha construido en un orden diferente. - Los bordes de ordenación se encuentran en
DAGCircuit.edges(). DAGCircuit.collect_2q_runs()El orden en el que se devuelven las ejecuciones y el orden exacto en el que se encuentran los nodos de una ejecución.- El orden en que se encuentran los nodos en métodos con orden degenerado, 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 recorrer contenedores basados en hash. La iteración sobre Python no es
setdeterminista debido a la aleatorización de la semilla del hash. En Rust, la iteración sobre los contenedores basados en hash de la biblioteca estándar, incluidos los equivalenteshashbrowncon sus hashers predeterminados, no es determinista.NotaLa iteración sobre las listas de Python *es *
dictdeterminista, y se garantiza que sigue el orden de inserción si no se han producido eliminaciones, y un orden arbitrario, aunque también determinista, si se han producido eliminaciones deterministas.En Python, si necesitas crear un
sety luego recorrerlo, plantéate utilizar en su lugar undicten el que todos los valores seanNonecomo alternativa. Utilizar un exclusivamentesetpara comprobar si un elemento pertenece a un conjunto no supone ningún problema.En Rust, utiliza y
indexmapsus estructurasIndexMapy comoIndexSetsustitutos deHashMapyHashSet, 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
seedvalor 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 objetopRNGa partir de dicha semilla al inicio de cada llamada aBasePass.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 derayonrecogerá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 código QuantumCircuit para un backend específico, el transpilador necesita una representación especializada de dicho backend, que incluya sus restricciones, su conjunto de instrucciones, las propiedades de los qubits y otros aspectos, a fin de poder compilar y optimizar de forma eficaz. Aunque la BackendV2 clase define una interfaz para consultar e interactuar con los backends, su ámbito de aplicación va más allá de las necesidades del transpilador, ya que incluye la gestión del envío de trabajos y, potencialmente, la interconexión con servicios remotos. La información específica que necesita el transpilador se describe en la Target clase
Por ejemplo, para crear un objeto sencillo Target , se pueden ir añadiendo de forma iterativa las 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.2Esto Target representa un backend de 3 qubits que admite CXGate entre los qubits 0 y 1, UGate en los qubits 0 y 1, RZGate, RXGate, y 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 procedente de Target. Por ejemplo, la CouplingMap clase se utiliza exclusivamente 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 Target.build_coupling_map() método. Estas estructuras de datos suelen ser anteriores a la Target clase, pero siguen utilizándose en algunas pasadas del transpilador que aún no funcionan de forma nativa con una Target instancia de, o cuando se trabaja con backends que no utilizan la última BackendV2 interfaz.
Por ejemplo, si quisiéramos visualizar el CouplingMap del ejemplo de 3 qubits Target anterior:
target.build_coupling_map().draw()
Esto muestra la conectividad global de, Target que es la combinación de los qubits compatibles con CXGate y CZGate. CouplingMap.build_coupling_map()Para ver la conectividad de cada conexión, puedes introducir el nombre de la operación en:
target.build_coupling_map('cx').draw()
target.build_coupling_map('cz').draw()
Programación de circuitos
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:
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')
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() :
La programación de un circuito consta de dos partes: el análisis y la asignación de restricciones, seguidas de una pasada de relleno. La primera parte requiere ejecutar una pasada de análisis de programación, como ALAPSchedulingAnalysis o ASAPSchedulingAnalysis , que analiza el circuito y registra la hora de inicio de cada instrucción del circuito utilizando un algoritmo de programación («lo más tarde posible» para ALAPSchedulingAnalysis y «lo antes posible» para ASAPSchedulingAnalysis) incluido en el conjunto de propiedades. Una vez que el circuito cuenta con una programación inicial, se pueden ejecutar pasadas adicionales para tener en cuenta cualquier restricción de sincronización en el backend de destino, como las restricciones de alineación. Esto suele hacerse mediante el ConstrainedReschedule paso que ajustará la programación definida en el conjunto de propiedades a las restricciones del backend de destino. Una vez finalizados todos los procesos de programación y los ajustes o reprogramaciones, se ejecuta una pasada de relleno, como PadDelay o PadDynamicalDecoupling , para insertar las instrucciones en el circuito, con lo que se completa la programación.
API del transpilador
Descripción del hardware
Columna « 1 » | Columna « 2 » |
|---|---|
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
Columna « 1 » | Columna « 2 » |
|---|---|
StagedPassManager( [etapas] ) | Una canalización de gestores de pases construida a partir de etapas individuales. |
PassManager( [pasadas, max_iteration] ) | Gestor de un conjunto de Pases y su programación durante la transpilación. |
PassManagerConfig( [initial_layout,...] ) | Configuración de Pass Manager. |
PassManagerCliffordTConfig( [initial_layout,...] ) | Configuración de Pass Manager para la transpilación de Clifford+T. |
generate_preset_pass_manager([...]) | Generar un ajuste predefinido PassManager |
generate_preset_clifford_t_pass_manager([...]) | StagedPassManagerGenerar un ajuste preestablecido de Clifford+T. |
generate_preset_pbc_pass_manager([...]) | StagedPassManagerGenerar un PBC predefinido. |
Diseño y topología
Columna « 1 » | Columna « 2 » |
|---|---|
Layout( [input_dict] ) | Dictado bidireccional para representar un Layout. |
CouplingMap( [lista de acoplamientos, descripción] ) | Grafo dirigido que especifica el acoplamiento fijo. |
TranspileLayout(initial_layout,...[,...] ) | Atributos de diseño para el circuito de salida del transpilador. |
Planificación
Columna « 1 » | Columna « 2 » |
|---|---|
InstructionDurations( [duraciones_de_las_clases, dt] ) | Clase auxiliar para proporcionar duraciones de instrucciones para la programación. |
Pases abstractos
Columna « 1 » | Columna « 2 » |
|---|---|
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)
Bases: TranspilerAccessError
Excepciones planteadas durante la transpilación.
Establece el mensaje de error.
TranspilerAccessError
exception qiskit.transpiler.TranspilerAccessError(*message)
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)
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)
Bases: QiskitError
Errores generados por el objeto de diseño.
Establece el mensaje de error.
CircuitTooWideForTarget
exception qiskit.transpiler.CircuitTooWideForTarget(*message)
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)
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
Columna « 1 » | Columna « 2 » |
|---|---|
OptimizationMetric(*valores) | Métrica de optimización considerada durante la transpilación. |