Skip to main content
IBM Quantum Platform

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

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

Otimização da luz:

  • Layout/Roteamento: O layout é tentado primeiro com TrivialLayout. Se forem necessários SWAPs adicionais, um layout com um número mínimo de SWAPs é encontrado usando SabreSwap e, em seguida, ele usa VF2LayoutPostLayout para tentar selecionar os melhores qubits no gráfico.
  • InverseCancellation
  • 1Q otimização de portões
2

Otimização média:

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

Alta otimização:

  • Nível de otimização 2 + heurística otimizada no layout/roteiro com mais esforço/tentativas
  • Ressíntese de blocos de dois qubits usando a decomposição KAK de Cartan.
  • Passes de quebra de unidade:
    • OptimizeSwapBeforeMeasure: Move as medições para evitar SWAPs
    • RemoveDiagonalGatesBeforeMeasure: Remove as portas antes das medições que não afetariam as medições

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:

Output of the previous code cell
Note

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:

Output of the previous code cell

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:

Output of the previous code cell

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:

Output of the previous code cell

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:

Output of the previous code cell

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:

Output of the previous code cell

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

Recomendações
Esta página foi útil?
Relate um bug, erro de digitação ou solicite conteúdo no GitHub.