Sari la conținutul principal

Estimarea Fazei Cuantice cu Funcțiile Qiskit ale Q-CTRL

Estimare de utilizare: 40 de secunde pe un procesor Heron r2. (NOTĂ: Aceasta este doar o estimare. Durata de execuție poate varia.)

Fundal

Estimarea Fazei Cuantice (QPE) este un algoritm fundamental în calculul cuantic care stă la baza multor aplicații importante, cum ar fi algoritmul lui Shor, estimarea energiei stării fundamentale în chimia cuantică și problemele de valori proprii. QPE estimează faza φ\varphi asociată cu un eigenstate al unui operator unitar, codificată în relația

Uφ=e2πiφφ,U \lvert \varphi \rangle = e^{2\pi i \varphi} \lvert \varphi \rangle,

și o determină cu o precizie de ϵ=O(1/2m)\epsilon = O(1/2^m) folosind mm qubiți de numărare [1]. Prin pregătirea acestor qubiți în superpozițe, aplicarea puterilor controlate ale lui UU și utilizarea Transformatei Cuantice Fourier inverse (QFT) pentru a extrage faza în rezultatele de măsurare codificate binar, QPE produce o distribuție de probabilitate concentrată în jurul șirurilor de biți ale căror fracții binare aproximează φ\varphi. În cazul ideal, cel mai probabil rezultat al măsurătorii corespunde direct expansiunii binare a fazei, în timp ce probabilitatea altor rezultate scade rapid odată cu numărul de qubiți de numărare. Cu toate acestea, rularea circuitelor QPE adânci pe hardware prezintă provocări: numărul mare de qubiți și operații de entanglament fac algoritmul extrem de sensibil la decoerență și erori de Gate. Aceasta duce la distribuții lărgite și deplasate ale șirurilor de biți, mascând eigenphase-ul real. Prin urmare, șirul de biți cu cea mai mare probabilitate poate să nu mai corespundă expansiunii binare corecte a lui φ\varphi.

În acest tutorial, prezentăm o implementare a algoritmului QPE folosind instrumentele de suprimare a erorilor și gestionare a performanței Fire Opal ale Q-CTRL, oferite ca o Funcție Qiskit (consultați documentația Fire Opal). Fire Opal aplică automat optimizări avansate, inclusiv decuplare dinamică, îmbunătățiri ale layout-ului de qubiți și tehnici de suprimare a erorilor, rezultând în rezultate de fidelitate mai ridicată. Aceste îmbunătățiri aduc distribuțiile de șiruri de biți ale hardware-ului mai aproape de cele obținute în simulări fără zgomot, astfel încât să poți identifica în mod fiabil eigenphase-ul corect chiar și sub efectele zgomotului.

Cerințe

Înainte de a începe acest tutorial, asigură-te că ai instalat următoarele:

  • Qiskit SDK v1.4 sau mai nou, cu suport pentru vizualizare
  • Qiskit Runtime v0.40 sau mai nou (pip install qiskit-ibm-runtime)
  • Qiskit Functions Catalog v0.9.0 (pip install qiskit-ibm-catalog)
  • Fire Opal SDK v9.0.2 sau mai nou (pip install fire-opal)
  • Q-CTRL Visualizer v8.0.2 sau mai nou (pip install qctrl-visualizer)

Configurare

Mai întâi, autentifică-te folosind cheia ta API IBM Quantum. Apoi, selectează Funcția Qiskit după cum urmează. (Acest cod presupune că ți-ai salvat deja contul în mediul local.)

# 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")

Pasul 1: Maparea intrărilor clasice la o problemă cuantică

În acest tutorial, ilustrăm QPE pentru a recupera eigenphase-ul unui unitar monoQubit cunoscut. Unitarul a cărui fază vrem să o estimăm este Gate-ul de fază monoQubit aplicat qubitului țintă:

U(θ)=(100eiθ)=eiθ1 ⁣1.U(\theta)= \begin{pmatrix} 1 & 0\\[2pt] 0 & e^{i\theta} \end{pmatrix} = e^{i\theta\,|1\rangle\!\langle 1|}.

Pregătim eigenstarea sa ψ=1|\psi\rangle=|1\rangle. Deoarece 1|1\rangle este un eigenvector al lui U(θ)U(\theta) cu valoarea proprie eiθe^{i\theta}, eigenphase-ul care trebuie estimat este:

φ=θ2π(mod1)\varphi = \frac{\theta}{2\pi} \pmod{1}

Setăm θ=162π\theta=\tfrac{1}{6}\cdot 2\pi, astfel încât faza reală este φ=1/6\varphi=1/6. Circuit-ul QPE implementează puterile controlate U2kU^{2^k} prin aplicarea rotațiilor de fază controlate cu unghiurile θ2k\theta\cdot2^k, apoi aplică QFT invers registrului de numărare și îl măsoară. Șirurile de biți rezultate se concentrează în jurul reprezentării binare a lui 1/61/6.

Circuit-ul folosește mm qubiți de numărare (pentru a seta precizia estimării) plus un Qubit țintă. Începem prin definirea blocurilor de construcție necesare pentru a implementa QPE: Transformata Cuantică Fourier (QFT) și inversa sa, funcții utilitare pentru a mapa între fracții zecimale și binare ale eigenphase-ului, și funcții ajutătoare pentru a normaliza contorizările brute în probabilități pentru compararea rezultatelor simulării cu cele ale hardware-ului.

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

Pasul 2: Optimizarea problemei pentru execuția pe hardware cuantic

Construim Circuit-ul QPE pregătind qubiții de numărare în superpozițe, aplicând rotații de fază controlate pentru a codifica eigenphase-ul țintă și finalizând cu un QFT invers înainte de măsurare.

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

Pasul 3: Execuție folosind primitivele Qiskit

Setăm numărul de shot-uri și qubiți pentru experiment și codificăm faza țintă φ=1/6\varphi = 1/6 folosind mm cifre binare. Cu acești parametri, construim Circuit-ul QPE care va fi executat pe simulare, hardware implicit și Backend-uri îmbunătățite cu 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
)
)

Rularea simulării MPS

Mai întâi, generăm o distribuție de referință folosind simulatorul matrix_product_state și convertim contorizările în probabilități normalizate pentru compararea ulterioară cu rezultatele hardware-ului.

# 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,
)
)

Rularea pe hardware

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)

Rularea pe hardware cu 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},
)

Pasul 4: Post-procesare și returnarea rezultatului în formatul clasic dorit

# 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

Output of the previous code cell

Simularea stabilește linia de referință pentru faza proprie corectă. Rulările pe hardware implicit arată zgomot care maschează acest rezultat, deoarece zgomotul răspândește probabilitatea pe mulți biți incorecți. Cu Q-CTRL Performance Management, distribuția devine mai clară și rezultatul corect este recuperat, permițând QPE fiabil la această scară.

Referințe

[1] Lectura 7: Estimarea fazei și factorizarea. IBM Quantum Learning - Fundamentals of quantum algorithms. Accesat la 3 octombrie 2025.

Sondaj tutorial

Te rog să acorzi un minut pentru a oferi feedback despre acest tutorial. Informațiile tale ne vor ajuta să îmbunătățim conținutul și experiența utilizatorilor.

Link către sondaj

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.