Definir o nível de otimização do transpiler
O código desta página foi desenvolvido usando os seguintes requisitos. Recomendamos o uso dessas versões ou de versões mais recentes.
qiskit[all]~=2.5.0 qiskit-ibm-runtime~=0.47.0
Os dispositivos quânticos reais estão sujeitos a ruídos e erros de porta, portanto, a otimização dos circuitos para reduzir sua profundidade e contagem de portas pode melhorar significativamente os resultados obtidos com a execução desses circuitos.
A função generate_preset_pass_manager tem um argumento posicional obrigatório, optimization_level, que controla o esforço que o transpilador faz para otimizar os circuitos. Esse argumento pode ser um número inteiro com um dos valores 0, 1, 2 ou 3.
Níveis de otimização mais altos geram circuitos mais otimizados à custa de tempos de compilação mais longos.
A tabela a seguir explica as otimizações realizadas com cada configuração.
Nível de Otimização | Descrição |
|---|---|
| 0 | Sem otimização: normalmente usado para caracterização de hardware
|
| 1 | Otimização da luz:
|
| 2 | Otimização média:
|
| 3 | Alta otimização:
|
Nível de otimização em ação
Como as portas de dois qubits normalmente são 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, tentaremos os diferentes níveis de otimização em um circuito de entrada que consiste em uma unidade aleatória seguida por uma porta SWAP.
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:
Nestes exemplos, é utilizado o backend FakeSherbrooke``qiskit_ibm_runtime simulado do, mas você pode testá-lo em qualquer backend real ou simulado compatível com o Qiskit. Seus resultados podem ser diferentes.
Primeiro, vamos fazer a transpilagem 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)Output:
O circuito transpilado tem seis portas ECR de dois qubits.
Repita o procedimento 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)Output:
O circuito transpilado ainda tem seis portas ECR, mas o número de portas de um único qubit foi reduzido.
Repita o procedimento 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)Output:
Isso produz os mesmos resultados do nível de otimização 1. Observe 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)Output:
Agora, há apenas três portões ECR. Obtivemos esse resultado porque, no nível de otimização 3, o Qiskit tenta sintetizar novamente blocos de portas de dois qubits, 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 como um valor menor que 1, permitindo que o transpilador faça aproximações que podem introduzir algum erro na decomposição da porta (consulte 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)Output:
Esse 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 esse 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, que são 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}"
)Output:
Synthesis fidelity
Exact: 1.000+0.000j
Approximate: 0.992+0.000j
Ajustar o nível de otimização também pode alterar outros aspectos do circuito, não apenas o número de portas ECR. Para ver exemplos de como a definição do nível de otimização altera o layout, consulte Representação de computadores quânticos.
Próximas etapas
- Para saber mais sobre a função
generate_preset_passmanager, comece pelo tópico Definições padrão de transpilação e opções de configuração. - Continue aprendendo sobre transpilação com o tópico Transpiler stages.
- Experimente o guia Comparar configurações do transpiler.
- Experimente o tutorial Construir códigos de repetição.
- Consulte a documentação da API Transpile.