Einführung in Primitives
Paketversionen
Der Code auf dieser Seite wurde mit den folgenden Anforderungen entwickelt. Wir empfehlen, diese oder neuere Versionen zu verwenden.
qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
Die Beta-Version eines neuen Ausführungsmodells ist jetzt verfügbar. Das gerichtete Ausführungsmodell bietet mehr Flexibilität bei der Anpassung deines Fehlerminderungs-Workflows. Weitere Informationen findest du im Leitfaden Gerichtetes Ausführungsmodell.
Warum hat Qiskit Primitives eingeführt?
Ähnlich wie in den Anfängen klassischer Computer, als Entwickler CPU-Register direkt manipulieren mussten,
lieferte das frühe Interface zu QPUs schlicht die Rohdaten der Steuerelektronik zurück.
Das war kein großes Problem, solange QPUs in Laboren betrieben wurden und nur Forschern direkten Zugang gewährten.
Da die meisten Entwickler mit der Aufbereitung solcher Rohdaten zu 0en und 1en weder vertraut sein würden noch sein sollten,
führte Qiskit backend.run ein – eine erste Abstraktionsschicht für den Cloud-Zugriff auf QPUs. Dadurch konnten Entwickler
mit einem vertrauten Datenformat arbeiten und sich auf das Wesentliche konzentrieren.
Als der Zugang zu QPUs weiter verbreitet wurde und immer mehr Quantenalgorithmen entwickelt wurden,
entstand erneut der Bedarf nach einer übergeordneten Abstraktionsebene. Als Antwort darauf führte Qiskit
das Primitives-Interface ein, das für zwei zentrale Aufgaben in der Quantenalgorithmusentwicklung optimiert ist:
die Schätzung von Erwartungswerten (Estimator) und das Sampling von Circuits (Sampler). Das Ziel ist es einmal
mehr, Entwicklern zu helfen, sich stärker auf Innovation und weniger auf Datenkonvertierung zu konzentrieren. Das Primitives-Interface löst das backend.run-Interface ab, da Sampler denselben direkten Hardware-Zugriff bietet, den backend.run bereitgestellt hat.
Was ist ein Primitive?
Computersysteme bauen auf mehreren Abstraktionsschichten auf. Abstraktionen ermöglichen es dir, dich auf eine bestimmte Detailtiefe zu konzentrieren, die für die jeweilige Aufgabe relevant ist. Je näher du der Hardware kommst, desto niedrigere Abstraktionsebenen benötigst du (zum Beispiel musst du möglicherweise Daten auf der Ebene von CPU-Instruktionen verschieben oder manipulieren). Je komplexer die Aufgabe, die du ausführen möchtest, desto höher sind die erforderlichen Abstraktionsebenen (zum Beispiel könnte eine Programmbibliothek für algebraische Berechnungen zum Einsatz kommen).
In diesem Kontext ist ein Primitive die kleinste Verarbeitungseinheit – der einfachste Baustein, aus dem sich auf einer bestimmten Abstraktionsebene etwas Nützliches zusammensetzen lässt.
Die jüngsten Fortschritte im Quantencomputing haben den Bedarf erhöht, auf höheren Abstraktionsebenen zu arbeiten. Da sich das Feld in Richtung größerer Quantum Processing Units (QPUs) und komplexerer Workflows bewegt, verlagert sich der Fokus vom Interagieren mit einzelnen Qubit-Signalen hin zur Betrachtung von Quantengeräten als Systeme, die notwendige Aufgaben erfüllen.
Die zwei häufigsten Aufgaben für Quantencomputer sind das Sampling von Quantenzuständen und die Berechnung von Erwartungswerten. Diese Aufgaben haben das Design der Qiskit Primitives motiviert: Estimator und Sampler.
- Estimator berechnet Erwartungswerte von Observablen bezüglich Zuständen, die von Quantum Circuits präpariert wurden.
- Sampler sampelt das Ausgaberegister aus der Ausführung von Quantum Circuits.
Kurz gesagt: Das durch die Qiskit Primitives eingeführte Berechnungsmodell bringt die Quantenprogrammierung einen Schritt näher an den heutigen Stand der klassischen Programmierung, bei der der Fokus weniger auf den Hardware-Details und mehr auf den angestrebten Ergebnissen liegt.
Definition und Implementierungen von Primitives
Es gibt zwei Arten von Qiskit Primitives: die Basisklassen und deren Implementierungen. Die Qiskit Primitives werden durch Open-Source-Basis-Klassen für Primitives definiert, die im Qiskit SDK (im Modul qiskit.primitives) enthalten sind. Provider (wie Qiskit Runtime) können diese Basisklassen nutzen, um eigene Sampler- und Estimator-Implementierungen abzuleiten. Die meisten Nutzer werden mit Provider-Implementierungen interagieren, nicht mit den Basis-Primitives.
Basisklassen
BaseEstimatorV2 und BaseSamplerV2 – abstrakte Basisklassen, die ein gemeinsames Interface für die Implementierung von Primitives definieren. Alle anderen Klassen im Modul qiskit.primitives erben von diesen Basisklassen. Entwickler sollten diese Klassen verwenden, wenn sie ihr eigenes auf Primitives basierendes Ausführungsmodell für einen bestimmten Provider erstellen möchten. Diese Klassen können auch für diejenigen nützlich sein, die hochgradig angepasste Verarbeitungen vornehmen wollen und die vorhandenen Primitives-Implementierungen für ihre Bedürfnisse als zu einfach empfinden. Allgemeine Nutzer werden die Basisklassen nicht direkt verwenden.
Implementierungen
Dies sind Implementierungen der Primitives-Basisklassen:
-
Die Qiskit Runtime Primitives (
EstimatorV2undSamplerV2) bieten eine ausgefeiltere Implementierung (beispielsweise durch Fehlerminderung) als Cloud-basierter Dienst. Diese Implementierung der Basis-Primitives wird für den Zugriff auf IBM Quantum®-Hardware verwendet. Der Zugriff erfolgt über IBM Qiskit Runtime. -
StatevectorEstimatorundStatevectorSampler– Referenzimplementierungen der Primitives, die den in Qiskit integrierten Simulator verwenden. Sie sind mit dem Qiskit-Modulquantum_infoaufgebaut und liefern Ergebnisse auf Basis idealer Zustandsvektor-Simulationen. Der Zugriff erfolgt über Qiskit. -
BackendEstimatorV2undBackendSamplerV2– Mit diesen Klassen kannst du beliebige Quantencomputing-Ressourcen in ein Primitive „einwickeln". So kannst du Primitive-style Code für Provider schreiben, die noch kein auf Primitives basierendes Interface besitzen. Diese Klassen lassen sich genauso verwenden wie der reguläre Sampler und Estimator, müssen jedoch mit einem zusätzlichenbackend-Argument initialisiert werden, um den gewünschten Quantencomputer auszuwählen. Der Zugriff erfolgt über Qiskit.
Vorteile der Qiskit Primitives
Mit Primitives können Qiskit-Nutzer Quantencode für eine bestimmte QPU schreiben, ohne jeden Detail explizit
verwalten zu müssen. Außerdem kannst du durch die zusätzliche Abstraktionsschicht möglicherweise einfacher
auf erweiterte Hardware-Funktionen eines bestimmten Providers zugreifen. Mit den Qiskit Runtime Primitives
kannst du beispielsweise von den neuesten Fortschritten in der Fehlerminderung und -unterdrückung profitieren, indem du Optionen wie das resilience_level des Primitives anpasst, anstatt eigene Implementierungen dieser Techniken zu erstellen.
Für Hardware-Provider bedeutet die native Implementierung von Primitives, dass du deinen Nutzern einen direkteren Zugang zu deinen Hardware-Funktionen wie erweiterten Nachverarbeitungstechniken ermöglichst. Es wird dadurch einfacher für deine Nutzer, von den besten Fähigkeiten deiner Hardware zu profitieren.
Details zu Primitives
Wie bereits beschrieben, werden alle Primitives aus den Basisklassen erstellt und haben daher die gleiche allgemeine Struktur und Verwendung. Zum Beispiel ist das Eingabeformat für alle Estimator-Primitives identisch. Es gibt jedoch Unterschiede in den Implementierungen, die sie einzigartig machen.
Da die meisten Nutzer auf die Qiskit Runtime Primitives zugreifen, basieren die Beispiele im restlichen Teil dieses Abschnitts auf Qiskit Runtime Primitives.
Estimator
Das Estimator-Primitive berechnet die Erwartungswerte für eine oder mehrere Observablen bezüglich Zuständen, die von Quantum Circuits präpariert wurden. Die Circuits können parametrisiert sein, solange die Parameterwerte ebenfalls als Eingabe für das Primitive bereitgestellt werden.
Die Eingabe ist ein Array von PUBs. Jedes PUB hat das Format:
(<einzelner Circuit>, <eine oder mehrere Observablen>, <optionale Parameterwerte>, <optionale Präzision>),
wobei die optionalen parameter values eine Liste oder ein einzelner Parameter sein können. Verschiedene Estimator-Implementierungen unterstützen verschiedene Konfigurationsoptionen. Enthält die Eingabe Messungen, werden diese ignoriert.
Die Ausgabe ist ein PubResult, das die berechneten Erwartungswerte pro Paar sowie deren Standardfehler im PubResult-Format enthält. Jedes PubResult enthält sowohl Daten als auch Metadaten.
Der Estimator kombiniert Elemente aus Observablen und Parameterwerten gemäß den NumPy-Broadcasting-Regeln, wie im Thema Primitive-Eingaben und -Ausgaben beschrieben.
Beispiel:
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime
# This cell is hidden from users, it creates the circuits and observables to run
from qiskit_ibm_runtime import EstimatorV2, SamplerV2, QiskitRuntimeService
from qiskit.circuit.random import random_circuit
from qiskit.circuit import Parameter
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler import generate_preset_pass_manager
import numpy as np
service = QiskitRuntimeService()
backend = service.least_busy()
phi = Parameter("phi")
circuit1 = random_circuit(10, 5, seed=12345)
circuit1.rzz(phi, 1, 2)
observable1 = SparsePauliOp.from_sparse_list(
[("ZXYZ", [1, 2, 3, 4], 1)], num_qubits=10
)
param_values1 = np.random.uniform(size=5).T
circuit2 = random_circuit(10, 5, seed=12345)
circuit2.rzz(phi, 1, 2)
observable2 = SparsePauliOp.from_sparse_list(
[("XZYX", [1, 2, 3, 4], 1)], num_qubits=10
)
param_values2 = np.random.uniform(size=5).T
shots1 = 164
shots2 = 1024
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
circuit1 = pm.run(circuit1)
circuit2 = pm.run(circuit2)
observable1 = observable1.apply_layout(circuit1.layout)
observable2 = observable2.apply_layout(circuit2.layout)
estimator = EstimatorV2(mode=backend)
estimator_job = estimator.run(
[
(circuit1, observable1, param_values1),
(circuit2, observable2, param_values2),
]
)
Sampler
Die Kernaufgabe des Samplers ist das Sampling des Ausgaberegisters aus der Ausführung eines oder mehrerer Quantum Circuits. Die Eingabe-Circuits können parametrisiert sein, solange die Parameterwerte ebenfalls als Eingabe für das Primitive bereitgestellt werden.
Die Eingabe sind ein oder mehrere PUBs, im Format:
(<einzelner Circuit>, <ein oder mehrere optionale Parameterwerte>, <optionale Shots>),
wobei es mehrere parameter values-Elemente geben kann und jedes Element je nach gewähltem Circuit entweder ein Array oder ein einzelner Parameter sein kann. Außerdem muss die Eingabe Messungen enthalten.
Die Ausgabe sind Counts oder Shot-by-Shot-Messungen als PubResult-Objekte, ohne Gewichtung. Die Ergebnisklasse hat jedoch Methoden, um gewichtete Samples wie Counts zurückzugeben. Vollständige Details findest du unter Primitive-Eingaben und -Ausgaben.
Beispiel:
# This cell is hidden from users, add measurement instructions to circuits
circuit1.measure_active()
circuit2.measure_active()
sampler = SamplerV2(mode=backend)
sampler_job = sampler.run(
[
(circuit1, param_values1, shots1),
(circuit2, param_values2, shots2),
]
)
Nächste Schritte
- Lies Erste Schritte mit Primitives, um Primitives in deiner Arbeit einzusetzen.
- Schau dir detaillierte Primitives-Beispiele an.
- Übe mit Primitives, indem du die Lektion zur Kostenfunktion in IBM Quantum Learning durcharbeitest.
- Sieh dir die EstimatorV2-API-Referenz und die SamplerV2-API-Referenz an.
- Lies Migration zu V2-Primitives.