Kvantový odhad fáze s Qiskit Functions od Q-CTRL
Odhadovaná spotřeba: 40 sekund na procesoru Heron r2. (POZNÁMKA: Jedná se pouze o odhad. Skutečná doba běhu se může lišit.)
Pozadí
Kvantový odhad fáze (QPE) je základní algoritmus kvantového výpočtu, který tvoří základ mnoha důležitých aplikací, jako jsou Shorův algoritmus, odhad energie základního stavu v kvantové chemii a úlohy s vlastními hodnotami. QPE odhaduje fázi spojenou s vlastním stavem unitárního operátoru, zakódovanou ve vztahu
a určuje ji s přesností pomocí počítacích qubitů [1]. QPE připraví tyto qubity v superpozici, aplikuje řízené mocniny a poté pomocí inverzní Kvantové Fourierovy transformace (QFT) extrahuje fázi do binárně zakódovaných výsledků měření. QPE tak produkuje pravděpodobnostní distribuci soustředěnou okolo bitových řetězců, jejichž binární zlomky aproximují . V ideálním případě nejpravděpodobnější výsledek měření přímo odpovídá binárnímu rozvoji fáze, zatímco pravděpodobnost ostatních výsledků rychle klesá s počtem počítacích qubitů. Spouštění hlubokých QPE okruhů na hardwaru však přináší výzvy: velký počet qubitů a propletovacích operací činí algoritmus vysoce citlivým na dekoherenci a chyby hradel. To vede k rozšířeným a posunutým distribucím bitových řetězců, které zakrývají skutečnou vlastní fázi. Výsledkem je, že bitový řetězec s nejvyšší pravděpodobností už nemusí odpovídat správnému binárnímu rozvoji .
V tomto tutoriálu představujeme implementaci algoritmu QPE pomocí nástrojů pro potlačení chyb a správu výkonu Fire Opal od Q-CTRL, nabízených jako Qiskit Function (viz dokumentaci Fire Opal). Fire Opal automaticky aplikuje pokročilé optimalizace, včetně dynamického oddělování, vylepšení rozmístění qubitů a technik potlačení chyb, což vede k výsledkům s vyšší věrností. Tato vylepšení přibližují distribuce bitových řetězců z hardwaru těm získaným v simulacích bez šumu, takže dokážeš spolehlivě identifikovat správnou vlastní fázi i při vlivu šumu.
Požadavky
Než začneš s tímto tutoriálem, ujisti se, že máš nainstalované následující:
- Qiskit SDK v1.4 nebo novější s podporou vizualizace
- Qiskit Runtime v0.40 nebo novější (
pip install qiskit-ibm-runtime) - Qiskit Functions Catalog v0.9.0 (
pip install qiskit-ibm-catalog) - Fire Opal SDK v9.0.2 nebo novější (
pip install fire-opal) - Q-CTRL Visualizer v8.0.2 nebo novější (
pip install qctrl-visualizer)
Nastavení
Nejprve se autentizuj pomocí svého IBM Quantum API klíče. Poté vyber Qiskit Function takto. (Tento kód předpokládá, že sis již uložil/a svůj účet do místního prostředí.)
# 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: Mapování klasických vstupů na kvantový problém
V tomto tutoriálu ilustrujeme QPE na příkladu obnovy vlastní fáze známé jednoQubitové unitární matice. Unitární matice, jejíž fázi chceme odhadnout, je hradlo fáze jednoho qubitu aplikované na cílový Qubit:
Připravíme jeho vlastní stav . Protože je vlastní vektor s vlastní hodnotou , odhadovaná vlastní fáze je:
Nastavíme , takže skutečná fáze je . QPE Circuit implementuje řízené mocniny aplikací řízených fázových rotací s úhly , poté aplikuje inverzní QFT na počítací registr a změří ho. Výsledné bitové řetězce se soustřeďují okolo binárního vyjádření .
Circuit používá počítacích qubitů (pro nastavení přesnosti odhadu) plus jeden cílový Qubit. Začneme definováním stavebních bloků potřebných k implementaci QPE: Kvantová Fourierova transformace (QFT) a její inverzní verze, pomocné funkce pro převod mezi desítkovými a binárními zlomky vlastní fáze a pomocníci pro normalizaci surových počtů na pravděpodobnosti pro srovnání výsledků simulace a hardwaru.
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: Optimalizace problému pro spuštění na kvantovém hardwaru
Sestavíme QPE Circuit tak, že připravíme počítací qubity v superpozici, aplikujeme řízené fázové rotace pro zakódování cílové vlastní fáze a zakončíme inverzní QFT před měřením.
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: Spuštění pomocí primitiv Qiskit
Nastavíme počet snímků a qubitů pro experiment a zakódujeme cílovou fázi pomocí binárních číslic. S těmito parametry sestavíme QPE Circuit, který bude spuštěn na simulaci, výchozím hardwaru a Backend vylepšeném pomocí 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
)
)
Spuštění MPS simulace
Nejprve vygenerujeme referenční distribuci pomocí simulátoru matrix_product_state a převedeme počty na normalizované pravděpodobnosti pro pozdější srovnání s výsledky z hardwaru.
# 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,
)
)
Spuštění na hardwaru
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)
Spuštění na hardwaru s 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: Následné zpracování a vrácení výsledku v požadovaném klasickém formátu
# 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

Simulace nastavuje základní linii pro správnou vlastní fázi. Výchozí hardwarové běhy vykazují šum, který tento výsledek zakrývá, protože šum rozptyluje pravděpodobnost do mnoha nesprávných bitových řetězců. S Q-CTRL Performance Management se distribuce zostřuje a správný výsledek je obnoven, což umožňuje spolehlivé QPE v tomto měřítku.
Reference
[1] Přednáška 7: Odhad fáze a faktorizace. IBM Quantum Learning - Základy kvantových algoritmů. Přístup 3. října 2025.
Průzkum k tutoriálu
Věnuj prosím chvíli zpětné vazbě k tomuto tutoriálu. Tvé postřehy nám pomohou zlepšit naše výukové materiály a uživatelský zážitek.
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.