Exakte Simulation mit Primitiven der „ Qiskit SDK “
Der Code auf dieser Seite wurde unter Berücksichtigung der folgenden Anforderungen entwickelt. Wir empfehlen die Verwendung dieser oder neuerer Versionen.
qiskit[all]~=2.5.2
Die Referenzprimitive im „ Qiskit SDK “ führen lokale Zustandsvektor-Simulationen durch. Diese Simulationen unterstützen zwar keine Modellierung von Geräuschrauschen der Geräte, eignen sich jedoch gut für die schnelle Prototypenerstellung von Algorithmen, bevor man sich mit fortgeschritteneren Simulationstechniken ( unter Verwendung von Qiskit Aer ) befasst oder die Algorithmen auf echten Geräten ausführt ( mit IBM Quantum -Primitiven ).
Das Estimator-Primitiv kann die Erwartungswerte von Schaltkreisen berechnen, und das Sampler-Primitiv kann Stichproben von Ausgangsverteilungen von Schaltkreisen.
In den folgenden Abschnitten wird gezeigt, wie Sie die Referenzprimitive verwenden, um Ihren Workflow lokal auszuführen.
Verwenden Sie den Referenzschätzer
Die Referenzimplementierung von EstimatorV2 in qiskit.primitives , die auf einem lokalen Zustandsvektor läuft simulatoren läuft, ist die StatevectorEstimator klasse. Sie kann Schaltkreise, Beobachtungswerte und Parameter als Eingaben annehmen 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
Observables ist qiskit.quantum_info.SparsePauliOp. Beachten Sie, dass
die Schaltung im Beispiel parametrisiert ist, Sie Estimator jedoch auch auf nicht parametrisierten Schaltungen ausführen können.
Jede Schaltung, die an einen Estimator weitergegeben wird, darf keine Messungen enthalten.
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")Output:
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 „ IBM Quantum “-Primitives-Workflow erfordert, dass Schaltkreise und Observables so umgewandelt werden, dass sie ausschließlich von der QPU unterstützte Befehle verwenden (diese werden als „Instruction Set Architecture“ (ISA) -Schaltkreise und -Observables bezeichnet). Die Referenzprimitive akzeptieren nach wie vor abstrakte Befehle, da sie auf Simulationen mit lokalen Zustandsvektoren basieren; dennoch könnte das Transpiling der Schaltung im Hinblick auf die Schaltungsoptimierung von Vorteil 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)Initialisieren Sie den Schätzer
A instanziieren qiskit.primitives.StatevectorEstimator.
from qiskit.primitives import StatevectorEstimator
estimator = StatevectorEstimator()Laufen und Ergebnisse erzielen
Dieses Beispiel verwendet nur einen Stromkreis (vom Typ QuantumCircuit) und eine beobachtbar.
Führen Sie die Schätzung aus, indem Sie die StatevectorEstimator.run methode, die eine Instanz eines PrimitiveJob objekts zurückgibt. Sie können die Ergebnisse des Auftrags (als qiskit.primitives.PrimitiveResult objekt) mit der qiskit.primitives.PrimitiveJob.result methode.
job = estimator.run([(circuit, observable, parameter_values)])
result = job.result()
print(f" > Result class: {type(result)}")Output:
> Result class: <class 'qiskit.primitives.containers.primitive_result.PrimitiveResult'>
Den erwarteten Wert aus dem Ergebnis ermitteln
Das Ergebnis der Primitive gibt ein Array von PubResult Objekten aus, wobei jedes Element des Arrays ein PubResult Objekt ist, das in seinen Daten das Array der Auswertungen enthält, die jeder Kombination von Schaltkreisen und Beobachtungsgrößen in der PUB entsprechen.
Um die Erwartungswerte und Metadaten für die erste (und in diesem Fall einzige) Schaltkreisauswertung abzurufen, müssen wir auf die Auswertung zugreifen data für PUB 0 zugreifen:
print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")Output:
> Expectation value: [4. 3.73205081 2. ]
> Metadata: {'target_precision': 0.0, 'circuit_metadata': {}}
Optionen für die Ausführung des Schätzer-Sets festlegen
Standardmäßig führt der Reference Estimator eine exakte Zustandsvektorberechnung auf der Grundlage des quantum_info.Statevector klasse.
Dies kann jedoch geändert werden, um den Effekt des Sampling-Overheads (auch bekannt als "Shot Noise") einzuführen.
Estimator akzeptiert ein precision Argument, das die Fehlerbalken ausdrückt, die die primitive Implementierung für Erwartungswertschätzungen anstreben sollte. Dies ist der Stichproben-Overhead und wird ausschließlich in der Methode .run() definiert. Auf diese Weise können Sie die Option bis hin zur Ebene PUB feineinstellen.
# 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 finden Sie auf der Seite mit den Estimator-Beispielen.
Verwenden Sie den Referenz-Sampler
Die Referenzimplementierung von SamplerV2 in qiskit.primitives ist die StatevectorSampler klasse. Es nimmt Schaltkreise und Parameter als Eingaben und liefert die Ergebnisse der Stichproben aus den Wahrscheinlichkeitsverteilungen der Ausgänge als Quasi-Wahrscheinlichkeitsverteilung der Ausgangszustände.
Der folgende Code bereitet die Eingaben vor, die in den nachfolgenden Beispielen verwendet werden. Beachten Sie, dass diese Beispiele eine einzelne parametrisierte Schaltung ausführen, Sie Sampler jedoch auch auf nicht-parametrisierten Schaltungen ausführen können.
from qiskit import QuantumCircuit
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw("mpl", style="iqp")Output:
Jeder Quantenkreis, der an einen Sampler übergeben wird , muss Messungen enthalten.
Der „ IBM Quantum “-Primitives-Workflow erfordert, dass Schaltkreise so umgewandelt werden, dass sie ausschließlich von der QPU unterstützte Befehle verwenden (sogenannte ISA-Schaltkreise). Die Referenzprimitive akzeptieren nach wie vor abstrakte Befehle, da sie auf Simulationen mit lokalen Zustandsvektoren basieren; dennoch könnte das Transpiling der Schaltung im Hinblick auf die Schaltungsoptimierung von Vorteil 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)Initialisieren SamplerV2
Instanziieren qiskit.primitives.StatevectorSampler:
from qiskit.primitives import StatevectorSampler
sampler = StatevectorSampler()Laufen und Ergebnisse erzielen
# execute 1 circuit with Sampler
job = sampler.run([circuit])
pub_result = job.result()[0]
print(f" > Result class: {type(pub_result)}")Output:
> Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>
Primitive akzeptieren mehrere PUBs als Eingaben, und jedes PUB erhält sein eigenes Ergebnis. Daher können Sie verschiedene Schaltkreise mit unterschiedlichen Kombinationen von Parametern/Beobachtungswerten ausführen und die Ergebnisse von PUB 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)}")Output:
> Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>
Die Wahrscheinlichkeitsverteilung oder das Messergebnis ermitteln
Stichproben von Messergebnissen werden als Bitstrings oder Zählungen zurückgegeben. Die Bitstrings zeigen die Messergebnisse unter Beibehaltung der Schussreihenfolge, in der sie gemessen wurden. Die Sampler-Ergebnisobjekte organisieren die Daten in Form der klassischen Registernamen ihrer Eingangsschaltungen, um mit dynamischen Schaltungen kompatibel zu sein.
Der Name des klassischen Registers lautet standardmäßig "meas". Dieser Name wird später für den Zugriff auf die Messungs-Bitstrings verwendet.
# Define quantum circuit with 2 qubits
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw()Output:
┌───┐ ░ ┌─┐
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}")Output:
The number of bitstrings is: 1024
The counts are: {'00': 513, '11': 511}
Ausführungsoptionen ändern
Standardmäßig führt der Referenz-Sampler eine exakte Zustandsvektorberechnung auf der Grundlage des quantum_info.Statevector klasse.
Dies kann jedoch geändert werden, um den Effekt des Sampling-Overheads (auch bekannt als "Shot Noise") einzuführen. Um diesen Overhead zu verwalten, akzeptiert die Sampler-Schnittstelle ein shots Argument, das auf der PUB Ebene definiert werden kann.
In diesem Beispiel wird davon ausgegangen, dass Sie zwei Stromkreise definiert haben.
# 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)])Output:
<qiskit.primitives.primitive_job.PrimitiveJob at 0x1297391d0>
Ein vollständiges Beispiel finden Sie auf der Seite mit den Sampler-Beispielen.
Nächste Schritte
- Für eine leistungsstärkere Simulation, die größere Schaltungen verarbeiten kann, oder um Rauschmodelle in Ihre Simulation einzubeziehen, siehe Exakte und verrauschte Simulation mit Qiskit Aer-Primitiven.
- Wie Sie Quantum Composer für die Simulation verwenden, erfahren Sie in der Anleitung IBM Quantum Composer.
- Lesen Sie die Qiskit Estimator API-Referenz.
- Lesen Sie die Qiskit Sampler API-Referenz.