Przejdź do głównej treści

Porównaj ustawienia Transpilera

Package versions

The code on this page was developed using the following requirements. We recommend using these versions or newer.

qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1

Różne ustawienia Transpilera zapewniają różne typy optymalizacji Circuit, często kosztem dłuższego czasu przetwarzania klasycznego. Ten przewodnik przechodzi przez pełny proces tworzenia, transpilowania i przesyłania Circuit, aby zademonstrować testowanie wydajności różnych ustawień.

Zauważ, że to samo ustawienie może poprawić wyniki jednego Circuit, jednocześnie utrudniając pracę innego. Przed uruchomieniem na rzeczywistym sprzęcie koniecznie sprawdź wynikowe transpilowane Circuit.

Konfiguracja i tworzenie przykładowego Circuit

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
# 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

Utwórz mały Circuit, który Transpiler będzie próbował zoptymalizować. Ten przykład tworzy Circuit realizujący algorytm Grovera z wyrocznią oznaczającą stan 111. Następnie zasymuluj idealny rozkład (to, czego spodziewałbyś się zmierzyć, gdybyś uruchomił to na doskonałym komputerze kwantowym nieskończoną liczbę razy) w celu późniejszego porównania.

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 of the previous code cell

ideal_distribution = Statevector.from_instruction(qc).probabilities_dict()

plot_histogram(ideal_distribution)

Output of the previous code cell

Transpilacja

Następnie transpiluj Circuit dla QPU. Porównasz wydajność Transpilera z optimization_level ustawionym na 0 (najniższy) w porównaniu z 3 (najwyższy). Najniższy poziom optymalizacji wykonuje absolutne minimum potrzebne do uruchomienia Circuit na urządzeniu: odwzorowuje Qubity Circuit na Qubity urządzenia i dodaje bramki swap, aby umożliwić wszystkie operacje dwu-Qubitowe. Najwyższy poziom optymalizacji jest znacznie inteligentniejszy i stosuje wiele sztuczek w celu zmniejszenia całkowitej liczby Gate. Ponieważ Gate wieloQubitowe mają wysokie współczynniki błędów, a Qubity dekoherują z czasem, krótsze Circuit powinny dawać lepsze wyniki.

important

Ten przykład używa sprzętu IBM Quantum®, ale możesz go wypróbować na dowolnym QPU zgodnym z Qiskit. Twoje wyniki mogą się różnić.

Poniższa komórka transpiluje qc dla obu wartości optimization_level, wyświetla liczbę dwuQubitowych Gate i dodaje transpilowane Circuit do listy. Niektóre algorytmy Transpilera są losowe, dlatego ustawia się ziarno dla powtarzalności.

# 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
'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)
Two-qubit gates (optimization_level=0): 21
Two-qubit gates (optimization_level=3): 12

Ponieważ bramki CNOT zazwyczaj mają wysoki współczynnik błędów, Circuit transpilowany z optimization_level=3 powinien działać znacznie lepiej.

Innym sposobem na poprawę wydajności jest dynamiczne odsprzęganie, polegające na stosowaniu sekwencji Gate do bezczynnych Qubitów. Niweluje to pewne niepożądane interakcje ze środowiskiem. Poniższa komórka dodaje dynamiczne odsprzęganie do obwód transpilowanego z optimization_level=3 i dodaje go do listy.

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 of the previous code cell

Wykonanie Circuit

W tym momencie masz listę Circuit transpilowanych z różnymi ustawieniami. Następnie uruchom te obwód przy użyciu prymitywu Sampler i zapisz wyniki w result.

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

Wyświetl wyniki

Na koniec zwizualizuj wyniki z uruchomień na urządzeniu w porównaniu z idealnym rozkładem. Możesz zobaczyć, że wyniki z optimization_level=3 są bliższe idealnemu rozkładowi ze względu na mniejszą liczbę Gate, a optimization_level=3 + dd jest jeszcze bliżej dzięki dynamicznemu odsprzęganiu.

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 of the previous code cell

Możesz to potwierdzić, obliczając wierność Hellingera między każdym zestawem wyników a idealnym rozkładem (wyższe wartości są lepsze, a 1 oznacza doskonałą wierność).

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

Następne kroki

Recommendations