Przejdź do głównej treści

Przejście fazowe Nishimoriego

Szacowany czas użycia: 3 minuty na procesorze Heron r2 (UWAGA: To jest jedynie szacunek. Rzeczywisty czas może się różnić.)

Wprowadzenie

Ten samouczek pokazuje, jak zrealizować przejście fazowe Nishimoriego na procesorze kwantowym IBM®. Eksperyment został pierwotnie opisany w artykule Realizing the Nishimori transition across the error threshold for constant-depth quantum circuits.

Przejście fazowe Nishimoriego oznacza przejście między fazami o krótko- i dalekosiężnym porządku w modelu Isinga z losowymi wiązaniami. Na komputerze kwantowym faza o dalekosiężnym porządku objawia się stanem, w którym qubity są ze sobą splątane w całym urządzeniu. Ten silnie splątany stan jest przygotowywany przy użyciu protokołu generowania splątania przez pomiar (GEM). Dzięki wykorzystaniu pomiarów w trakcie obwodu protokół GEM jest w stanie splątać qubity w całym urządzeniu za pomocą obwodów o stałej głębokości. W tym samouczku stosowana jest implementacja protokołu GEM z pakietu oprogramowania GEM Suite.

Wymagania

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

  • Qiskit SDK v1.0 lub nowszy z obsługą wizualizacji
  • Qiskit Runtime v0.22 lub nowszy ( pip install qiskit-ibm-runtime )
  • GEM Suite ( pip install gem-suite )

Konfiguracja

# Added by doQumentation — required packages for this notebook
!pip install -q gem-suite matplotlib qiskit qiskit-ibm-runtime
import matplotlib.pyplot as plt

from collections import defaultdict

from qiskit_ibm_runtime import QiskitRuntimeService

from qiskit.transpiler import generate_preset_pass_manager

from gem_suite import PlaquetteLattice
from gem_suite.experiments import GemExperiment

Krok 1: Odwzorowanie danych wejściowych na problem kwantowy

Protokół GEM działa na procesorze kwantowym, którego topologia połączeń qubitów jest opisana przez sieć krystaliczną. Współczesne procesory kwantowe IBM używają sieci heavy hex. Qubity procesora są grupowane w plakietki na podstawie komórki jednostkowej sieci, w której się znajdują. Ponieważ qubit może wystąpić w więcej niż jednej komórce jednostkowej, plakietki nie są rozłączne. W sieci heavy hex plakietka zawiera 12 qubitów. Same plakietki również tworzą sieć, gdzie dwie plakietki są połączone, jeśli mają wspólne qubity. W sieci heavy hex sąsiednie plakietki mają 3 wspólne qubity.

W pakiecie oprogramowania GEM Suite podstawową klasą do implementacji protokołu GEM jest PlaquetteLattice, która reprezentuje sieć plakietek (odrębną od sieci heavy hex). Obiekt PlaquetteLattice można zainicjować na podstawie mapy sprzężeń qubitów. Obecnie obsługiwane są tylko mapy sprzężeń heavy hex.

Poniższa komórka kodu inicjuje sieć plakietek na podstawie mapy sprzężeń procesora kwantowego IBM. Sieć plakietek nie zawsze obejmuje całe urządzenie. Na przykład ibm_torino ma łącznie 133 qubity, ale największa sieć plakietek, która mieści się na urządzeniu, używa tylko 125 z nich i składa się z 18 plakietek. Podobne sytuacje można zaobserwować na urządzeniach IBM Quantum® z różną liczbą qubitów.

# QiskitRuntimeService.save_account(channel="ibm_quantum", token="<YOUR_API_KEYN>", overwrite=True, set_as_default=True)
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
plaquette_lattice = PlaquetteLattice.from_coupling_map(backend.coupling_map)

print(f"Number of qubits in backend: {backend.num_qubits}")
print(
f"Number of qubits in plaquette lattice: {len(list(plaquette_lattice.qubits()))}"
)
print(f"Number of plaquettes: {len(list(plaquette_lattice.plaquettes()))}")
Number of qubits in backend: 133
Number of qubits in plaquette lattice: 125
Number of plaquettes: 18

Możesz zwizualizować sieć plakietek, generując diagram jej reprezentacji grafowej. Na diagramie plakietki są przedstawiane jako oznakowane sześciokąty, a dwie plakietki są połączone krawędzią, jeśli mają wspólne qubity.

plaquette_lattice.draw_plaquettes()

Output of the previous code cell

Możesz uzyskać informacje o poszczególnych plakietkach, takie jak zawarte w nich qubity, używając metody plaquettes.

