Przejdź do głównej treści

Ustawianie poziomu optymalizacji Transpilera

Wersje pakietów

Kod na tej stronie został opracowany przy użyciu poniższych wymagań. Zalecamy używanie tych wersji lub nowszych.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1

Prawdziwe urządzenia kwantowe są podatne na szum i błędy bramek, dlatego optymalizacja Circuit w celu zmniejszenia ich głębokości i liczby bramek może znacząco poprawić wyniki uzyskiwane z wykonania tych Circuit. Funkcja generate_preset_pass_manager ma jeden wymagany argument pozycyjny, optimization_level, który kontroluje, ile wysiłku Transpiler wkłada w optymalizację Circuit. Ten argument może być liczbą całkowitą przyjmującą jedną z wartości 0, 1, 2 lub 3. Wyższe poziomy optymalizacji generują bardziej zoptymalizowane Circuit kosztem dłuższego czasu kompilacji. Poniższa tabela wyjaśnia optymalizacje przeprowadzane przy każdym ustawieniu.

Poziom optymalizacjiOpis
0

Brak optymalizacji: zazwyczaj używany do charakteryzacji sprzętu

  • Podstawowe tłumaczenie
  • Układ/Routing: TrivialLayout, gdzie wybiera te same numery fizycznych Qubitów co wirtualne i wstawia SWAP, aby to działało (przy użyciu SabreSwap)
1

Lekka optymalizacja:

  • Układ/Routing: Najpierw próbowany jest układ z TrivialLayout. Jeśli wymagane są dodatkowe SWAP, układ z minimalną liczbą SWAP jest znajdowany za pomocą SabreSwap, następnie używany jest VF2LayoutPostLayout, aby wybrać najlepsze Qubity w grafie.
  • InverseCancellation
  • Optymalizacja bramek 1Q
2

Średnia optymalizacja:

  • Układ/Routing: Poziom optymalizacji 1 (bez trivial) + heurystyczna optymalizacja z większą głębokością przeszukiwania i próbami funkcji optymalizacji. Ponieważ TrivialLayout nie jest używany, nie ma próby użycia tych samych numerów fizycznych i wirtualnych Qubitów.
  • CommutativeCancellation
3

Wysoka optymalizacja:

  • Poziom optymalizacji 2 + heurystyczna optymalizacja układu/routingu z większym wysiłkiem/próbami
  • Resynteza bloków dwuqubitowych przy użyciu dekompozycji KAK Cartana.
  • Przebiegi łamiące unitarność:
    • OptimizeSwapBeforeMeasure: Przesuwa pomiary, aby unikać SWAP
    • RemoveDiagonalGatesBeforeMeasure: Usuwa bramki przed pomiarami, które nie wpływałyby na pomiary

Poziom optymalizacji w działaniu

Ponieważ bramki dwuqubitowe są zazwyczaj najważniejszym źródłem błędów, możemy w przybliżeniu określić ilościowo „efektywność sprzętową" transpilacji, licząc liczbę bramek dwuqubitowych w wynikowym Circuit. Tutaj wypróbujemy różne poziomy optymalizacji na wejściowym Circuit składającym się z losowej unitarnej, po której następuje bramka 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")

Wynik poprzedniej komórki kodu

Użyjemy atrapowego Backend FakeSherbrooke w naszych przykładach. Najpierw transpilujmy przy użyciu poziomu optymalizacji 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)

Wynik poprzedniej komórki kodu

Transpilowany Circuit ma sześć dwuqubitowych bramek ECR.

Powtórzmy dla poziomu optymalizacji 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)

Wynik poprzedniej komórki kodu

Transpilowany Circuit nadal ma sześć bramek ECR, ale liczba jednoqubitowych bramek się zmniejszyła.

Powtórzmy dla poziomu optymalizacji 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)

Wynik poprzedniej komórki kodu

To daje te same wyniki co poziom optymalizacji 1. Pamiętaj, że zwiększenie poziomu optymalizacji nie zawsze robi różnicę.

Powtórzmy ponownie z poziomem optymalizacji 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)

Wynik poprzedniej komórki kodu

Teraz są tylko trzy bramki ECR. Osiągamy ten wynik, ponieważ na poziomie optymalizacji 3 Qiskit próbuje powtórnie syntetyzować dwuqubitowe bloki bramek, a każda dwuqubitowa bramka może być zaimplementowana przy użyciu co najwyżej trzech bramek ECR. Możemy uzyskać jeszcze mniej bramek ECR, jeśli ustawimy approximation_degree na wartość mniejszą niż 1, pozwalając Transpilerowi na przybliżenia, które mogą wprowadzić pewien błąd w dekompozycji bramki (patrz Często używane parametry transpilacji):

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)

Wynik poprzedniej komórki kodu

Ten Circuit ma tylko dwie bramki ECR, ale jest przybliżonym Circuit. Aby zrozumieć, czym różni się jego działanie od dokładnego Circuit, możemy obliczyć wierność między operatorem unitarnym, który implementuje ten Circuit, a dokładnym unitarnym. Przed wykonaniem obliczeń najpierw redukujemy transpilowany Circuit, który zawiera 127 Qubitów, do Circuit zawierającego tylko aktywne Qubity, których jest dwa.

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

Zmiana poziomu optymalizacji może również wpływać na inne aspekty Circuit, nie tylko na liczbę bramek ECR. Przykłady tego, jak ustawienie poziomu optymalizacji zmienia układ, znajdziesz w sekcji Reprezentowanie komputerów kwantowych.

Kolejne kroki

Zalecenia