Quantumphasenschätzung mit dem Q-CTRL- Qiskit Functions
Geschätzte Nutzungsdauer: 40 Sekunden auf einem Heron r2 Prozessor. (HINWEIS: Dies ist nur eine Schätzung. Ihre Laufzeit kann variieren.)
Hintergrund
Die Quantenphasenschätzung (Quantum Phase Estimation, QPE) ist ein grundlegender Algorithmus in der Quanteninformatik, der die Basis für viele wichtige Anwendungen wie den Shor-Algorithmus, die Abschätzung der Grundzustandsenergie in der Quantenchemie und Eigenwertprobleme bildet. QPE schätzt die Phase , die mit einem Eigenzustand eines unitären Operators verbunden ist, kodiert in der Beziehung
und bestimmt es mit einer Genauigkeit von unter Verwendung von Zählqubits [1]. Indem man diese Qubits in Überlagerung präpariert, kontrollierte Potenzen von anwendet und dann die inverse Quanten-Fourier-Transformation (QFT) nutzt, um die Phase in binär kodierte Messergebnisse zu extrahieren, erzeugt QPE eine Wahrscheinlichkeitsverteilung, die bei Bitstrings gipfelt, deren binäre Brüche sich annähern. Im Idealfall entspricht das wahrscheinlichste Messergebnis direkt der binären Erweiterung der Phase, während die Wahrscheinlichkeit anderer Ergebnisse schnell mit der Anzahl der zählenden Qubits abnimmt. Der Betrieb tiefer QPE-Schaltkreise auf Hardware stellt jedoch eine Herausforderung dar: Die große Anzahl von Qubits und Verschränkungsoperationen machen den Algorithmus sehr anfällig für Dekohärenz und Gatterfehler. Dies führt zu verbreiterten und verschobenen Verteilungen von Bitstrings, die die wahre Eigenphase verdecken. Dies hat zur Folge, dass die Bitfolge mit der höchsten Wahrscheinlichkeit nicht mehr der korrekten binären Erweiterung von entspricht.
In diesem Tutorial stellen wir eine Implementierung des QPE-Algorithmus unter Verwendung von Q-CTRLs Fire Opal-Fehlerunterdrückungs- und Leistungsmanagement-Tools vor, die als Qiskit-Funktion angeboten werden (siehe die Fire Opal-Dokumentation ). Fire Opal wendet automatisch fortschrittliche Optimierungen an, einschließlich dynamischer Entkopplung, Verbesserungen des Qubit-Layouts und Fehlerunterdrückungstechniken, was zu realitätsgetreueren Ergebnissen führt. Diese Verbesserungen bringen die Hardware-Bitstring-Verteilungen näher an die in rauschfreien Simulationen erhaltenen Verteilungen heran, so dass Sie auch unter Rauscheffekten zuverlässig die richtige Eigenphase identifizieren können.
Anforderungen
Bevor Sie mit diesem Tutorial beginnen, sollten Sie sicherstellen, dass Sie Folgendes installiert haben:
- Qiskit SDK v1.4 oder später, mit Visual isierungsunterstützung
- Qiskit Runtime v0.40 oder später (
pip install qiskit-ibm-runtime) - Qiskit Functions Catalog v0.9.0 (
pip install qiskit-ibm-catalog) - Fire Opal SDK v9.0.2 oder höher (
pip install fire-opal) - Q-CTRL Visualizer v8.0.2 oder höher (
pip install qctrl-visualizer)
Einrichtung
Melden Sie sich zunächst mit Ihrem API-Schlüssel für „ IBM Quantum “ an. Wählen Sie anschließend die Qiskit-Funktion wie folgt aus. (Dieser Code setzt voraus, dass Sie Ihr Konto bereits in Ihrer lokalen Umgebung gespeichert haben.)
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")Schritt 1: Klassische Eingaben einem Quantenproblem zuordnen
In diesem Tutorial veranschaulichen wir QPE, um die Eigenphase eines bekannten Ein-Qubit-Unitariums wiederherzustellen. Die Einheit, deren Phase wir schätzen wollen, ist das Ein-Qubit-Phasentor, das auf das Ziel-Qubit angewendet wird:
Wir bereiten seinen Eigenzustand vor. Da ein Eigenvektor von mit dem Eigenwert ist, lautet die zu schätzende Eigenphase:
Wir stellen ein, so dass die Phase der Grundwahrheit ist. Der QPE-Schaltkreis implementiert die kontrollierten Potenzen durch Anwendung kontrollierter Phasendrehungen mit Winkeln , wendet dann die inverse QFT auf das Zählregister an und misst sie. Die resultierenden Bitstrings konzentrieren sich auf die binäre Darstellung von .
Die Schaltung verwendet Zählende Qubits (zur Einstellung der Schätzgenauigkeit) plus ein Ziel-Qubit. Wir beginnen mit der Definition der Bausteine, die für die Implementierung von QPE erforderlich sind: die Quanten-Fourier-Transformation (QFT) und ihre Umkehrung, Hilfsfunktionen zur Umrechnung zwischen Dezimal- und Binärbruchteilen der Eigenphase und Hilfsfunktionen zur Normalisierung von Rohwerten in Wahrscheinlichkeiten für den Vergleich von Simulations- und Hardwareergebnissen.
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_circuitdef 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_probabilitiesSchritt 2: Optimierung des Problems für die Ausführung auf Quantenhardware
Wir bauen die QPE-Schaltung auf, indem wir die zählenden Qubits in Überlagerung vorbereiten, kontrollierte Phasendrehungen anwenden, um die Zieleigenphase zu kodieren, und mit einer inversen QFT vor der Messung abschließen.
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 qcSchritt 3: Mit „ Qiskit primitives “ ausführen
Wir legen die Anzahl der Schüsse und Qubits für das Experiment fest und kodieren die Zielphase mit Binärziffern. Mit diesen Parametern erstellen wir den QPE-Schaltkreis, der auf der Simulation, der Standardhardware und den mit Fire Opal erweiterten Backends ausgeführt wird.
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
)
)MPS-Simulation ausführen
Zunächst erzeugen wir mit dem matrix_product_state Simulator eine Referenzverteilung und wandeln die Zählungen in normalisierte Wahrscheinlichkeiten um, um sie später mit den Hardware-Ergebnissen zu vergleichen.
# 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,
)
)Auf Hardware ausführen
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 IBM Quantum Compute Service.
ibm_default_job = sampler.run([isa_circuits], shots=shot_count)Auf Hardware mit Fire Opal ausführen
# Run the circuit using 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},
)Schritt 4: Nachbearbeitung und Rückgabe des Ergebnisses im gewünschten klassischen Format
# 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
)Output:
Correct result: 00101010101010101010101010101010101
Die Simulation legt die Basis für die richtige Eigenphase fest. Standard-Hardwareläufe zeigen Rauschen, das dieses Ergebnis verschleiert, da das Rauschen die Wahrscheinlichkeit über viele falsche Bitstrings verteilt. Mit Q-CTRL Performance Management wird die Verteilung schärfer und das korrekte Ergebnis wird wiederhergestellt, was eine zuverlässige QPE in diesem Umfang ermöglicht.
Referenzen
[1] Vorlesung 7: Phasenabschätzung und Faktorisierung. IBM Quantum Learning - Grundlagen der Quantenalgorithmen. Abgerufen am 3. Oktober 2025.
Tutorial-Umfrage
Bitte nehmen Sie sich eine Minute Zeit, um uns Ihr Feedback zu diesem Tutorial zu geben. Ihre Erkenntnisse helfen uns, unser Inhaltsangebot und unsere Nutzererfahrung zu verbessern.