# Get a list of the plaquettes
plaquettes = list(plaquette_lattice.plaquettes())
# Display information about plaquette 0
plaquettes[0]
PyPlaquette(index=0, qubits=[0, 1, 2, 3, 4, 15, 16, 19, 20, 21, 22, 23], neighbors=[3, 1])

Możesz również wygenerować diagram bazowych qubitów tworzących sieć plakietek.

plaquette_lattice.draw_qubits()

Output of the previous code cell

Oprócz etykiet qubitów i krawędzi wskazujących połączone qubity diagram zawiera trzy dodatkowe informacje istotne dla protokołu GEM:

  • Każdy qubit jest albo zacieniony (szary), albo niezacieniony. Zacienione qubity to qubity „węzłowe" reprezentujące węzły modelu Isinga, a niezacienione qubity to qubity „wiązaniowe" służące do pośredniczenia w interakcjach między qubitami węzłowymi.
  • Każdy qubit węzłowy jest oznaczony (A) lub (B), wskazując jedną z dwóch ról, jaką qubit węzłowy może odgrywać w protokole GEM (role są wyjaśnione później).
  • Każda krawędź jest zabarwiona jednym z sześciu kolorów, dzieląc krawędzie na sześć grup. Ten podział określa, jak bramki dwuqubitowe mogą być zrównoleglone, a także różne wzorce harmonogramowania, które prawdopodobnie wprowadzają różne ilości błędów na zaszumionym procesorze kwantowym. Ponieważ krawędzie w grupie są rozłączne, warstwa bramek dwuqubitowych może być zastosowana do tych krawędzi jednocześnie. W rzeczywistości możliwe jest podzielenie sześciu kolorów na trzy grupy po dwa kolory tak, że suma każdej grupy dwóch kolorów jest nadal rozłączna. Dlatego do aktywacji każdej krawędzi potrzebne są tylko trzy warstwy bramek dwuqubitowych. Istnieje 12 sposobów takiego podziału sześciu kolorów, a każdy taki podział daje inny harmonogram 3-warstwowy.

Teraz, gdy masz już sieć plakietek, następnym krokiem jest zainicjowanie obiektu GemExperiment, przekazując zarówno sieć plakietek, jak i backend, na którym zamierzasz przeprowadzić eksperyment. Klasa GemExperiment zarządza rzeczywistą implementacją protokołu GEM, w tym generowaniem obwodów, przesyłaniem zadań i analizą danych. Poniższa komórka kodu inicjuje klasę eksperymentu, ograniczając sieć plakietek tylko do dwóch plakietek (21 qubitów), co zmniejsza rozmiar eksperymentu, aby upewnić się, że szum sprzętu nie zdominuje sygnału.

gem_exp = GemExperiment(plaquette_lattice.filter([9, 12]), backend=backend)

# visualize the plaquette lattice after filtering
plaquette_lattice.filter([9, 12]).draw_qubits()

Output of the previous code cell

Obwód protokołu GEM jest budowany przy użyciu następujących kroków:

  1. Przygotuj stan +|+\rangle dla wszystkich qubitów, stosując bramkę Hadamarda do każdego qubitu.
  2. Zastosuj bramkę RZZR_{ZZ} między każdą parą połączonych qubitów. Można to osiągnąć przy użyciu 3 warstw bramek. Każda bramka RZZR_{ZZ} działa na qubit węzłowy i qubit wiązaniowy. Jeśli qubit węzłowy jest oznaczony (B), kąt jest ustalony na π2\frac{\pi}{2}. Jeśli qubit węzłowy jest oznaczony (A), kąt może się zmieniać, tworząc różne obwody. Domyślnie zakres kątów jest ustawiony na 21 równomiernie rozmieszczonych punktów między 00 a π2\frac{\pi}{2}, włącznie.
  3. Zmierz każdy qubit wiązaniowy w bazie Pauliego XX. Ponieważ qubity są mierzone w bazie Pauliego ZZ, można to osiągnąć, stosując bramkę Hadamarda przed pomiarem qubitu.

Zauważ, że artykuł cytowany we wstępie do tego samouczka używa innej konwencji dla kąta RZZR_{ZZ}, która różni się od konwencji stosowanej w tym samouczku czynnikiem 2.

W kroku 3 mierzone są tylko qubity wiązaniowe. Aby zrozumieć, w jakim stanie pozostają qubity węzłowe, pouczające jest rozważenie przypadku, gdy kąt RZZR_{ZZ} zastosowany do qubitów węzłowych (A) w kroku 2 wynosi π2\frac{\pi}{2}. W tym przypadku qubity węzłowe pozostają w silnie splątanym stanie podobnym do stanu GHZ,

