Exakte Simulation mit Qiskit SDK Primitives
Paketversionen
Der Code auf dieser Seite wurde mit den folgenden Anforderungen entwickelt. Wir empfehlen, diese Versionen oder neuere zu verwenden.
qiskit[all]~=2.3.0
Die Referenz-Primitives im Qiskit SDK führen lokale Statevector-Simulationen durch. Diese Simulationen unterstützen keine Modellierung von Geräterauschen, eignen sich aber gut zum schnellen Prototypisieren von Algorithmen, bevor du fortgeschrittenere Simulationstechniken (mit Qiskit Aer) oder echte Geräte (Qiskit Runtime Primitives) nutzt.
Das Estimator Primitive kann Erwartungswerte von Circuits berechnen, und das Sampler Primitive kann aus den Ausgabeverteilungen von Circuits sampeln.
Die folgenden Abschnitte zeigen, wie du die Referenz-Primitives verwendest, um deinen Workflow lokal auszuführen.
Den Referenz-Estimator verwenden
Die Referenzimplementierung von EstimatorV2 in qiskit.primitives, die auf einem lokalen Statevector-Simulator läuft,
ist die Klasse StatevectorEstimator. Sie akzeptiert Circuits, Observablen und Parameter als Eingaben und gibt die lokal berechneten Erwartungswerte zurück.
Der folgende Code bereitet die Eingaben vor, die in den nachfolgenden Beispielen verwendet werden. Der erwartete Eingabetyp für die
Observablen ist qiskit.quantum_info.SparsePauliOp. Beachte, dass
der Circuit im Beispiel parametrisiert ist, du aber den Estimator auch auf nicht-parametrisierte Circuits anwenden kannst.
Jeder Circuit, der an einen Estimator übergeben wird, darf keine Messungen enthalten.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
from qiskit import QuantumCircuit
from qiskit.circuit import Parameter
# circuit for which you want to obtain the expected value
circuit = QuantumCircuit(2)
circuit.ry(Parameter("theta"), 0)
circuit.h(0)
circuit.cx(0, 1)
circuit.draw("mpl", style="iqp")
from qiskit.quantum_info import SparsePauliOp
import numpy as np
# observable(s) whose expected values you want to compute
observable = SparsePauliOp(["II", "XX", "YY", "ZZ"], coeffs=[1, 1, -1, 1])
# value(s) for the circuit parameter(s)
parameter_values = [[0], [np.pi / 6], [np.pi / 2]]
Der Qiskit Runtime Primitives-Workflow erfordert, dass Circuits und Observablen so transformiert werden, dass sie nur Anweisungen verwenden, die vom QPU unterstützt werden (auch als Instruction Set Architecture (ISA)-Circuits und Observablen bezeichnet). Die Referenz-Primitives akzeptieren weiterhin abstrakte Anweisungen, da sie auf lokalen Statevector-Simulationen basieren. Das Transpilieren des Circuits kann jedoch in Bezug auf die Circuit-Optimierung dennoch vorteilhaft sein.
# Generate a pass manager without providing a backend
from qiskit.transpiler import generate_preset_pass_manager
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
Estimator initialisieren
Instanziiere einen qiskit.primitives.StatevectorEstimator.
from qiskit.primitives import StatevectorEstimator
estimator = StatevectorEstimator()
Ausführen und Ergebnisse abrufen
Dieses Beispiel verwendet nur einen Circuit (vom Typ QuantumCircuit) und eine
Observable.
Führe die Schätzung aus, indem du die Methode StatevectorEstimator.run aufrufst, die eine Instanz eines PrimitiveJob-Objekts zurückgibt. Du kannst die Ergebnisse aus dem Job (als qiskit.primitives.PrimitiveResult-Objekt)
mit der Methode qiskit.primitives.PrimitiveJob.result abrufen.
job = estimator.run([(circuit, observable, parameter_values)])
result = job.result()
print(f" > Result class: {type(result)}")
> Result class: <class 'qiskit.primitives.containers.primitive_result.PrimitiveResult'>
Den Erwartungswert aus dem Ergebnis auslesen
Die Ausgabe der Primitives enthält ein Array von PubResult-Objekten, wobei jedes Element des Arrays ein PubResult-Objekt ist, das in seinen Daten das Array der Auswertungen für jede Circuit-Observable-Kombination im PUB enthält.
Um die Erwartungswerte und Metadaten für die erste (und in diesem Fall einzige) Circuit-Auswertung abzurufen, musst du auf die Auswertungs-data für PUB 0 zugreifen:
print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")
> Expectation value: [4. 3.73205081 2. ]
> Metadata: {'target_precision': 0.0, 'circuit_metadata': {}}
Estimator-Ausführungsoptionen festlegen
Standardmäßig führt der Referenz-Estimator eine exakte Statevector-Berechnung basierend auf der
Klasse quantum_info.Statevector durch.
Dies kann jedoch angepasst werden, um den Effekt des Sampling-Overheads (auch bekannt als „Shot-Rauschen") einzubeziehen.
Der Estimator akzeptiert ein precision-Argument, das die Fehlerbalken angibt, auf die die
Primitive-Implementierung bei Erwartungswert-Schätzungen abzielen soll. Dies ist der Sampling-Overhead und wird ausschließlich in der .run()-Methode definiert. Damit kannst du die Option bis auf PUB-Ebene fein abstimmen.
# Estimate expectation values for two PUBs, both with 0.05 precision.
precise_job = estimator.run(
[(circuit, observable, parameter_values)], precision=0.05
)
Ein vollständiges Beispiel findest du auf der Seite Primitives-Beispiele.
Den Referenz-Sampler verwenden
Die Referenzimplementierung von SamplerV2 in qiskit.primitives ist die Klasse StatevectorSampler. Sie akzeptiert Circuits und Parameter als Eingaben und gibt die Ergebnisse des Sampelns aus den Ausgabe-Wahrscheinlichkeitsverteilungen als Quasi-Wahrscheinlichkeitsverteilung der Ausgabezustände zurück.
Der folgende Code bereitet die Eingaben vor, die in den nachfolgenden Beispielen verwendet werden. Beachte, dass diese Beispiele einen einzelnen parametrisierten Circuit ausführen, du den Sampler aber auch auf nicht-parametrisierte Circuits anwenden kannst.
from qiskit import QuantumCircuit
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw("mpl", style="iqp")
Jeder Quantenschaltkreis, der an einen Sampler übergeben wird, muss Messungen enthalten.
Der Qiskit Runtime Primitives-Workflow erfordert, dass Circuits so transformiert werden, dass sie nur Anweisungen verwenden, die vom QPU unterstützt werden (auch als ISA-Circuits bezeichnet). Die Referenz-Primitives akzeptieren weiterhin abstrakte Anweisungen, da sie auf lokalen Statevector-Simulationen basieren. Das Transpilieren des Circuits kann jedoch in Bezug auf die Circuit-Optimierung dennoch vorteilhaft sein.
# Generate a pass manager without providing a backend
from qiskit.transpiler import generate_preset_pass_manager
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(qc)
SamplerV2 initialisieren
Instanziiere qiskit.primitives.StatevectorSampler:
from qiskit.primitives import StatevectorSampler
sampler = StatevectorSampler()
Ausführen und Ergebnisse abrufen
# execute 1 circuit with Sampler
job = sampler.run([circuit])
pub_result = job.result()[0]
print(f" > Result class: {type(pub_result)}")
> Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>
Primitives akzeptieren mehrere PUBs als Eingaben, und jeder PUB erhält sein eigenes Ergebnis. Daher kannst du verschiedene Circuits mit unterschiedlichen Parameter-/Observable-Kombinationen ausführen und die PUB-Ergebnisse abrufen:
from qiskit.transpiler import generate_preset_pass_manager
# create two circuits
circuit1 = circuit.copy()
circuit2 = circuit.copy()
# transpile circuits
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit1 = pm.run(circuit1)
isa_circuit2 = pm.run(circuit2)
# execute 2 circuits using Sampler
job = sampler.run([(isa_circuit1), (isa_circuit2)])
pub_result_1 = job.result()[0]
pub_result_2 = job.result()[1]
print(f" > Result class: {type(pub_result)}")
> Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>
Wahrscheinlichkeitsverteilung oder Messergebnis abrufen
Messergebnisse werden als Bitstrings oder Counts zurückgegeben. Die Bitstrings zeigen die Messergebnisse und bewahren dabei die Reihenfolge der Shots, in der sie gemessen wurden. Die Sampler-Ergebnisobjekte organisieren Daten anhand der Namen der klassischen Register ihrer Eingabe-Circuits, um die Kompatibilität mit dynamischen Circuits sicherzustellen.
Der Name des klassischen Registers ist standardmäßig "meas". Dieser Name wird später verwendet, um auf die Mess-Bitstrings zuzugreifen.
# Define quantum circuit with 2 qubits
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw()
┌───┐ ░ ┌─┐
q_0: ┤ H ├──■───░─┤M├───
└───┘┌─┴─┐ ░ └╥┘┌─┐
q_1: ─────┤ X ├─░──╫─┤M├
└───┘ ░ ║ └╥┘
meas: 2/══════════════╩══╩═
0 1
# Transpile circuit
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(circuit)
# Run using sampler
result = sampler.run([circuit]).result()
# Access result data for PUB 0
data_pub = result[0].data
# Access bitstring for the classical register "meas"
bitstrings = data_pub.meas.get_bitstrings()
print(f"The number of bitstrings is: {len(bitstrings)}")
# Get counts for the classical register "meas"
counts = data_pub.meas.get_counts()
print(f"The counts are: {counts}")
The number of bitstrings is: 1024
The counts are: {'11': 515, '00': 509}
Ausführungsoptionen ändern
Standardmäßig führt der Referenz-Sampler eine exakte Statevector-Berechnung basierend auf der
Klasse quantum_info.Statevector durch.
Dies kann jedoch angepasst werden, um den Effekt des Sampling-Overheads (auch bekannt als „Shot-Rauschen") einzubeziehen. Um diesen Overhead zu steuern, akzeptiert das Sampler-Interface ein shots-Argument, das auf PUB-Ebene definiert werden kann.
Dieses Beispiel setzt voraus, dass du zwei Circuits definiert hast.
# Sample two circuits at 128 shots each.
sampler.run([isa_circuit1, isa_circuit2], shots=128)
# Sample two circuits at different amounts of shots. The "None"s are necessary
# as placeholders
# for the lack of parameter values in this example.
sampler.run([(isa_circuit1, None, 123), (isa_circuit2, None, 456)])
<qiskit.primitives.primitive_job.PrimitiveJob at 0x7fa430e39dd0>
Ein vollständiges Beispiel findest du auf der Seite Primitives-Beispiele.
Nächste Schritte
- Für eine leistungsstärkere Simulation, die größere Circuits verarbeiten kann oder Rauschmodelle einbezieht, siehe Exakte und verrauschte Simulation mit Qiskit Aer Primitives.
- Wie du Quantum Composer für Simulationen nutzt, erfährst du im IBM Quantum Composer-Leitfaden.
- Lies die Referenz zur Qiskit Estimator API.
- Lies die Referenz zur Qiskit Sampler API.
- Lies Migration zu V2 Primitives.