Saltar al contenido principal

Establecer el nivel de optimización del transpilador

Versiones de paquetes

El código de esta página fue desarrollado con los siguientes requisitos. Recomendamos usar estas versiones o versiones más recientes.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1

Los dispositivos cuánticos reales están sujetos a ruido y errores de puertas, por lo que optimizar los circuitos para reducir su profundidad y número de puertas puede mejorar significativamente los resultados obtenidos al ejecutar esos circuitos. La función generate_preset_pass_manager tiene un argumento posicional obligatorio, optimization_level, que controla cuánto esfuerzo dedica el transpilador a optimizar los circuitos. Este argumento puede ser un entero que tome uno de los valores 0, 1, 2 o 3. Los niveles de optimización más altos generan circuitos más optimizados a costa de tiempos de compilación más largos. La siguiente tabla explica las optimizaciones que se realizan con cada configuración.

Nivel de optimizaciónDescripción
0

Sin optimización: se usa típicamente para la caracterización del hardware

  • Traducción básica
  • Layout/Enrutamiento: TrivialLayout, que selecciona los mismos números de qubit físicos que virtuales e inserta SWAPs para que funcione (usando SabreSwap)
1

Optimización ligera:

  • Layout/Enrutamiento: primero se intenta el layout con TrivialLayout. Si se requieren SWAPs adicionales, se encuentra un layout con el número mínimo de SWAPs usando SabreSwap, y luego usa VF2LayoutPostLayout para intentar seleccionar los mejores qubits del grafo.
  • InverseCancellation
  • Optimización de puertas de 1 qubit
2

Optimización media:

  • Layout/Enrutamiento: nivel de optimización 1 (sin trivial) + heurística optimizada con mayor profundidad de búsqueda y pruebas de la función de optimización. Como no se usa TrivialLayout, no se intenta usar los mismos números de qubit físico y virtual.
  • CommutativeCancellation
3

Optimización alta:

  • Nivel de optimización 2 + heurística optimizada en layout/enrutamiento con mayor esfuerzo/pruebas
  • Resíntesis de bloques de dos qubits usando la Descomposición KAK de Cartan.
  • Pasadas que rompen la unitariedad:
    • OptimizeSwapBeforeMeasure: mueve las mediciones para evitar SWAPs
    • RemoveDiagonalGatesBeforeMeasure: elimina puertas antes de las mediciones que no afectarían a los resultados

El nivel de optimización en acción

Como las puertas de dos qubits son típicamente la fuente de errores más significativa, podemos cuantificar aproximadamente la "eficiencia hardware" de la transpilación contando el número de puertas de dos qubits en el circuito resultante. Aquí probaremos los diferentes niveles de optimización en un circuito de entrada compuesto por un unitario aleatorio seguido de una puerta SWAP.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime
from qiskit import QuantumCircuit
from qiskit.circuit.library import UnitaryGate
from qiskit.quantum_info import Operator, random_unitary

UU = random_unitary(4, seed=12345)
rand_U = UnitaryGate(UU)

qc = QuantumCircuit(2)
qc.append(rand_U, range(2))
qc.swap(0, 1)
qc.draw("mpl", style="iqp")

Output of the previous code cell

Usaremos el backend simulado FakeSherbrooke en nuestros ejemplos. Primero, transpilemos usando el nivel de optimización 0.

from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime.fake_provider import FakeSherbrooke

backend = FakeSherbrooke()

pass_manager = generate_preset_pass_manager(
optimization_level=0, backend=backend, seed_transpiler=12345
)
qc_t1_exact = pass_manager.run(qc)
qc_t1_exact.draw("mpl", idle_wires=False)

Output of the previous code cell

El circuito transpilado tiene seis puertas ECR de dos qubits.

Repetir para el nivel de optimización 1:

from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime.fake_provider import FakeSherbrooke

backend = FakeSherbrooke()

pass_manager = generate_preset_pass_manager(
optimization_level=1, backend=backend, seed_transpiler=12345
)
qc_t1_exact = pass_manager.run(qc)
qc_t1_exact.draw("mpl", idle_wires=False)

Output of the previous code cell

El circuito transpilado sigue teniendo seis puertas ECR, pero el número de puertas de un solo qubit se ha reducido.

Repetir para el nivel de optimización 2:

pass_manager = generate_preset_pass_manager(
optimization_level=2, backend=backend, seed_transpiler=12345
)
qc_t2_exact = pass_manager.run(qc)
qc_t2_exact.draw("mpl", idle_wires=False)

Output of the previous code cell

Esto produce los mismos resultados que el nivel de optimización 1. Ten en cuenta que aumentar el nivel de optimización no siempre marca la diferencia.

Repetir de nuevo, con el nivel de optimización 3:

pass_manager = generate_preset_pass_manager(
optimization_level=3, backend=backend, seed_transpiler=12345
)
qc_t3_exact = pass_manager.run(qc)
qc_t3_exact.draw("mpl", idle_wires=False)

Output of the previous code cell

Ahora solo hay tres puertas ECR. Obtenemos este resultado porque en el nivel de optimización 3, Qiskit intenta re-sintetizar bloques de puertas de dos qubits, y cualquier puerta de dos qubits puede implementarse usando como máximo tres puertas ECR. Podemos obtener incluso menos puertas ECR si establecemos approximation_degree a un valor menor que 1, permitiendo al transpilador hacer aproximaciones que pueden introducir algo de error en la descomposición de puertas (ver Parámetros de uso común para la transpilación):

pass_manager = generate_preset_pass_manager(
optimization_level=3,
approximation_degree=0.99,
backend=backend,
seed_transpiler=12345,
)
qc_t3_approx = pass_manager.run(qc)
qc_t3_approx.draw("mpl", idle_wires=False)

Output of the previous code cell

Este circuito tiene solo dos puertas ECR, pero es un circuito aproximado. Para entender en qué se diferencia su efecto del circuito exacto, podemos calcular la fidelidad entre el operador unitario que implementa este circuito y el unitario exacto. Antes de realizar el cálculo, primero reducimos el circuito transpilado, que contiene 127 qubits, a un circuito que solo contiene los qubits activos, que son dos.

import numpy as np

def trace_to_fidelity_2q(trace: float) -> float:
return (4.0 + trace * trace.conjugate()) / 20.0

# Reduce circuits down to 2 qubits so they are easy to simulate
qc_t3_exact_small = QuantumCircuit.from_instructions(qc_t3_exact)
qc_t3_approx_small = QuantumCircuit.from_instructions(qc_t3_approx)

# Compute the fidelity
exact_fid = trace_to_fidelity_2q(
np.trace(np.dot(Operator(qc_t3_exact_small).adjoint().data, UU))
)
approx_fid = trace_to_fidelity_2q(
np.trace(np.dot(Operator(qc_t3_approx_small).adjoint().data, UU))
)
print(
f"Synthesis fidelity\nExact: {exact_fid:.3f}\nApproximate: {approx_fid:.3f}"
)
Synthesis fidelity
Exact: 1.000+0.000j
Approximate: 0.992+0.000j

Ajustar el nivel de optimización también puede cambiar otros aspectos del circuito, no solo el número de puertas ECR. Para ver ejemplos de cómo establecer el nivel de optimización cambia el layout, consulta Representación de computadoras cuánticas.

Próximos pasos

Recomendaciones