Grovers Algorithmus
Nutzungsschätzung: under aner Minute uf emm Eagle r3-Prozessor (HINWEIS: Des isch nur e Schätzung. Dini Laufzeit ka variiere.)
Hintergrund
Amplitudenverstärkung isch e universeller Quantenalgorithmus oder e Unterroutine, wo mer verwende ka, um e quadratische Beschleunigung gegenüber anere Handvoll klassische Algorithmen z'erziele. Grovers Algorithmus isch dr erschte gsi, wo diese Beschleunigung bi unstrukturierte Suchproblemen zeigt hän. D'Formulierung vo emm Groverschen Suchproblem erfordert e Orakelfunktion, wo oine oder mehreri Basiszuständ als die Zuständ markiert, wo mir finde möchte, un en Verstärkungsschaltkreis, wo d'Amplitude vo de markierte Zuständ erhöht un folglich d'verbleibende Zuständ unterdrückt.
Do zeige mir, wie mer Grover-Orakel konstruiert un den grover_operator() aus dr Qiskit-Schaltkreisbibliothek verwendet, um eifach e Groversche Suchinstanz iizrichte. Des Runtime Sampler-Primitiv ermöglicht d'nahtlose Ausführung vo Grover-Schaltkreisen.
Anforderungen
Schau vor em Aafange vo diesem Tutorial, dass mir des Folgende installiert hän:
- Qiskit SDK v1.4 oder neier, mit visualization-Unterstützung
- Qiskit Runtime (
pip install qiskit-ibm-runtime) v0.36 oder neier
Setup
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
# Built-in modules
import math
# Imports from Qiskit
from qiskit import QuantumCircuit
from qiskit.circuit.library import grover_operator, MCMTGate, ZGate
from qiskit.visualization import plot_distribution
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
# Imports from Qiskit Runtime
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler
def grover_oracle(marked_states):
"""Build a Grover oracle for multiple marked states
Here we assume all input marked states have the same number of bits
Parameters:
marked_states (str or list): Marked states of oracle
Returns:
QuantumCircuit: Quantum circuit representing Grover oracle
"""
if not isinstance(marked_states, list):
marked_states = [marked_states]
# Compute the number of qubits in circuit
num_qubits = len(marked_states[0])
qc = QuantumCircuit(num_qubits)
# Mark each target state in the input list
for target in marked_states:
# Flip target bit-string to match Qiskit bit-ordering
rev_target = target[::-1]
# Find the indices of all the '0' elements in bit-string
zero_inds = [
ind
for ind in range(num_qubits)
if rev_target.startswith("0", ind)
]
# Add a multi-controlled Z-gate with pre- and post-applied X-gates (open-controls)
# where the target bit-string has a '0' entry
if zero_inds:
qc.x(zero_inds)
qc.compose(MCMTGate(ZGate(), num_qubits - 1, 1), inplace=True)
if zero_inds:
qc.x(zero_inds)
return qc
Schritt 1: Klassische Eingaben uf es Quantenproblem abbilden
Grovers Algorithmus bruucht es Orakel, wo oine oder mehreri markierte Basiszuständ spezifiziert, wobei "markiert" en Zustand mit enere Phase vo -1 bedeutet. E Controlled-Z-Gate oder sini mehrfach kontrollierte Verallgemeinerung über Qubits markiert de -Zustand ('1'* Bit-String). Des Markiere vo Basiszuständen mit oim oder mehrere '0' in dr binäre Darstellung erfordert des Aawendem vo X-Gates uf d'entsprechende Qubits vor un nach em Controlled-Z-Gate; des entspricht enere offene Kontrolle uf diesem Qubit. Im nachfolgende Code definiere mir e Orakel, wo genau des macht un oine oder mehreri Eingabe-Basiszuständ markiert, wo durch ihri Bit-String-Darstellung definiert send. Des MCMT-Gate wird verwendet, um des mehrfach kontrollierte Z-Gate z'implementiere.
# To run on hardware, select the backend with the fewest number of jobs in the queue
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
backend.name
'ibm_brisbane'
Spezifischi Grover-Instanz
Da mir jetzt d'Orakelfunktion hän, könne mir e spezifischi Instanz vo dr Groverschen Suche definiere. In diesem Bispiel werre mir zwei Berechnungszuständ aus de acht verfügbare in emm Drei-Qubit-Berechnungsraum markiere:
marked_states = ["011", "100"]
oracle = grover_oracle(marked_states)
oracle.draw(output="mpl", style="iqp")
marked_states = ["011", "100"]
oracle = grover_oracle(marked_states)
oracle.draw(output="mpl", style="iqp")

