Definir o nível de otimização do transpilador
Versões dos pacotes
O código desta página foi desenvolvido com os seguintes requisitos. Recomendamos o uso dessas versões ou de versões mais recentes.
qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
Dispositivos quânticos reais estão sujeitos a ruído e erros de porta, portanto otimizar os circuitos para reduzir sua profundidade e contagem de portas pode melhorar significativamente os resultados obtidos ao executar esses circuitos.
A função generate_preset_pass_manager possui um argumento posicional obrigatório, optimization_level, que controla o quanto de esforço o transpilador dedica à otimização dos circuitos. Esse argumento pode ser um inteiro com um dos valores 0, 1, 2 ou 3.
Níveis de otimização mais altos geram circuitos mais otimizados ao custo de tempos de compilação maiores.
A tabela a seguir explica as otimizações realizadas para cada configuração.
| Nível de Otimização | Descrição |
|---|---|
| 0 | Sem otimização: tipicamente usado para caracterização de hardware
|
| 1 | Otimização leve:
|
| 2 | Otimização média:
|
| 3 | Otimização alta:
|
Nível de otimização em ação
Como portas de dois qubits são tipicamente a fonte mais significativa de erros, podemos quantificar aproximadamente a "eficiência de hardware" da transpilação contando o número de portas de dois qubits no circuito resultante. Aqui, vamos testar os diferentes níveis de otimização em um circuito de entrada composto por uma unitária aleatória seguida de uma porta 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 o backend simulado FakeSherbrooke nos nossos exemplos. Primeiro, vamos transpilar usando o nível de otimização 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)
O circuito transpilado possui seis portas ECR de dois qubits.
Repita para o nível de otimização 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)
O circuito transpilado ainda possui seis portas ECR, mas o número de portas de um único qubit foi reduzido.
Repita para o nível de otimização 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)
Isso produz os mesmos resultados que o nível de otimização 1. Note que aumentar o nível de otimização nem sempre faz diferença.
Repita novamente, com o nível de otimização 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)
Agora, há apenas três portas ECR. Obtemos esse resultado porque no nível de otimização 3, o Qiskit tenta ressintesizar blocos de dois qubits de portas, e qualquer porta de dois qubits pode ser implementada usando no máximo três portas ECR. Podemos obter ainda menos portas ECR se definirmos approximation_degree para um valor menor que 1, permitindo que o transpilador faça aproximações que podem introduzir algum erro na decomposição das portas (veja Parâmetros comumente usados para transpilação):
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 tem apenas duas portas ECR, mas é um circuito aproximado. Para entender como seu efeito difere do circuito exato, podemos calcular a fidelidade entre o operador unitário que este circuito implementa e o unitário exato. Antes de realizar o cálculo, primeiro reduzimos o circuito transpilado, que contém 127 qubits, para um circuito que contém apenas os qubits ativos, dos quais há dois.
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 o nível de otimização pode alterar outros aspectos do circuito também, não apenas o número de portas ECR. Para exemplos de como a definição do nível de otimização altera o layout, veja Representando computadores quânticos.
Próximos passos
- Para saber mais sobre a função
generate_preset_passmanager, comece com o tópico Configurações padrão e opções de configuração da transpilação. - Continue aprendendo sobre transpilação com o tópico Estágios do transpilador.
- Experimente o guia Comparar configurações do transpilador.
- Experimente o tutorial Construir códigos de repetição.
- Veja a documentação da API de Transpilação.