GHZ=0000+1111.\lvert \text{GHZ} \rangle = \lvert 00 \cdots 00 \rangle + \lvert 11 \cdots 11 \rangle.

Ze względu na losowość wyników pomiarów rzeczywisty stan qubitów węzłowych może być innym stanem o dalekosiężnym porządku, na przykład 00110+11001\lvert 00110 \rangle + \lvert 11001 \rangle. Jednak stan GHZ można odzyskać, stosując operację dekodowania opartą na wynikach pomiarów. Gdy kąt RZZR_{ZZ} jest zmniejszany z π2\frac{\pi}{2}, dalekosiężny porządek można nadal odzyskać aż do kąta krytycznego, który w przypadku braku szumu wynosi około 0.3π0.3 \pi. Poniżej tego kąta wynikowy stan nie wykazuje już dalekosiężnego splątania. To przejście między obecnością a brakiem dalekosiężnego porządku jest właśnie przejściem fazowym Nishimoriego.

W powyższym opisie qubity węzłowe pozostały niezmierzone, a operacja dekodowania może być wykonana przez zastosowanie bramek kwantowych. W eksperymencie zaimplementowanym w pakiecie GEM, za którym podąża ten samouczek, qubity węzłowe są w rzeczywistości mierzone, a operacja dekodowania jest stosowana w kroku klasycznego post-processingu.

W powyższym opisie operacja dekodowania może być wykonana przez zastosowanie bramek kwantowych do qubitów węzłowych w celu odzyskania stanu kwantowego. Jednak jeśli celem jest natychmiastowe zmierzenie stanu, na przykład w celach charakteryzacji, qubity węzłowe są mierzone razem z qubitami wiązaniowymi, a operacja dekodowania może być zastosowana w klasycznym kroku post-processingu. W ten sposób eksperyment jest zaimplementowany w pakiecie GEM, za którym podąża ten samouczek.

Oprócz zależności od kąta RZZR_{ZZ} w kroku 2, który domyślnie przyjmuje 21 wartości, obwód protokołu GEM zależy również od wzorca harmonogramowania używanego do implementacji 3 warstw bramek RZZR_{ZZ}. Jak omówiono wcześniej, istnieje 12 takich wzorców harmonogramowania. Dlatego łączna liczba obwodów w eksperymencie wynosi 21×12=25221 \times 12 = 252.

Obwody eksperymentu można wygenerować przy użyciu metody circuits klasy GemExperiment.

circuits = gem_exp.circuits()
print(f"Total number of circuits: {len(circuits)}")
Total number of circuits: 252

Na potrzeby tego samouczka wystarczy rozważyć tylko jeden wzorzec harmonogramowania. Poniższa komórka kodu ogranicza eksperyment do pierwszego wzorca harmonogramowania. W rezultacie eksperyment ma tylko 21 obwodów, po jednym dla każdego przemiatanego kąta RZZR_{ZZ}.

# Restrict experiment to the first scheduling pattern
gem_exp.set_experiment_options(schedule_idx=0)

# There are less circuits now
circuits = gem_exp.circuits()
print(f"Total number of circuits: {len(circuits)}")

# Print the RZZ angles swept over
print(f"RZZ angles:\n{gem_exp.parameters()}")
Total number of circuits: 21
RZZ angles:
[0. 0.07853982 0.15707963 0.23561945 0.31415927 0.39269908
0.4712389 0.54977871 0.62831853 0.70685835 0.78539816 0.86393798
0.9424778 1.02101761 1.09955743 1.17809725 1.25663706 1.33517688
1.41371669 1.49225651 1.57079633]

Poniższa komórka kodu rysuje diagram obwodu o indeksie 5. Aby zmniejszyć rozmiar diagramu, bramki pomiarowe na końcu obwodu są usunięte.

# Get the circuit at index 5
circuit = circuits[5]
# Remove the final measurements to ease visualization
circuit.remove_final_measurements()
# Draw the circuit
circuit.draw("mpl", fold=-1, scale=0.5)

Output of the previous code cell

Krok 2: Optymalizacja problemu pod kątem wykonania na sprzęcie kwantowym

Transpilacja obwodów kwantowych do wykonania na sprzęcie zazwyczaj obejmuje kilka etapów. Etapy powodujące największe obciążenie obliczeniowe to zazwyczaj wybór układu qubitów, trasowanie bramek dwuqubitowych zgodnie z topologią połączeń sprzętu oraz optymalizacja obwodu w celu minimalizacji liczby bramek i głębokości. W protokole GEM etapy układu i trasowania są zbędne, ponieważ topologia sprzętu jest już wbudowana w projekt protokołu. Obwody mają już układ qubitów, a bramki dwuqubitowe są już odwzorowane na natywne połączenia. Ponadto, aby zachować strukturę obwodu przy zmiennym kącie RZZR_{ZZ}, powinna być wykonywana tylko bardzo podstawowa optymalizacja obwodu.