marked_states = ["011", "100"]
oracle = grover_oracle(marked_states)
oracle.draw(output="mpl", style="iqp")

Grover-Operator
Dr iigebaute Qiskit grover_operator() nimmt en Orakel-Schaltkreis entgege un gibt en Schaltkreis zruck, wo aus em Orakel-Schaltkreis selber un emm Schaltkreis besteht, wo d'vom Orakel markierte Zuständ verstärkt. Do verwende mir d'decompose()-Methode vo dem Schaltkreis, um d'Gates innerhalb vo em Operator z'gseh:
grover_op = grover_operator(oracle)
grover_op.decompose().draw(output="mpl", style="iqp")
Wiederholte Aawendunge vo diesem grover_op-Schaltkreis verstärke d'markierte Zuständ un mache sie zu de wahrscheinlichste Bit-Strings in dr Ausgabeverteilung vo dem Schaltkreis. Es git e optimali Anzahl selle Aawendunge, wo durch des Verhältnis vo markierte Zuständen zur Gesamtzahl möglicher Berechnungszuständ bestimmt wird:
optimal_num_iterations = math.floor(
math.pi
/ (4 * math.asin(math.sqrt(len(marked_states) / 2**grover_op.num_qubits)))
)
Vollständige Grover-Schaltkreis
E vollständiges Grover-Experiment aafangt mit emm Hadamard-Gate uf jedem Qubit; des erzeugt e gleichmäßige Überlagerung aller Berechnungsbasisstände, gefolgt vom Grover-Operator (grover_op), wo d'optimali Anzahl vo Male widderholt wird. Do verwende mir d'QuantumCircuit.power(INT)-Methode, um de Grover-Operator wiederholt aazuwende.
qc = QuantumCircuit(grover_op.num_qubits)
# Create even superposition of all basis states
qc.h(range(grover_op.num_qubits))
# Apply Grover operator the optimal number of times
qc.compose(grover_op.power(optimal_num_iterations), inplace=True)
# Measure all qubits
qc.measure_all()
qc.draw(output="mpl", style="iqp")
Schritt 2: Problem für d'Ausführung uf Quantenhardware optimiere
target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
circuit_isa = pm.run(qc)
circuit_isa.draw(output="mpl", idle_wires=False, style="iqp")

Schritt 3: Ausführe mit Qiskit-Primitiven
Amplitudenverstärkung isch es Sampling-Problem, wo für d'Ausführung mit em Sampler-Runtime-Primitiv geeignet isch.
Beachte, dass d'run()-Methode vom Qiskit Runtime SamplerV2 es Iterable vo primitive unified blocks (PUBs) akzeptiert. Für de Sampler isch jedes PUB es Iterable im Format (circuit, parameter_values). Mindestens akzeptiert er aber e Lischt vo Quantenschaltkreis(en).
# To run on local simulator:
# 1. Use the StatevectorSampler from qiskit.primitives instead
sampler = Sampler(mode=backend)
sampler.options.default_shots = 10_000
result = sampler.run([circuit_isa]).result()
dist = result[0].data.meas.get_counts()
Schritt 4: Nachbearbeitung un Rückgabe vom Ergebnis im gewünschte klassische Format
plot_distribution(dist)
Tutorial-Umfrage
Bitte nimm dir e kurzi Minute Zeit, um Feedback zu diesem Tutorial z'gebe. Dini Erkenntnisse helfe ons, unseri Inhaltsangebote un Benutzererfahrung z'verbessere.