Przejdź do głównej treści

Mitygacja błędów z funkcją IBM Circuit

Uwaga

Qiskit Functions to eksperymentalna funkcja dostępna wyłącznie dla użytkowników planów IBM Quantum® Premium Plan, Flex Plan i On-Prem (przez IBM Quantum Platform API). Są one w stanie podglądu i mogą ulec zmianie.

Szacowany czas użycia: 26 minut na procesorze Eagle (UWAGA: to tylko szacunek. Rzeczywisty czas może się różnić.) Ten samouczek przeprowadza przez przykład budowania i uruchamiania przepływu pracy z użyciem funkcji IBM Circuit. Funkcja ta przyjmuje Primitive Unified Blocs (PUB) jako dane wejściowe i zwraca wartości oczekiwane z mitygacją błędów jako wyniki. Zapewnia zautomatyzowany i dostosowany potok optymalizacji obwodów oraz wykonywania na sprzęcie kwantowym, dzięki czemu badacze mogą skupić się na odkrywaniu algorytmów i zastosowań.

Odwiedź dokumentację, aby zapoznać się z wprowadzeniem do Qiskit Functions i dowiedzieć się, jak zacząć korzystać z funkcji IBM Circuit.

Tło

Ten samouczek rozważa ogólny sprzętowo-wydajny obwód ewolucji czasowej w formacie Trottera dla dwuwymiarowego transwersalnego modelu Isinga z polem i oblicza globalną magnetyzację. Taki obwód jest przydatny w różnych dziedzinach zastosowań, takich jak fizyka materii skondensowanej, chemia i uczenie maszynowe. Więcej informacji na temat struktury tego modelu znajdziesz w artykule Nature 618, 500–505 (2023).

Funkcja IBM Circuit łączy możliwości usługi transpilatora Qiskit i Estimatora Qiskit Runtime, aby zapewnić uproszczony interfejs do uruchamiania obwodów. Funkcja wykonuje transpilację, tłumienie błędów, mitygację błędów i wykonanie obwodów w ramach jednej zarządzanej usługi, dzięki czemu możemy skupić się na mapowaniu problemu na obwody, zamiast samodzielnie budować każdy krok wzorca.

Wymagania

Przed rozpoczęciem tego samouczka upewnij się, że masz zainstalowane następujące pakiety:

  • Qiskit SDK v1.2 lub nowszy (pip install qiskit)
  • Qiskit Runtime v0.28 lub nowszy (pip install qiskit-ibm-runtime)
  • IBM Qiskit Functions Catalog client v0.0.0 lub nowszy (pip install qiskit-ibm-catalog)
  • Qiskit Aer v0.15.0 lub nowszy (pip install qiskit-aer)

Konfiguracja

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-catalog qiskit-ibm-runtime rustworkx
import rustworkx
from collections import defaultdict
from numpy import pi, mean

from qiskit_ibm_runtime import QiskitRuntimeService

from qiskit_ibm_catalog import QiskitFunctionsCatalog

from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.quantum_info import SparsePauliOp

Krok 1: Odwzorowanie klasycznych danych wejściowych na problem kwantowy

  • Wejście: Parametry do tworzenia obwodu kwantowego
  • Wyjście: Abstrakcyjny Circuit i obserwable

Konstrukcja obwodu

Obwód, który stworzymy, to sprzętowo-wydajny, Trotteryzowany obwód ewolucji czasowej dla dwuwymiarowego transwersalnego modelu Isinga z polem. Zaczynamy od wybrania Backend. Właściwości tego Backend (czyli jego mapa sprzęgania) zostaną użyte do zdefiniowania problemu kwantowego i zapewnienia jego sprzętowej wydajności.

service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)

Następnie pobieramy mapę sprzęgania z Backend.

coupling_graph = backend.coupling_map.graph.to_undirected(multigraph=False)
layer_couplings = defaultdict(list)

Musimy być ostrożni w projektowaniu warstw naszego obwodu. Zrobimy to poprzez kolorowanie krawędzi mapy sprzęgania (czyli grupowanie rozłącznych krawędzi) i użyjemy tego kolorowania do bardziej wydajnego rozmieszczania bramek w obwodzie. Doprowadzi to do płytszego obwodu z warstwami bramek, które mogą być wykonywane jednocześnie na sprzęcie.

edge_coloring = rustworkx.graph_bipartite_edge_color(coupling_graph)

for edge_idx, color in edge_coloring.items():
layer_couplings[color].append(
coupling_graph.get_edge_endpoints_by_index(edge_idx)
)
layer_couplings = [
sorted(layer_couplings[i]) for i in sorted(layer_couplings.keys())
]

Następnie piszemy prostą funkcję pomocniczą, która implementuje sprzętowo-wydajny, Trotteryzowany obwód ewolucji czasowej dla dwuwymiarowego transwersalnego modelu Isinga z użyciem kolorowania krawędzi uzyskanego powyżej.

def construct_trotter_circuit(
num_qubits: int,
num_trotter_steps: int,
layer_couplings: list,
barrier: bool = True,
) -> QuantumCircuit:
theta, phi = Parameter("theta"), Parameter("phi")
circuit = QuantumCircuit(num_qubits)

for _ in range(num_trotter_steps):
circuit.rx(theta, range(num_qubits))
for layer in layer_couplings:
for edge in layer:
if edge[0] < num_qubits and edge[1] < num_qubits:
circuit.rzz(phi, edge[0], edge[1])
if barrier:
circuit.barrier()

return circuit

Wybierzemy liczbę Qubitów i kroków Trottera, a następnie skonstruujemy obwód.

