Skip to main content
IBM Quantum Platform

Compare as configurações do transpiler

  • O código desta página foi desenvolvido com base nos seguintes requisitos. Recomendamos usar essas versões ou versões mais recentes.

    qiskit[all]~=2.5.0
    qiskit-ibm-runtime~=0.47.0
    

Diferentes configurações do transpilador proporcionam diferentes tipos de otimização ao circuito, muitas vezes em detrimento de um tempo de processamento clássico mais longo. Este guia orienta o usuário por todo o processo de criação, transpilagem e envio de circuitos, a fim de demonstrar como testar o desempenho de várias configurações.

Observe que a mesma configuração pode melhorar os resultados de um circuito, mas prejudicar outro. Certifique-se de verificar os circuitos transpilados resultantes antes de executá-los em hardware real.


Configurar e criar um circuito de exemplo

# Create circuit to test transpiler on
from qiskit import QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.circuit.library import grover_operator, DiagonalGate

# Use Statevector object to calculate the ideal output
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_histogram
from qiskit.transpiler import PassManager

from qiskit.circuit.library import XGate
from qiskit.quantum_info import hellinger_fidelity

Crie um pequeno circuito para o transpiler tentar otimizar. Este exemplo cria um circuito que executa o algoritmo de Grover com um oráculo que marca o estado 111. Em seguida, simule a distribuição ideal (o que você esperaria medir se executasse isso em um computador quântico perfeito um número infinito de vezes) para comparação posterior.

oracle = DiagonalGate([1] * 7 + [-1])
qc = QuantumCircuit(3)
qc.h([0, 1, 2])
qc = qc.compose(grover_operator(oracle))

qc.draw(output="mpl", style="iqp")

Output:

Output of the previous code cell
ideal_distribution = Statevector.from_instruction(qc).probabilities_dict()

plot_histogram(ideal_distribution)

Output:

Output of the previous code cell

Transpilar

Em seguida, transpilem os circuitos para a QPU. Você irá comparar o desempenho do transpiler com optimization_level definido como 0 (mais baixo) em relação a 3 (mais alto). O nível mais baixo de otimização faz o mínimo necessário para que o circuito funcione no dispositivo; ele mapeia os qubits do circuito para os qubits do dispositivo e adiciona portas de troca para permitir todas as operações de dois qubits. O nível mais alto de otimização é muito mais inteligente e usa vários truques para reduzir a contagem geral de portas. Como os portões multi-qubit apresentam altas taxas de erro e os qubits se descoerem com o tempo, os circuitos mais curtos devem proporcionar melhores resultados.

Important

Este exemplo utiliza o hardwar IBM Quantum®, mas você pode testá-lo em qualquer QPU compatível com o Qiskit. Seus resultados podem ser diferentes.

A célula a seguir transpila qc para ambos os valores de optimization_level, imprime o número de portas de dois qubits e adiciona os circuitos transpilos a uma lista. Alguns dos algoritmos do transpiler são aleatórios, por isso define uma semente para garantir a reprodutibilidade.

# Use Qiskit Runtime to run jobs on hardware
from qiskit_ibm_runtime import (
    QiskitRuntimeService,
    SamplerV2 as Sampler,
)
# Select the backend with the fewest number of jobs in the queue
service = QiskitRuntimeService()
backend = service.least_busy(
    operational=True, simulator=False, min_num_qubits=127
)
backend.name

Output:

'ibm_marrakesh'
# Need to add measurements to the circuit
qc.measure_all()

# Find the correct two-qubit gate
twoQ_gates = set(["ecr", "cz", "cx"])
for gate in backend.basis_gates:
    if gate in twoQ_gates:
        twoQ_gate = gate

circuits = []
for optimization_level in [0, 3]:
    pm = generate_preset_pass_manager(
        optimization_level, backend=backend, seed_transpiler=0
    )
    t_qc = pm.run(qc)
    print(
        f"Two-qubit gates (optimization_level={optimization_level}): ",
        t_qc.count_ops()[twoQ_gate],
    )
    circuits.append(t_qc)

Output:

Two-qubit gates (optimization_level=0):  21
Two-qubit gates (optimization_level=3):  12

Como os CNOTs geralmente apresentam uma alta taxa de erros, o circuito transposto com optimization_level=3 deve ter um desempenho muito melhor.

Outra maneira de melhorar o desempenho é por meio do desacoplamento dinâmico, aplicando uma sequência de portas aos qubits em repouso. Isso elimina algumas interações indesejadas com o ambiente. A célula a seguir adiciona desacoplamento dinâmico ao circuito compilado com optimization_level=3 e o adiciona à lista.

from qiskit_ibm_runtime.transpiler.passes.scheduling import (
    ASAPScheduleAnalysis,
    PadDynamicalDecoupling,
)

# Get gate durations so the transpiler knows how long each operation takes
durations = backend.target.durations()

# This is the sequence we'll apply to idling qubits
dd_sequence = [XGate(), XGate()]

# Run scheduling and dynamic decoupling passes on circuit
pm = PassManager(
    [
        ASAPScheduleAnalysis(durations),
        PadDynamicalDecoupling(durations, dd_sequence),
    ]
)
circ_dd = pm.run(circuits[1])

# Add this new circuit to our list
circuits.append(circ_dd)
circ_dd.draw(output="mpl", style="iqp", idle_wires=False)

Output:

Output of the previous code cell

Faça o circuito

Neste momento, você tem uma lista de circuitos compilados com diferentes configurações. Em seguida, execute esses circuitos usando a primitiva Sampler e salve os resultados em result.

sampler = Sampler(backend)
job = sampler.run(
    [(circuit) for circuit in circuits],  # sample all three circuits
    shots=8000,
)
result = job.result()

Visualizar resultados

Por fim, represente graficamente os resultados das medições do dispositivo em relação à distribuição ideal. É possível observar que os resultados com optimization_level=3 estão mais próximos da distribuição ideal devido ao menor número de portas, e optimization_level=3 + dd está ainda mais próximo devido ao desacoplamento dinâmico.

binary_prob = [
    {
        k: v / res.data.meas.num_shots
        for k, v in res.data.meas.get_counts().items()
    }
    for res in result
]
plot_histogram(
    binary_prob + [ideal_distribution],
    bar_labels=False,
    legend=[
        "optimization_level=0",
        "optimization_level=3",
        "optimization_level=3 + dd",
        "ideal distribution",
    ],
)

Output:

Output of the previous code cell

Você pode confirmar isso calculando a fidelidade de Hellinger entre cada conjunto de resultados e a distribuição ideal (quanto maior, melhor, e 1 é a fidelidade perfeita).

for prob in binary_prob:
    print(f"{hellinger_fidelity(prob, ideal_distribution):.3f}")

Output:

0.982
0.992
0.995

Próximas etapas

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