Klasa GemExperiment transparentnie transpiluje obwody podczas wykonywania eksperymentu. Etapy układu i trasowania są już domyślnie zastąpione przez operacje nierobienia niczego, a optymalizacja obwodu jest wykonywana na poziomie, który optymalizuje jedynie bramki jednoqubitowe. Możesz jednak nadpisać lub przekazać dodatkowe opcje przy użyciu metody set_transpile_options. Dla celów wizualizacji poniższa komórka kodu ręcznie transpiluje wcześniej wyświetlony obwód i rysuje transpilowany obwód.

# Demonstrate setting transpile options
gem_exp.set_transpile_options(
optimization_level=1 # This is the default optimization level
)
pass_manager = generate_preset_pass_manager(
backend=backend,
initial_layout=list(gem_exp.physical_qubits),
**dict(gem_exp.transpile_options),
)
transpiled = pass_manager.run(circuit)
transpiled.draw("mpl", idle_wires=False, fold=-1, scale=0.5)

Output of the previous code cell

Krok 3: Wykonanie przy użyciu prymitywów Qiskit

Aby wykonać obwody protokołu GEM na sprzęcie, wywołaj metodę run obiektu GemExperiment. Możesz określić liczbę próbek (shots) do pobrania z każdego obwodu. Metoda run zwraca obiekt ExperimentData, który powinieneś zapisać do zmiennej. Zauważ, że metoda run jedynie przesyła zadania bez oczekiwania na ich zakończenie, więc jest to wywołanie nieblokujące.

exp_data = gem_exp.run(shots=10_000)

Aby poczekać na wyniki, wywołaj metodę block_for_results obiektu ExperimentData. To wywołanie spowoduje zatrzymanie interpretera do momentu zakończenia zadań.

exp_data.block_for_results()
ExperimentData(GemExperiment, d0d5880a-34c1-4aab-a7b6-c4f58516bc03, job_ids=['cwg12ptmptp00082khhg'], metadata=<5 items>, figure_names=['two_point_correlation.svg', 'normalized_variance.svg', 'plaquette_ops.svg', 'bond_ops.svg'])

Krok 4: Post-processing i zwracanie wyników w żądanym formacie klasycznym

Przy kącie RZZR_{ZZ} wynoszącym π2\frac{\pi}{2} zdekodowany stan byłby stanem GHZ w przypadku braku szumu. Dalekosiężny porządek stanu GHZ można zwizualizować, kreśląc magnetyzację zmierzonych ciągów bitów. Magnetyzacja MM jest zdefiniowana jako suma jednoqubitowych operatorów Pauliego ZZ,

M=j=1NZj,M = \sum_{j=1}^N Z_j,

gdzie NN jest liczbą qubitów węzłowych. Jej wartość dla ciągu bitów jest równa różnicy między liczbą zer a liczbą jedynek. Pomiar stanu GHZ daje stan wszystkich zer lub stan wszystkich jedynek z równym prawdopodobieństwem, więc magnetyzacja wynosiłaby +N+N przez połowę czasu i N-N przez drugą połowę. W obecności błędów spowodowanych szumem pojawiałyby się również inne wartości, ale jeśli szum nie jest zbyt duży, rozkład nadal byłby skupiony blisko +N+N i N-N.

Dla surowych ciągów bitów przed dekodowaniem rozkład magnetyzacji byłby równoważny rozkładowi dla losowych ciągów bitów, w przypadku braku szumu.

Poniższa komórka kodu kreśli magnetyzację surowych ciągów bitów i zdekodowanych ciągów bitów przy kącie RZZR_{ZZ} wynoszącym π2\frac{\pi}{2}.

def magnetization_distribution(
counts_dict: dict[str, int],
) -> dict[str, float]:
"""Compute magnetization distribution from counts dictionary."""
# Construct dictionary from magnetization to count
mag_dist = defaultdict(float)
for bitstring, count in counts_dict.items():
mag = bitstring.count("0") - bitstring.count("1")
mag_dist[mag] += count
# Normalize
shots = sum(counts_dict.values())
for mag in mag_dist:
mag_dist[mag] /= shots
return mag_dist

