Pular para o conteúdo principal

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çãoDescrição
0

Sem otimização: tipicamente usado para caracterização de hardware

  • Tradução básica
  • Layout/Roteamento: TrivialLayout, que seleciona os mesmos números de qubits físicos que os virtuais e insere SWAPs para fazer funcionar (usando SabreSwap)
1

Otimização leve:

  • Layout/Roteamento: O layout é tentado primeiro com TrivialLayout. Se SWAPs adicionais forem necessários, um layout com número mínimo de SWAPs é encontrado usando SabreSwap, e então VF2LayoutPostLayout é usado para tentar selecionar os melhores qubits no grafo.
  • InverseCancellation
  • Otimização de portas de 1 qubit
2

Otimização média:

  • Layout/Roteamento: Nível de otimização 1 (sem trivial) + heurística otimizada com maior profundidade de busca e tentativas da função de otimização. Como TrivialLayout não é usado, não há tentativa de usar os mesmos números de qubits físicos e virtuais.
  • CommutativeCancellation
3

Otimização alta:

  • Nível de otimização 2 + heurística otimizada em layout/roteamento com maior esforço/tentativas
  • Ressíntese de blocos de dois qubits usando a Decomposição KAK de Cartan.
  • Passos que quebram a unitariedade:
    • OptimizeSwapBeforeMeasure: Move as medições para evitar SWAPs
    • RemoveDiagonalGatesBeforeMeasure: Remove portas antes de medições que não afetariam os resultados

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")

Output of the previous code cell

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)

Output of the previous code cell

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)

Output of the previous code cell

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)

Output of the previous code cell

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)

Output of the previous code cell

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)

Output of the previous code cell

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

Recomendações