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ón | Descripción |
|---|---|
| 0 | Sin optimización: se usa típicamente para la caracterización del hardware
|
| 1 | Optimización ligera:
|
| 2 | Optimización media:
|
| 3 | Optimización alta:
|
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")
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)
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)
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)
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)
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)
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
- Para aprender más sobre la función
generate_preset_passmanager, comienza con el tema Configuración predeterminada y opciones de configuración de la transpilación. - Continúa aprendiendo sobre transpilación con el tema Etapas del transpilador.
- Prueba la guía Comparar configuraciones del transpilador.
- Prueba el tutorial Construir códigos de repetición.
- Consulta la documentación de la API de Transpile.