# Get counts dictionaries with and without decoding
data = exp_data.data()
# Get the last data point, which is at the angle for the GHZ state
raw_counts = data[-1]["counts"]
# Without decoding
site_indices = [
i for i, q in enumerate(gem_exp.plaquettes.qubits()) if q.role == "Site"
]
site_raw_counts = defaultdict(int)
for key, val in raw_counts.items():
site_str = "".join(key[-1 - i] for i in site_indices)
site_raw_counts[site_str] += val
# With decoding
_, site_decoded_counts = gem_exp.plaquettes.decode_outcomes(
raw_counts, return_counts=True
)

# Compute magnetization distribution
raw_magnetization = magnetization_distribution(site_raw_counts)
decoded_magnetization = magnetization_distribution(site_decoded_counts)

# Plot
plt.bar(*zip(*raw_magnetization.items()), label="raw")
plt.bar(*zip(*decoded_magnetization.items()), label="decoded", width=0.3)
plt.legend()
plt.xlabel("Magnetization")
plt.ylabel("Frequency")
plt.title("Magnetization distribution with and without decoding")
Text(0.5, 1.0, 'Magnetization distribution with and without decoding')

Output of the previous code cell

Aby dokładniej scharakteryzować dalekosiężny porządek, możesz zbadać średnią korelację dwupunktową ff, zdefiniowaną jako

f=1N2(M2M2).f = \frac{1}{N^2} \left(\langle M^2 \rangle - \langle M \rangle ^2\right).

Wyższa wartość wskazuje na większy stopień splątania. Klasa GemExperiment automatycznie oblicza tę wartość dla zdekodowanych ciągów bitów w ramach przetwarzania danych eksperymentalnych. Przechowuje rysunek dostępny za pomocą metody figure klasy danych eksperymentu. W tym przypadku nazwa rysunku to two_point_correlation.

exp_data.figure("two_point_correlation")

Output of the previous code cell

Aby określić punkt krytyczny przejścia fazowego Nishimoriego, możesz spojrzeć na znormalizowaną wariancję M2/NM^2 / N, zdefiniowaną jako

g=1N3(M4M22),g = \frac{1}{N^3} \left(\langle M^4 \rangle - \langle M^2 \rangle^2\right),

która kwantyfikuje wielkość fluktuacji w kwadracie magnetyzacji. Wartość ta jest maksymalna w punkcie krytycznym przejścia fazowego Nishimoriego. W przypadku braku szumu punkt krytyczny występuje przy około 0.3π0.3 \pi. W obecności szumu punkt krytyczny przesuwa się wyżej, ale przejście fazowe jest nadal obserwowane, o ile punkt krytyczny występuje poniżej 0.5π0.5 \pi.

exp_data.figure("normalized_variance")

Output of the previous code cell

Skalowanie eksperymentu

Poniższe komórki kodu przeprowadzają eksperyment dla sześciu plakietek (49 qubitów) i pełnych 12 plakietek (125 qubitów) oraz kreślą znormalizowaną wariancję. W miarę skalowania eksperymentu do większych rozmiarów większa ilość szumu przesuwa punkt krytyczny w prawo.

gem_exp = GemExperiment(
plaquette_lattice.filter(range(3, 9)), backend=backend
)
gem_exp.set_experiment_options(schedule_idx=0)
exp_data = gem_exp.run(shots=10_000)
exp_data.block_for_results()
exp_data.figure("normalized_variance")

Output of the previous code cell

gem_exp = GemExperiment(plaquette_lattice, backend=backend)
gem_exp.set_experiment_options(schedule_idx=0)
exp_data = gem_exp.run(shots=10_000)
exp_data.block_for_results()
exp_data.figure("normalized_variance")

Output of the previous code cell

Podsumowanie

W tym samouczku zrealizowałeś przejście fazowe Nishimoriego na procesorze kwantowym przy użyciu protokołu GEM. Metryki zbadane podczas post-processingu, w szczególności korelacja dwupunktowa i znormalizowana wariancja, służą jako wskaźniki zdolności urządzenia do generowania stanów o dalekosiężnym splątaniu. Te wskaźniki rozszerzają użyteczność protokołu GEM poza sondowanie interesującej fizyki. W ramach protokołu splątałeś qubity w całym urządzeniu przy użyciu obwodów o stałej głębokości. Ten wyczyn jest możliwy tylko dzięki zastosowaniu w protokole pomiarów w trakcie obwodu. W tym eksperymencie splątany stan został natychmiast zmierzony, ale interesującym kierunkiem do eksploracji byłoby dalsze wykorzystanie tego stanu w dodatkowych obliczeniach kwantowych!

Ankieta dotycząca samouczka

Wypełnij tę krótką ankietę, aby przekazać opinię na temat tego samouczka. Twoje uwagi pomogą nam ulepszyć naszą ofertę 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.