Etapas 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
Esta página descreve os estágios do pipeline de transpilação pré-construído no Qiskit SDK. Há seis estágios:
initlayoutroutingtranslationoptimizationscheduling
A função generate_preset_pass_manager cria um gerenciador de passagens em estágios predefinido composto por esses estágios. As passagens específicas que compõem cada estágio dependem dos argumentos passados para generate_preset_pass_manager. O optimization_level é um argumento posicional que deve ser especificado; é um número inteiro que pode ser 0, 1, 2 ou 3. Valores mais altos indicam uma otimização mais pesada, porém mais cara (consulte Padrões de transpilação e opções de configuração ).
A maneira recomendada de transpilar um circuito é criar um gerenciador de passagens predefinido e, em seguida, executar esse gerenciador de passagens no circuito, conforme descrito em Transpilar com gerenciadores de passagens. No entanto, uma alternativa mais simples, mas menos personalizável, é usar a função transpile função. Essa função aceita o circuito diretamente como um argumento. Assim como em generate_preset_pass_manager, as passagens específicas do transpilador usadas dependem dos argumentos, como optimization_level, passados para transpile. De fato, internamente, a função transpile chama generate_preset_pass_manager para criar um gerenciador de passagens em etapas predefinido e o executa no circuito.
Fase inicial
Esse primeiro estágio faz muito pouco por padrão e é útil principalmente se você quiser incluir suas próprias otimizações iniciais. Como a maioria dos algoritmos de layout e roteamento só foi projetada para trabalhar com portas de um e dois qubits, esse estágio também é usado para traduzir todas as portas que operam em mais de dois qubits em portas que operam somente em um ou dois qubits.
Para obter mais informações sobre a implementação de suas próprias otimizações iniciais para esse estágio, consulte a seção sobre plug-ins e personalização de gerenciadores de passes.
Fase de layout
A próxima etapa envolve a configuração ou a conectividade do backend para o qual o circuito será enviado. Em geral, os circuitos quânticos são entidades abstratas cujos qubits são representações “virtuais” ou “lógicas” dos qubits reais utilizados nos cálculos. Para executar uma sequência de portas, é necessário um mapeamento um-para-um entre os qubits “virtuais” e os qubits “físicos” em um dispositivo quântico real. Esse mapeamento é armazenado como um Layout objeto e faz parte das restrições definidas na arquitetura do conjunto de instruções (ISA) de um backend.
A escolha do mapeamento é extremamente importante para minimizar o número de operações SWAP necessárias para mapear o circuito de entrada na topologia do dispositivo e garantir que os qubits mais bem calibrados sejam usados. Devido à importância dessa etapa, os gerentes de passes predefinidos tentam alguns métodos diferentes para encontrar o melhor layout. Normalmente, isso envolve duas etapas: primeiro, tentar encontrar um layout "perfeito" (um layout que não exija nenhuma operação SWAP) e, em seguida, uma passagem heurística que tenta encontrar o melhor layout a ser usado se não for possível encontrar um layout perfeito. Há dois sites Passes normalmente usados para essa primeira etapa:
TrivialLayout: Mapeia ingenuamente cada qubit virtual para o mesmo qubit físico numerado no dispositivo (ou seja, [0,1,1,3] -> [0,1,1,3] ). Este é um comportamento histórico usado apenas emoptimzation_level=1para tentar encontrar um layout perfeito. Em caso de falha,VF2Layouté a próxima tentativa.VF2Layout: Este é umAnalysisPassque seleciona um layout ideal tratando esse estágio como um problema de isomorfismo de subgrafo, resolvido pelo algoritmo VF2++. Se mais de um layout for encontrado, uma heurística de pontuação será executada para selecionar o mapeamento com o menor erro médio.
Em seguida, para o estágio heurístico, duas passagens são usadas por padrão:
DenseLayout: Encontra o subgrafo do dispositivo com a maior conectividade e que tem o mesmo número de qubits que o circuito (usado para o nível de otimização 1 se houver operações de fluxo de controle (como IfElseOp ) presentes no circuito).SabreLayout: Esta passagem seleciona um layout a partir de um layout aleatório inicial e executando repetidamente oSabreSwapalgoritmo. Esta passagem só é usada nos níveis de otimização 1, 2 e 3 se um layout perfeito não for encontrado através daVF2Layoutpassagem. Para obter mais detalhes sobre este algoritmo, consulte o artigo “ arXiv:1809.02573 ” (Algoritmo de classificação de dados de vídeo baseado em características de movimento).
Etapa de roteamento
Para implementar uma porta de dois qubits entre qubits que não estejam diretamente conectados em um dispositivo quântico, uma ou mais portas SWAP devem ser inseridas no circuito para mover os estados dos qubits até que fiquem adjacentes no mapa de portas do dispositivo. Cada porta SWAP representa uma operação cara e ruidosa a ser executada. Portanto, encontrar o número mínimo de portas SWAP necessárias para mapear um circuito em um determinado dispositivo é uma etapa importante no processo de transpilação. Para fins de eficiência, esse estágio é normalmente computado junto com o estágio de Layout por padrão, mas eles são logicamente distintos um do outro. O estágio de Layout seleciona os qubits de hardware a serem usados, enquanto o estágio de Roteamento insere a quantidade adequada de portas SWAP para executar os circuitos usando o layout selecionado.
No entanto, é difícil encontrar o mapeamento SWAP ideal. De fato, é um problema NP difícil e, portanto, proibitivamente caro de calcular para todos os dispositivos quânticos e circuitos de entrada, exceto os menores. Para contornar esse problema, o Qiskit usa um algoritmo heurístico estocástico chamado SabreSwap para calcular um mapeamento SWAP bom, mas não necessariamente ideal. O uso de um método estocástico significa que não há garantia de que os circuitos gerados sejam os mesmos em execuções repetidas. De fato, a execução do mesmo circuito repetidamente resulta em uma distribuição de profundidades de circuito e contagens de portas na saída. É por esse motivo que muitos usuários optam por executar a função de roteamento (ou todo o site StagedPassManager) várias vezes e selecionar os circuitos de menor profundidade na distribuição de saídas.
Por exemplo, vamos considerar um circuito GHZ de 15 qubits executado 100 vezes, usando um circuito "ruim" (desconectado) initial_layout.
import matplotlib.pyplot as plt
from qiskit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.providers.fake_provider import GenericBackendV2
backend = GenericBackendV2(15)
ghz = QuantumCircuit(15)
ghz.h(0)
ghz.cx(0, range(1, 15))
depths = []
for seed in range(100):
pass_manager = generate_preset_pass_manager(
optimization_level=1,
backend=backend,
layout_method="trivial", # Fixed layout mapped in circuit order
seed_transpiler=seed, # For reproducible results
)
depths.append(pass_manager.run(ghz).depth())
plt.figure(figsize=(8, 6))
plt.hist(depths, align="left", color="#AC557C")
plt.xlabel("Depth", fontsize=14)
plt.ylabel("Counts", fontsize=14)Output:
Text(0, 0.5, 'Counts')
Essa ampla distribuição demonstra como é difícil para o mapeador SWAP calcular o melhor mapeamento. Para ter uma ideia, vamos examinar o circuito que está sendo executado e os qubits que foram escolhidos no hardware.
ghz.draw("mpl", idle_wires=False)Output:
from qiskit.visualization import plot_circuit_layout
# Plot the hardware graph and indicate which hardware qubits were chosen to run the circuit
transpiled_circ = pass_manager.run(ghz)
plot_circuit_layout(transpiled_circ, backend)Output:
Como você pode ver, esse circuito precisa executar uma porta de dois qubits entre os qubits 0 e 14, que estão muito distantes no gráfico de conectividade. Portanto, a execução desse circuito requer a inserção de portas SWAP para executar todas as portas de dois qubits usando a passagem SabreSwap .
Observe também que o algoritmo SabreSwap é diferente do método SabreLayout maior do estágio anterior. Por padrão, o site SabreLayout executa o layout e o roteamento e retorna o circuito transformado. Isso é feito por alguns motivos técnicos específicos especificados na página de referência da API do passe.
Fase de tradução
Ao escrever um circuito quântico, você tem liberdade para usar qualquer porta quântica (operação unitária) que desejar, juntamente com um conjunto de operações que não sejam portas, como medição de qubits ou instruções de reinicialização. No entanto, a maioria dos dispositivos quânticos suporta nativamente apenas um número limitado de operações de portas quânticas e de operações que não envolvem portas. Essas portas nativas fazem parte da definição da ISA de um destino, e esta etapa da pré-configuração PassManagers traduz (ou desdobra ) as portas especificadas em um circuito para as portas da base nativa de um backend especificado. Este é um passo importante, pois permite que o circuito seja executado pelo backend, mas geralmente leva a um aumento na profundidade e no número de portas lógicas.
É especialmente importante destacar dois casos especiais que ajudam a ilustrar o que esse estágio faz.
- Se uma porta SWAP não for uma porta nativa para o backend de destino, serão necessárias três portas CNOT:
print("native gates:" + str(sorted(backend.operation_names)))
qc = QuantumCircuit(2)
qc.swap(0, 1)
qc.decompose().draw("mpl")Output:
native gates:['cx', 'delay', 'id', 'measure', 'reset', 'rz', 'sx', 'x']
Como um produto de três portas CNOT, um SWAP é uma operação cara para ser executada em dispositivos quânticos com ruído. No entanto, essas operações geralmente são necessárias para incorporar um circuito nas conectividades de porta limitadas de muitos dispositivos. Assim, minimizar o número de portas SWAP em um circuito é um objetivo primordial no processo de transpilação.
- Um Toffoli, ou porta controlada-controlada-não controlada (
ccx), é uma porta de três qubits. Como nosso conjunto de portas de base inclui apenas portas de um e dois qubits, essa operação deve ser decomposta. No entanto, ele é bastante caro:
qc = QuantumCircuit(3)
qc.ccx(0, 1, 2)
qc.decompose().draw("mpl")Output:
Para cada porta Toffoli em um circuito quântico, o hardware pode executar até seis portas CNOT e um punhado de portas de um único qubit. Esse exemplo demonstra que qualquer algoritmo que faça uso de várias portas Toffoli terminará como um circuito com grande profundidade e, portanto, será consideravelmente afetado pelo ruído.
Fase de otimização
Esse estágio se concentra na decomposição dos circuitos quânticos no conjunto de portas de base do dispositivo de destino e deve lutar contra o aumento da profundidade dos estágios de layout e roteamento. Felizmente, há muitas rotinas para otimizar os circuitos combinando ou eliminando portas. Em alguns casos, esses métodos são tão eficazes que os circuitos de saída têm uma profundidade menor do que as entradas, mesmo após o layout e o roteamento para a topologia do hardware. Em outros casos, não é possível fazer muita coisa, e o cálculo pode ser difícil de ser realizado em dispositivos com ruído. É nesse estágio que os vários níveis de otimização começam a se diferenciar.
- Para
optimization_level=1, esse estágio preparaOptimize1qGatesDecompositioneCXCancellationque combinam cadeias de portas de um único qubit e cancelam quaisquer portas CNOT consecutivas. - Para
optimization_level=2, esse estágio usa a passagemCommutativeCancellationem vez deCXCancellation, que remove portas redundantes ao explorar as relações de comutação. - Para
optimization_level=3, esse estágio prepara os seguintes passes:
Além disso, esse estágio também executa algumas verificações finais para garantir que todas as instruções no circuito sejam compostas pelas portas de base disponíveis no backend de destino.
O exemplo abaixo, usando um estado GHZ, demonstra os efeitos de diferentes configurações de nível de otimização na profundidade do circuito e na contagem de portas.
A saída da transpilação varia devido ao mapeador SWAP estocástico. Portanto, os números abaixo provavelmente mudarão cada vez que você executar o código.