num_qubits = 100
num_trotter_steps = 2

circuit = construct_trotter_circuit(
num_qubits, num_trotter_steps, layer_couplings
)
circuit.draw("mpl", fold=-1)

Output of the previous code cell

Aby porównać jakość wykonania z idealnym wynikiem, musimy przeprowadzić benchmarking. Wybrany obwód wykracza poza możliwości klasycznej symulacji metodą siłową. Dlatego ustalamy parametry wszystkich bramek Rx w obwodzie na 00, a wszystkich bramek Rzz na π\pi. Sprawia to, że obwód jest Cliffordowski, co umożliwia przeprowadzenie idealnej symulacji i uzyskanie idealnego wyniku do porównania. W tym przypadku wiemy, że wynikiem będzie 1.0.

parameters = [0, pi]

Konstrukcja obserwabla

Najpierw oblicz globalną magnetyzację wzdłuż z^\hat{z} dla problemu NN-Qubitowego: Mz=i=1NZi/NM_z = \sum_{i=1}^N \langle Z_i \rangle / N. Wymaga to najpierw obliczenia jednokomórkowej magnetyzacji Zi\langle Z_i \rangle dla każdego Qubita ii, która jest zdefiniowana w poniższym kodzie.

observables = []
for i in range(num_qubits):
obs = "I" * (i) + "Z" + "I" * (num_qubits - i - 1)
observables.append(SparsePauliOp(obs))

print(observables[0])
SparsePauliOp(['ZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j])

Kroki 2 i 3: Optymalizacja problemu do wykonania na sprzęcie kwantowym i wykonanie z funkcją IBM Circuit

  • Wejście: Abstrakcyjny Circuit i obserwable
  • Wyjście: Zmitygowane wartości oczekiwane

Teraz możemy przekazać abstrakcyjny Circuit i obserwable do funkcji IBM Circuit. Zajmie się ona transpilacją i wykonaniem na sprzęcie kwantowym i zwróci zmitygowane wartości oczekiwane. Najpierw wczytujemy funkcję z IBM Qiskit Functions Catalog.

catalog = QiskitFunctionsCatalog(
token="<YOUR_API_KEY>"
) # Use the 44-character API_KEY you created and saved from the IBM Quantum Platform Home dashboard
function = catalog.load("ibm/circuit-function")

Funkcja IBM Circuit przyjmuje pubs, backend_name, a także opcjonalne dane wejściowe do konfiguracji transpilacji, mitygacji błędów i innych ustawień. Tworzymy pub z abstrakcyjnego Circuit, obserwabli i parametrów obwodu. Nazwa Backend powinna być podana jako ciąg znaków.

pubs = [(circuit, observables, parameters)]
backend_name = backend.name

Możemy również skonfigurować options dla transpilacji, tłumienia błędów i mitygacji błędów. Jeśli nie chcemy ich określać, zostaną użyte ustawienia domyślne. Funkcja IBM Circuit zawiera powszechnie używane opcje dla optimization_level, który kontroluje zakres optymalizacji obwodu, oraz mitigation_level, który określa poziom tłumienia i mitygacji błędów. Zauważ, że mitigation_level funkcji IBM Circuit różni się od resilience_level używanego w Qiskit Runtime Estimator. Szczegółowy opis tych powszechnie używanych opcji oraz innych zaawansowanych opcji znajdziesz w dokumentacji funkcji IBM Circuit.

W tym samouczku ustawimy default_precision, optimization_level: 3 i mitigation_level: 3, co włączy gate twirling i ekstrapolację zerowego szumu (ZNE) za pomocą probabilistycznego wzmocnienia błędów (PEA) ponad domyślne ustawienia poziomu 1.

options = {
"default_precision": 0.011,
"optimization_level": 3,
"mitigation_level": 3,
}

Po określeniu danych wejściowych przesyłamy zadanie do funkcji IBM Circuit w celu optymalizacji i wykonania.

job = function.run(backend_name=backend_name, pubs=pubs, options=options)

Krok 4: Post-przetwarzanie i zwrócenie wyniku w żądanym formacie klasycznym

  • Wejście: Wyniki z funkcji IBM Circuit
  • Wyjście: Globalna magnetyzacja

Obliczanie globalnej magnetyzacji

Wynik uruchomienia funkcji ma ten sam format co Estimator.

result = job.result()[0]

Uzyskujemy zmitygowane i niezmitygowane wartości oczekiwane z tego wyniku. Te wartości oczekiwane reprezentują jednokomórkową magnetyzację wzdłuż kierunku z^\hat{z}. Uśredniamy je, aby otrzymać globalną magnetyzację i porównujemy z idealną wartością 1.0 dla tej instancji problemu.

mitigated_expvals = result.data.evs
magnetization_mitigated = mean(mitigated_expvals)

print("mitigated:", magnetization_mitigated)

unmitigated_expvals = [
result.data.evs_extrapolated[i][0][1] for i in range(num_qubits)
]
magnetization_unmitigated = mean(unmitigated_expvals)

print("unmitigated:", magnetization_unmitigated)
mitigated: 0.9749883476088692
unmitigated: 0.7832977198447583

Ankieta dotycząca samouczka

Wypełnij tę krótką ankietę, aby przekazać opinię na temat tego samouczka. Twoje uwagi pomogą nam ulepszyć nasze treści i doświadczenie użytkownika.

Link do ankiety

Note: This survey is provided by IBM Quantum and relates to the original English content. To give feedback on doQumentation's website, translations, or code execution, please open a GitHub issue.