Kwantowe Estymowanie Fazy z funkcjami Qiskit od Q-CTRL
Szacowane zużycie zasobów: 40 sekund na procesorze Heron r2. (UWAGA: To tylko szacunek. Rzeczywisty czas działania może się różnić.)
Kontekst
Kwantowe Estymowanie Fazy (QPE) to fundamentalny algorytm w obliczeniach kwantowych, który stanowi podstawę wielu ważnych zastosowań, takich jak algorytm Shora, estymacja energii stanu podstawowego w chemii kwantowej oraz problemy wartości własnych. QPE szacuje fazę powiązaną ze stanem własnym operatora unitarnego, zakodowaną w relacji
i wyznacza ją z dokładnością , używając qubitów liczących [1]. Poprzez przygotowanie tych qubitów w superpozycji, zastosowanie kontrolowanych potęg , a następnie użycie odwrotnej Kwantowej Transformaty Fouriera (QFT) do wyekstrahowania fazy do binarnie zakodowanych wyników pomiarów, QPE produkuje rozkład prawdopodobieństwa skoncentrowany wokół łańcuchów bitów, których ułamki binarne aproksymują . W idealnym przypadku najbardziej prawdopodobny wynik pomiaru bezpośrednio odpowiada rozwinięciu binarnemu fazy, a prawdopodobieństwo innych wyników maleje szybko wraz z liczbą qubitów liczących. Jednak uruchamianie głębokich Circuit QPE na sprzęcie wiąże się z wyzwaniami: duża liczba qubitów i operacji splątujących sprawia, że algorytm jest bardzo wrażliwy na dekoherencję i błędy bramek. Skutkuje to rozszerzeniem i przesunięciem rozkładów łańcuchów bitów, maskując prawdziwą eigenphase. W konsekwencji łańcuch bitów o najwyższym prawdopodobieństwie może już nie odpowiadać poprawnemu rozwinięciu binarnemu .
W tym samouczku przedstawiamy implementację algorytmu QPE z wykorzystaniem narzędzi do tłumienia błędów i zarządzania wydajnością Fire Opal od Q-CTRL, oferowanych jako funkcja Qiskit (zobacz dokumentację Fire Opal). Fire Opal automatycznie stosuje zaawansowane optymalizacje, w tym dynamiczne rozsprzęganie, ulepszenia rozmieszczenia qubitów oraz techniki tłumienia błędów, co skutkuje wynikami o wyższej wierności. Te ulepszenia przybliżają sprzętowe rozkłady łańcuchów bitów do tych uzyskiwanych w bezszumowych symulacjach, dzięki czemu możesz niezawodnie zidentyfikować poprawną eigenphase nawet w obecności szumów.
Wymagania
Przed rozpoczęciem tego samouczka upewnij się, że masz zainstalowane następujące pakiety:
- Qiskit SDK v1.4 lub nowszy, z obsługą wizualizacji
- Qiskit Runtime v0.40 lub nowszy (
pip install qiskit-ibm-runtime) - Qiskit Functions Catalog v0.9.0 (
pip install qiskit-ibm-catalog) - Fire Opal SDK v9.0.2 lub nowszy (
pip install fire-opal) - Q-CTRL Visualizer v8.0.2 lub nowszy (
pip install qctrl-visualizer)
Konfiguracja
Najpierw uwierzytelnij się przy użyciu swojego klucza API IBM Quantum. Następnie wybierz funkcję Qiskit w następujący sposób. (Ten kod zakłada, że już zapisałeś swoje konto w lokalnym środowisku.)
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qctrlvisualizer qiskit qiskit-aer qiskit-ibm-catalog qiskit-ibm-runtime
from qiskit import QuantumCircuit
import numpy as np
import matplotlib.pyplot as plt
import qiskit
from qiskit import qasm2
from qiskit_aer import AerSimulator
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
import qctrlvisualizer as qv
from qiskit_ibm_catalog import QiskitFunctionsCatalog
plt.style.use(qv.get_qctrl_style())
catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
# Access Function
perf_mgmt = catalog.load("q-ctrl/performance-management")
Krok 1: Mapowanie klasycznych danych wejściowych na problem kwantowy
W tym samouczku ilustrujemy QPE do odtworzenia eigenphase znanego jednoQubitowego unitarnego. Unitarny, którego fazę chcemy estymować, to jednoQubitowa Gate fazy zastosowana do docelowego qubitu:
Przygotowujemy jego stan własny . Ponieważ jest wektorem własnym z wartością własną , eigenphase do estymacji wynosi:
Ustawiamy , więc prawdziwa faza to . Circuit QPE implementuje kontrolowane potęgi przez zastosowanie kontrolowanych rotacji fazy z kątami , a następnie stosuje odwrotną QFT do rejestru liczącego i mierzy go. Wynikowe łańcuchy bitów koncentrują się wokół binarnej reprezentacji .
Circuit używa qubitów liczących (do ustawienia precyzji estymacji) oraz jednego docelowego qubitu. Zaczynamy od zdefiniowania bloków konstrukcyjnych potrzebnych do implementacji QPE: Kwantowej Transformaty Fouriera (QFT) i jej odwrotności, funkcji pomocniczych do mapowania między dziesiętnymi i binarnymi ułamkami eigenphase oraz pomocników normalizujących surowe liczby do prawdopodobieństw w celu porównania wyników symulacji i sprzętu.
def inverse_quantum_fourier_transform(quantum_circuit, number_of_qubits):
"""
Apply an inverse Quantum Fourier Transform the first `number_of_qubits` qubits in the
`quantum_circuit`.
"""
for qubit in range(number_of_qubits // 2):
quantum_circuit.swap(qubit, number_of_qubits - qubit - 1)
for j in range(number_of_qubits):
for m in range(j):
quantum_circuit.cp(-np.pi / float(2 ** (j - m)), m, j)
quantum_circuit.h(j)
return quantum_circuit
def bitstring_count_to_probabilities(data, shot_count):
"""
This function turns an unsorted dictionary of bitstring counts into a sorted dictionary
of probabilities.
"""
# Turn the bitstring counts into probabilities.
probabilities = {
bitstring: bitstring_count / shot_count
for bitstring, bitstring_count in data.items()
}
sorted_probabilities = dict(
sorted(probabilities.items(), key=lambda x: x[1], reverse=True)
)
return sorted_probabilities
Krok 2: Optymalizacja problemu pod kątem wykonania na sprzęcie kwantowym
Budujemy Circuit QPE, przygotowując qubity liczące w superpozycji, stosując kontrolowane rotacje fazy do zakodowania docelowej eigenphase, a na końcu dodając odwrotną QFT przed pomiarem.
def quantum_phase_estimation_benchmark_circuit(
number_of_counting_qubits, phase
):
"""
Create the circuit for quantum phase estimation.
Parameters
----------
number_of_counting_qubits : The number of qubits in the circuit.
phase : The desired phase.
Returns
-------
QuantumCircuit
The quantum phase estimation circuit for `number_of_counting_qubits` qubits.
"""
qc = QuantumCircuit(
number_of_counting_qubits + 1, number_of_counting_qubits
)
target = number_of_counting_qubits
# |1> eigenstate for the single-qubit phase gate
qc.x(target)
# Hadamards on counting register
for q in range(number_of_counting_qubits):
qc.h(q)
# ONE controlled phase per counting qubit: cp(phase * 2**k)
for k in range(number_of_counting_qubits):
qc.cp(phase * (1 << k), k, target)
qc.barrier()
# Inverse QFT on counting register
inverse_quantum_fourier_transform(qc, number_of_counting_qubits)
qc.barrier()
for q in range(number_of_counting_qubits):
qc.measure(q, q)
return qc
Krok 3: Wykonanie przy użyciu prymitywów Qiskit
Ustawiamy liczbę próbek i qubitów do eksperymentu oraz kodujemy docelową fazę przy użyciu cyfr binarnych. Z tymi parametrami budujemy Circuit QPE, który zostanie wykonany na symulacji, domyślnym sprzęcie i Backend wzbogaconym przez Fire Opal.
shot_count = 10000
num_qubits = 35
phase = (1 / 6) * 2 * np.pi
circuits_quantum_phase_estimation = (
quantum_phase_estimation_benchmark_circuit(
number_of_counting_qubits=num_qubits, phase=phase
)
)
Uruchomienie symulacji MPS
Najpierw generujemy rozkład referencyjny przy użyciu symulatora matrix_product_state i konwertujemy liczby na znormalizowane prawdopodobieństwa do późniejszego porównania z wynikami sprzętowymi.
# Run the algorithm on the IBM Aer simulator.
aer_simulator = AerSimulator(method="matrix_product_state")
# Transpile the circuits for the simulator.
transpiled_circuits = qiskit.transpile(
circuits_quantum_phase_estimation, aer_simulator
)
simulated_result = (
aer_simulator.run(transpiled_circuits, shots=shot_count)
.result()
.get_counts()
)
simulated_result_probabilities = []
simulated_result_probabilities.append(
bitstring_count_to_probabilities(
simulated_result,
shot_count=shot_count,
)
)
Uruchomienie na sprzęcie
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
isa_circuits = pm.run(circuits_quantum_phase_estimation)
# Run the algorithm with IBM default.
sampler = Sampler(backend)
# Run all circuits using Qiskit Runtime.
ibm_default_job = sampler.run([isa_circuits], shots=shot_count)
Uruchomienie na sprzęcie z Fire Opal
# Run the circuit using the sampler
fire_opal_job = perf_mgmt.run(
primitive="sampler",
pubs=[qasm2.dumps(circuits_quantum_phase_estimation)],
backend_name=backend.name,
options={"default_shots": shot_count},
)
Krok 4: Przetwarzanie końcowe i zwrócenie wyników w pożądanym formacie klasycznym
# Retrieve results.
ibm_default_result = ibm_default_job.result()
ibm_default_probabilities = []
for idx, pub_result in enumerate(ibm_default_result):
ibm_default_probabilities.append(
bitstring_count_to_probabilities(
pub_result.data.c0.get_counts(),
shot_count=shot_count,
)
)
fire_opal_result = fire_opal_job.result()
fire_opal_probabilities = []
for idx, pub_result in enumerate(fire_opal_result):
fire_opal_probabilities.append(
bitstring_count_to_probabilities(
pub_result.data.c0.get_counts(),
shot_count=shot_count,
)
)
data = {
"simulation": simulated_result_probabilities,
"default": ibm_default_probabilities,
"fire_opal": fire_opal_probabilities,
}
def plot_distributions(
data,
number_of_counting_qubits,
top_k=None,
by="prob",
shot_count=None,
):
def nrm(d):
s = sum(d.values())
return {k: (v / s if s else 0.0) for k, v in d.items()}
def as_float(d):
return {k: float(v) for k, v in d.items()}
def to_space(d):
if by == "prob":
return nrm(as_float(d))
else:
if shot_count and 0.99 <= sum(d.values()) <= 1.01:
return {
k: v * float(shot_count) for k, v in as_float(d).items()
}
else:
return as_float(d)
def topk(d, k):
items = sorted(d.items(), key=lambda kv: kv[1], reverse=True)
return items[: (k or len(d))]
phase = "1/6"
sim = to_space(data["simulation"])
dft = to_space(data["default"])
qct = to_space(data["fire_opal"])
correct = max(sim, key=sim.get) if sim else None
print("Correct result:", correct)
sim_items = topk(sim, top_k)
dft_items = topk(dft, top_k)
qct_items = topk(qct, top_k)
sim_keys, y_sim = zip(*sim_items) if sim_items else ([], [])
dft_keys, y_dft = zip(*dft_items) if dft_items else ([], [])
qct_keys, y_qct = zip(*qct_items) if qct_items else ([], [])
fig, axes = plt.subplots(3, 1, layout="constrained")
ylab = "Probabilities"
def panel(ax, keys, ys, title, color):
x = np.arange(len(keys))
bars = ax.bar(x, ys, color=color)
ax.set_title(title)
ax.set_ylabel(ylab)
ax.set_xticks(x)
ax.set_xticklabels(keys, rotation=90)
ax.set_xlabel("Bitstrings")
if correct in keys:
i = keys.index(correct)
bars[i].set_edgecolor("black")
bars[i].set_linewidth(2)
return max(ys, default=0.0)
c_sim, c_dft, c_qct = (
qv.QCTRL_STYLE_COLORS[5],
qv.QCTRL_STYLE_COLORS[1],
qv.QCTRL_STYLE_COLORS[0],
)
m1 = panel(axes[0], list(sim_keys), list(y_sim), "Simulation", c_sim)
m2 = panel(axes[1], list(dft_keys), list(y_dft), "Default", c_dft)
m3 = panel(axes[2], list(qct_keys), list(y_qct), "Q-CTRL", c_qct)
for ax, m in zip(axes, (m1, m2, m3)):
ax.set_ylim(0, 1.05 * (m or 1.0))
for ax in axes:
ax.label_outer()
fig.suptitle(
rf"{number_of_counting_qubits} counting qubits, $2\pi\varphi$={phase}"
)
fig.set_size_inches(20, 10)
plt.show()
experiment_index = 0
phase_index = 0
distributions = {
"simulation": data["simulation"][phase_index],
"default": data["default"][phase_index],
"fire_opal": data["fire_opal"][phase_index],
}
plot_distributions(
distributions, num_qubits, top_k=100, by="prob", shot_count=shot_count
)
Correct result: 00101010101010101010101010101010101

Symulacja wyznacza punkt odniesienia dla prawidłowej eigenfazy. Uruchomienia na domyślnym sprzęcie wykazują szum zasłaniający ten wynik — szum rozprasza prawdopodobieństwo na wiele nieprawidłowych bitciągów. Dzięki Q-CTRL Performance Management rozkład staje się ostrzejszy i prawidłowy wynik zostaje odzyskany, umożliwiając wiarygodne QPE w tej skali.
Odniesienia
[1] Wykład 7: Estymacja fazy i faktoryzacja. IBM Quantum Learning – Fundamentals of quantum algorithms. Dostęp: 3 października 2025.
Ankieta dotycząca samouczka
Poświęć chwilę na przekazanie opinii na temat tego samouczka. Twoje spostrzeżenia pomogą nam ulepszyć nasze treści i doświadczenia użytkownika.
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.