Entrées et sorties des primitives
La version bêta d'un nouveau modèle d'exécution est maintenant disponible. Le modèle d'exécution dirigée offre plus de flexibilité pour personnaliser ton flux de travail d'atténuation des erreurs. Consulte le guide du Modèle d'exécution dirigée pour plus d'informations.
Versions des paquets
Le code de cette page a été développé avec les prérequis suivants. Nous recommandons d'utiliser ces versions ou des versions plus récentes.
qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
Cette page donne un aperçu des entrées et sorties des primitives de Qiskit Runtime qui exécutent des charges de travail sur les ressources de calcul d'IBM Quantum®. Ces primitives te permettent de définir des charges de travail vectorisées de manière efficace en utilisant une structure de données appelée Primitive Unified Bloc (PUB). Ces PUBs sont l'unité fondamentale de travail dont un QPU a besoin pour exécuter ces charges. Ils sont utilisés comme entrées de la méthode run() des primitives Sampler et Estimator, qui exécutent la charge de travail définie comme un job. Ensuite, une fois le job terminé, les résultats sont retournés dans un format qui dépend à la fois des PUBs utilisés et des options d'exécution spécifiées par les primitives Sampler ou Estimator.
Aperçu des PUBs
Lors de l'invocation de la méthode run() d'une primitive, l'argument principal requis est une list d'un ou plusieurs tuples — un pour chaque circuit exécuté par la primitive. Chacun de ces tuples est considéré comme un PUB, et les éléments requis de chaque tuple dans la liste dépendent de la primitive utilisée. Les données fournies à ces tuples peuvent également être organisées sous différentes formes pour offrir de la flexibilité à une charge de travail via le broadcasting — dont les règles sont décrites dans une section ultérieure.
PUB de l'Estimator
Pour la primitive Estimator, le format du PUB doit contenir au maximum quatre valeurs :
- Un seul
QuantumCircuit, qui peut contenir un ou plusieurs objetsParameter - Une liste d'un ou plusieurs observables qui spécifient les valeurs d'espérance à estimer, organisés en un tableau (par exemple, un seul observable représenté comme un tableau de dimension 0, une liste d'observables comme un tableau de dimension 1, etc.). Les données peuvent être dans n'importe quel format
ObservablesArrayLikecommePauli,SparsePauliOp,PauliListoustr.remarqueSi tu as deux observables qui commutent dans des PUBs différents mais avec le même circuit, ils ne seront pas estimés en utilisant la même mesure. Chaque PUB représente une base de mesure différente et, par conséquent, des mesures séparées sont nécessaires pour chaque PUB. Pour garantir que les observables qui commutent soient estimés en utilisant la même mesure, ils doivent être regroupés dans le même PUB.
- Une collection de valeurs de paramètres à lier au circuit. Cela peut être spécifié comme un seul objet de type tableau où le dernier indice correspond aux objets
Parameterdu circuit, ou omis (ou de manière équivalente, défini commeNone) si le circuit n'a pas d'objetsParameter. - (Optionnellement) une précision cible pour les valeurs d'espérance à estimer
PUB du Sampler
Pour la primitive Sampler, le format du tuple PUB contient au maximum trois valeurs :
- Un seul
QuantumCircuit, qui peut contenir un ou plusieurs objetsParameterNote : ces circuits doivent également inclure des instructions de mesure pour chacun des qubits à échantillonner. - Une collection de valeurs de paramètres à lier au circuit (nécessaire uniquement si des objets
Parametersont utilisés et doivent être liés au moment de l'exécution) - (Optionnellement) un nombre de shots avec lequel mesurer le circuit
Le code suivant montre un exemple d'entrées vectorisées pour la primitive Estimator et les exécute sur un backend IBM® en tant qu'objet RuntimeJobV2 unique.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime
from qiskit.circuit import (
Parameter,
QuantumCircuit,
ClassicalRegister,
QuantumRegister,
)
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp
from qiskit.primitives.containers import BitArray
from qiskit_ibm_runtime import (
QiskitRuntimeService,
EstimatorV2 as Estimator,
SamplerV2 as Sampler,
)
import numpy as np
# Instantiate runtime service and get
# the least busy backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Define a circuit with two parameters.
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.ry(Parameter("a"), 0)
circuit.rz(Parameter("b"), 0)
circuit.cx(0, 1)
circuit.h(0)
# Transpile the circuit
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
transpiled_circuit = pm.run(circuit)
layout = transpiled_circuit.layout
# Now define a sweep over parameter values, the last axis of dimension 2 is
# for the two parameters "a" and "b"
params = np.vstack(
[
np.linspace(-np.pi, np.pi, 100),
np.linspace(-4 * np.pi, 4 * np.pi, 100),
]
).T
# Define three observables. The inner length-1 lists cause this array of
# observables to have shape (3, 1), rather than shape (3,) if they were
# omitted.
observables = [
[SparsePauliOp(["XX", "IY"], [0.5, 0.5])],
[SparsePauliOp("XX")],
[SparsePauliOp("IY")],
]
# Apply the same layout as the transpiled circuit.
observables = [
[observable.apply_layout(layout) for observable in observable_set]
for observable_set in observables
]
# Estimate the expectation value for all 300 combinations of observables
# and parameter values, where the pub result will have shape (3, 100).
#
# This shape is due to our array of parameter bindings having shape
# (100, 2), combined with our array of observables having shape (3, 1).
estimator_pub = (transpiled_circuit, observables, params)
# Instantiate the new estimator object, then run the transpiled circuit
# using the set of parameters and observables.
estimator = Estimator(mode=backend)
job = estimator.run([estimator_pub])
result = job.result()
Règles de broadcasting
Les PUBs agrègent des éléments de plusieurs tableaux (observables et valeurs de paramètres) en suivant les mêmes règles de broadcasting que NumPy. Cette section résume brièvement ces règles. Pour une explication détaillée, consulte la documentation des règles de broadcasting de NumPy.
Règles :
- Les tableaux d'entrée n'ont pas besoin d'avoir le même nombre de dimensions.
- Le tableau résultant aura le même nombre de dimensions que le tableau d'entrée avec le plus grand nombre de dimensions.
- La taille de chaque dimension est la taille la plus grande de la dimension correspondante.
- Les dimensions manquantes sont supposées avoir une taille de un.
- Les comparaisons de forme commencent par la dimension la plus à droite et continuent vers la gauche.
- Deux dimensions sont compatibles si leurs tailles sont égales ou si l'une d'entre elles est 1.
Exemples de paires de tableaux compatibles avec le broadcasting :
A1 (1d array): 1
A2 (2d array): 3 x 5
Result (2d array): 3 x 5
A1 (3d array): 11 x 2 x 7
A2 (3d array): 11 x 1 x 7
Result (3d array): 11 x 2 x 7
Exemples de paires de tableaux non compatibles avec le broadcasting :
A1 (1d array): 5
A2 (1d array): 3
A1 (2d array): 2 x 1
A2 (3d array): 6 x 5 x 4 # This would work if the middle dimension were 2, but it is 5.
EstimatorV2 retourne une estimation de la valeur d'espérance pour chaque élément de la forme résultant du broadcasting.
Voici quelques exemples de motifs courants exprimés en termes de broadcasting de tableaux. Leur représentation visuelle correspondante est montrée dans la figure suivante :
Les ensembles de valeurs de paramètres sont représentés comme des tableaux de n x m, et les tableaux d'observables sont représentés comme un ou plusieurs tableaux à une seule colonne. Pour chaque exemple dans le code précédent, les ensembles de valeurs de paramètres sont combinés avec leur tableau d'observables pour créer les estimations de valeurs d'espérance résultantes.
-
Exemple 1 : (broadcast d'un seul observable) possède un ensemble de valeurs de paramètres qui est un tableau de 5x1 et un tableau d'observables de 1x1. L'unique élément du tableau d'observables est combiné avec chaque élément de l'ensemble de valeurs de paramètres pour créer un seul tableau de 5x1 où chaque élément est une combinaison de l'élément original dans l'ensemble de valeurs de paramètres avec l'élément du tableau d'observables.
-
Exemple 2 : (zip) possède un ensemble de valeurs de paramètres de 5x1 et un tableau d'observables de 5x1. La sortie est un tableau de 5x1 où chaque élément est une combinaison du n-ième élément de l'ensemble de valeurs de paramètres avec le n-ième élément du tableau d'observables.
-
Exemple 3 : (produit extérieur) possède un ensemble de valeurs de paramètres de 1x6 et un tableau d'observables de 4x1. Leur combinaison produit un tableau de 4x6 créé en combinant chaque élément de l'ensemble de valeurs de paramètres avec chaque élément du tableau d'observables, de sorte que chaque valeur de paramètre devient une colonne complète dans la sortie.
-
Exemple 4 : (généralisation nd standard) possède un tableau de valeurs de paramètres de 3x6 et deux tableaux d'observables de 3x1. Ceux-ci sont combinés pour créer deux tableaux de sortie de 3x6 de manière similaire à l'exemple précédent.
# Broadcast single observable
parameter_values = np.random.uniform(size=(5,)) # shape (5,)
observables = SparsePauliOp("ZZZ") # shape ()
# >> pub result has shape (5,)
# Zip
parameter_values = np.random.uniform(size=(5,)) # shape (5,)
observables = [
SparsePauliOp(pauli) for pauli in ["III", "XXX", "YYY", "ZZZ", "XYZ"]
] # shape (5,)
# >> pub result has shape (5,)
# Outer/Product
parameter_values = np.random.uniform(size=(1, 6)) # shape (1, 6)
observables = [
[SparsePauliOp(pauli)] for pauli in ["III", "XXX", "YYY", "ZZZ"]
] # shape (4, 1)
# >> pub result has shape (4, 6)
# Standard nd generalization
parameter_values = np.random.uniform(size=(3, 6)) # shape (3, 6)
observables = [
[
[SparsePauliOp(["XII"])],
[SparsePauliOp(["IXI"])],
[SparsePauliOp(["IIX"])],
],
[
[SparsePauliOp(["ZII"])],
[SparsePauliOp(["IZI"])],
[SparsePauliOp(["IIZ"])],
],
] # shape (2, 3, 1)
# >> pub result has shape (2, 3, 6)
Chaque SparsePauliOp compte comme un seul élément dans ce contexte, indépendamment du nombre de Paulis que contient le SparsePauliOp. Par conséquent, pour les besoins de ces règles de broadcasting, tous les éléments suivants ont la même forme :
a = SparsePauliOp("Z") # shape ()
b = SparsePauliOp("IIIIZXYIZ") # shape ()
c = SparsePauliOp.from_list(["XX", "XY", "IZ"]) # shape ()
Les listes d'opérateurs suivantes, bien qu'équivalentes en termes d'information contenue, ont des formes différentes :
list1 = SparsePauliOp.from_list(["XX", "XY", "IZ"]) # shape ()
list2 = [SparsePauliOp("XX"), SparsePauliOp("XY"), SparsePauliOp("IZ")] # shape (3, )
Aperçu des sorties des primitives
Une fois qu'un ou plusieurs PUBs sont envoyés à un QPU pour exécution et que le job se termine avec succès, les données sont retournées sous forme d'objet conteneur PrimitiveResult auquel on accède en appelant la méthode RuntimeJobV2.result(). Le PrimitiveResult contient une liste itérable d'objets PubResult qui contiennent les résultats d'exécution de chaque PUB. Selon la primitive utilisée, ces données seront des valeurs d'espérance avec leurs barres d'erreur dans le cas de l'Estimator, ou des échantillons de la sortie du circuit dans le cas du Sampler.
Chaque élément de cette liste correspond à chaque PUB envoyé à la méthode run() de la primitive (par exemple, un job envoyé avec 20 PUBs retournera un objet PrimitiveResult contenant une liste de 20 PubResults, un pour chaque PUB).
Chacun de ces objets PubResult possède à la fois un attribut data et un attribut metadata. L'attribut data est un DataBin personnalisé qui contient les valeurs de mesure réelles, les écarts-types, etc. Ce DataBin a différents attributs selon la forme ou la structure du PUB associé, ainsi que les options d'atténuation des erreurs spécifiées par la primitive utilisée pour envoyer le job (par exemple, ZNE ou PEC). L'attribut metadata contient quant à lui des informations sur le temps d'exécution et les options d'atténuation des erreurs utilisées (expliqué plus loin dans la section Métadonnées des résultats de cette page).
Voici un schéma visuel de la structure de données de PrimitiveResult :
- Sortie de l'Estimator
- Sortie du Sampler
└── PrimitiveResult
├── PubResult[0]
│ ├── metadata
│ └── data ## In the form of a DataBin object
│ ├── evs
│ │ └── List of estimated expectation values in the shape
| | specified by the first pub
│ └── stds
│ └── List of calculated standard deviations in the
| same shape as above
├── PubResult[1]
| ├── metadata
| └── data ## In the form of a DataBin object
| ├── evs
| │ └── List of estimated expectation values in the shape
| | specified by the second pub
| └── stds
| └── List of calculated standard deviations in the
| same shape as above
├── ...
├── ...
└── ...
└── PrimitiveResult
├── PubResult[0]
│ ├── metadata
│ └── data ## In the form of a DataBin object
│ ├── NAME_OF_CLASSICAL_REGISTER
│ │ └── BitArray of count data (default is 'meas')
| |
│ └── NAME_OF_ANOTHER_CLASSICAL_REGISTER
│ └── BitArray of count data (exists only if more than one
| ClassicalRegister was specified in the circuit)
├── PubResult[1]
| ├── metadata
| └── data ## In the form of a DataBin object
| └── NAME_OF_CLASSICAL_REGISTER
| └── BitArray of count data for second pub
├── ...
├── ...
└── ...
En résumé, un seul job retourne un objet PrimitiveResult qui contient une liste d'un ou plusieurs objets PubResult. Ces objets PubResult stockent les données de mesure de chaque PUB envoyé au job.
Chaque PubResult possède des formats et attributs différents selon le type de primitive utilisée pour le job. Les détails sont expliqués ci-dessous.
Sortie de l'Estimator
Chaque PubResult de la primitive Estimator contient au moins un tableau de valeurs d'espérance (PubResult.data.evs) et les écarts-types associés (soit PubResult.data.stds soit PubResult.data.ensemble_standard_error selon le resilience_level utilisé), mais peut contenir plus de données selon les options d'atténuation des erreurs spécifiées.
Le fragment de code suivant décrit le format de PrimitiveResult (et le PubResult associé) pour le job créé précédemment.
print(
f"The result of the submitted job had {len(result)} PUB and has a value:\n {result}\n"
)
print(
f"The associated PubResult of this job has the following data bins:\n {result[0].data}\n"
)
print(f"And this DataBin has attributes: {result[0].data.keys()}")
print(
"Recall that this shape is due to our array of parameter binding sets having shape (100, 2) -- where 2 is the\n\
number of parameters in the circuit -- combined with our array of observables having shape (3, 1). \n"
)
print(
f"The expectation values measured from this PUB are: \n{result[0].data.evs}"
)
The result of the submitted job had 1 PUB and has a value:
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(3, 100), dtype=float64>), stds=np.ndarray(<shape=(3, 100), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(3, 100), dtype=float64>), shape=(3, 100)), metadata={'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False}, 'version': 2})
The associated PubResult of this job has the following data bins:
DataBin(evs=np.ndarray(<shape=(3, 100), dtype=float64>), stds=np.ndarray(<shape=(3, 100), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(3, 100), dtype=float64>), shape=(3, 100))
And this DataBin has attributes: dict_keys(['evs', 'stds', 'ensemble_standard_error'])
Recall that this shape is due to our array of parameter binding sets having shape (100, 2) -- where 2 is the
number of parameters in the circuit -- combined with our array of observables having shape (3, 1).
The expectation values measured from this PUB are:
[[ 0.00948597 0.12163221 0.29100944 0.40535344 0.46625814 0.54716103
0.57690846 0.59809047 0.5784682 0.50924868 0.4579837 0.40035644
0.37174056 0.32887613 0.25850853 0.26396412 0.25852429 0.26074166
0.29282485 0.34388535 0.37368314 0.43562138 0.46912323 0.51955146
0.54430185 0.55467261 0.5162183 0.52744696 0.47261781 0.42613541
0.35400013 0.33217125 0.29600426 0.27561903 0.25307754 0.25672088
0.28783701 0.36612701 0.40433263 0.44428286 0.51028376 0.55034507
0.55979913 0.57160124 0.54127534 0.49753533 0.42942659 0.32552331
0.20215918 0.04303087 -0.08115732 -0.18473659 -0.34015892 -0.44489319
-0.49112115 -0.54588034 -0.60601287 -0.55869218 -0.53353861 -0.51628053
-0.44978534 -0.38090252 -0.32481576 -0.28832245 -0.27057547 -0.26542929
-0.27054473 -0.29367389 -0.31531828 -0.38462352 -0.40276794 -0.47168997
-0.48548191 -0.5382924 -0.52716406 -0.53277032 -0.50776933 -0.48512907
-0.44335198 -0.38756463 -0.34438156 -0.29199194 -0.2729216 -0.24602918
-0.23527174 -0.3019153 -0.35159518 -0.38303379 -0.42434541 -0.47743033
-0.54652609 -0.5877912 -0.59175701 -0.57386895 -0.56416812 -0.48022381
-0.3853372 -0.2639702 -0.12030502 0.02081148]
[ 0.00581765 0.0552677 0.15998546 0.20725389 0.25452232 0.34178711
0.39196437 0.47050268 0.50031815 0.527952 0.57231161 0.64066903
0.72429779 0.77011181 0.78174711 0.86610308 0.88646487 0.91337151
0.94245978 0.98100173 0.97372966 1.00936279 1.01881647 1.0544496
1.01954368 1.03699664 0.99845469 1.03845105 1.00936279 1.00354513
0.95409508 0.95264067 0.91264431 0.91846196 0.8355604 0.80283611
0.77956549 0.74102354 0.69520953 0.64575948 0.58976457 0.53231524
0.43996 0.3956004 0.32069812 0.27706572 0.22470684 0.16653032
0.07272066 -0.00218162 -0.05817653 -0.06253977 -0.15853104 -0.25015908
-0.28506499 -0.34251432 -0.44359604 -0.44432324 -0.53158804 -0.60285429
-0.637033 -0.67630215 -0.71266249 -0.76793019 -0.81519862 -0.86464867
-0.90173621 -0.93155168 -0.9337333 -0.98245614 -0.99627307 -1.01518044
-1.01590764 -1.04863194 -1.00499955 -1.02827016 -1.01663485 -1.0108172
-1.02317971 -0.97518407 -0.96500318 -0.94682302 -0.901009 -0.87846559
-0.79556404 -0.84937733 -0.78101991 -0.73811472 -0.65521316 -0.57667485
-0.59921825 -0.49813653 -0.44577766 -0.36505772 -0.33524225 -0.25888556
-0.21161713 -0.12289792 -0.03781474 0.00654486]
[ 0.01315429 0.18799671 0.42203343 0.603453 0.67799397 0.75253494
0.76185256 0.72567827 0.65661825 0.49054535 0.3436558 0.16004385
0.01918334 -0.11235955 -0.26473006 -0.33817484 -0.36941628 -0.39188819
-0.35681008 -0.29323102 -0.22636339 -0.13812003 -0.08057002 -0.01534667
0.06906002 0.07234859 0.03398191 0.01644286 -0.06412716 -0.15127432
-0.24609482 -0.28829816 -0.32063579 -0.3672239 -0.32940532 -0.28939435
-0.20389148 -0.00876953 0.11345574 0.24280625 0.43080296 0.5683749
0.67963826 0.74760208 0.76185256 0.71800493 0.63414634 0.48451631
0.3315977 0.08824335 -0.10413812 -0.30693341 -0.52178679 -0.6396273
-0.69717731 -0.74924637 -0.76842971 -0.67306111 -0.53548918 -0.42970677
-0.26253768 -0.08550288 0.06303097 0.19128528 0.27404768 0.33379008
0.36064675 0.34420389 0.30309674 0.2132091 0.19073719 0.07180049
0.04494382 -0.02795286 -0.04932858 -0.03727049 0.00109619 0.04055906
0.13647575 0.20005481 0.27624007 0.36283913 0.3551658 0.38640723
0.32502055 0.24554673 0.07782954 -0.02795286 -0.19347767 -0.3781858
-0.49383393 -0.67744588 -0.73773637 -0.78268019 -0.793094 -0.70156207
-0.55905728 -0.40504248 -0.20279529 0.0350781 ]]
Comment l'Estimator calcule l'erreur
En plus de l'estimation de la moyenne des observables passés dans les PUBs d'entrée (le champ evs du DataBin), l'Estimator tente également de fournir une estimation de l'erreur associée à ces valeurs d'espérance. Toutes les requêtes à l'Estimator rempliront le champ stds avec une quantité similaire à l'erreur standard de la moyenne pour chaque valeur d'espérance, mais certaines options d'atténuation des erreurs produisent des informations supplémentaires, comme ensemble_standard_error.
Considère un seul observable . En l'absence de ZNE, tu peux considérer chaque shot de l'exécution de l'Estimator comme fournissant une estimation ponctuelle de la valeur d'espérance . Si les estimations ponctuelles sont dans un vecteur Os, alors la valeur retournée dans ensemble_standard_error est équivalente à ce qui suit (où est l'écart-type de l'estimation de la valeur d'espérance et est le nombre de shots) :
qui traite tous les shots comme faisant partie d'un seul ensemble. Si tu as demandé le twirling de portes (twirling.enable_gates = True), tu peux organiser les estimations ponctuelles de en ensembles partageant un même twirl. Appelle ces ensembles d'estimations O_twirls ; il y en a num_randomizations (nombre de twirls). Alors stds est l'erreur standard de la moyenne de O_twirls, comme dans