O código a seguir constrói um estado GHZ de 15 qubits e compara o optimization_levels da transpilação em termos de profundidade do circuito resultante, contagem de portas e contagem de portas de vários qubits.
ghz = QuantumCircuit(15)
ghz.h(0)
ghz.cx(0, range(1, 15))
depths = []
gate_counts = []
multiqubit_gate_counts = []
levels = [str(x) for x in range(4)]
for level in range(4):
pass_manager = generate_preset_pass_manager(
optimization_level=level,
backend=backend,
seed_transpiler=1234,
)
circ = pass_manager.run(ghz)
depths.append(circ.depth())
gate_counts.append(sum(circ.count_ops().values()))
multiqubit_gate_counts.append(circ.count_ops()["cx"])
fig, (ax1, ax2) = plt.subplots(2, 1)
ax1.bar(levels, depths, label="Depth")
ax1.set_xlabel("Optimization Level")
ax1.set_ylabel("Depth")
ax1.set_title("Output Circuit Depth")
ax2.bar(levels, gate_counts, label="Number of Circuit Operations")
ax2.bar(levels, multiqubit_gate_counts, label="Number of CX gates")
ax2.set_xlabel("Optimization Level")
ax2.set_ylabel("Number of gates")
ax2.legend()
ax2.set_title("Number of output circuit gates")
fig.tight_layout()
plt.show()Output:
Planejamento
Esse último estágio só é executado se for explicitamente solicitado (semelhante ao estágio Init) e não é executado por padrão (embora um método possa ser especificado definindo o argumento scheduling_method ao chamar generate_preset_pass_manager). O estágio de programação é normalmente usado depois que o circuito foi traduzido para a base de destino, mapeado para o dispositivo e otimizado. Essas passagens se concentram na contabilização de todo o tempo ocioso em um circuito. Em um nível mais alto, a passagem de agendamento pode ser considerada como a inserção explícita de instruções de atraso para levar em conta o tempo ocioso entre as execuções do portão e para inspecionar por quanto tempo o circuito será executado no backend.
Aqui está um exemplo:
ghz = QuantumCircuit(5)
ghz.h(0)
ghz.cx(0, range(1, 5))
# Use fake backend
backend = GenericBackendV2(5)
# Run with optimization level 3 and 'asap' scheduling pass
pass_manager = generate_preset_pass_manager(
optimization_level=3,
backend=backend,
scheduling_method="asap",
seed_transpiler=1234,
)
circ = pass_manager.run(ghz)
circ.draw(output="mpl", idle_wires=False)Output:
O transpilador inseriu Delay instruções para contabilizar o tempo ocioso em cada qubit. Para ter uma ideia melhor do tempo do circuito, também podemos examiná-lo com a função timeline.draw() :
O agendamento de um circuito envolve duas partes: análise e mapeamento de restrições, seguido de uma passagem de preenchimento. A primeira parte requer a execução de uma passagem de análise de programação (por padrão, é ALAPSchedulingAnalysis), que analisa o circuito e registra a hora de início de cada instrução no circuito em um cronograma. Uma vez que o circuito tenha um cronograma inicial, passes adicionais podem ser executados para levar em conta quaisquer restrições de tempo no backend de destino. Por fim, um passe de preenchimento, como PadDelay ou PadDynamicalDecoupling pode ser executada.
Próximas etapas
- Para saber como usar 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 with pass managers.
- Experimente o guia Comparar configurações do transpiler.
- Consulte a documentação da API Transpile.