Sari la conținutul principal

Intrări și ieșiri ale primitivelor

Model de execuție nou, acum în versiune beta

Versiunea beta a unui nou model de execuție este acum disponibilă. Modelul de execuție dirijată oferă mai multă flexibilitate atunci când îți personalizezi fluxul de lucru pentru atenuarea erorilor. Consultă ghidul Modelul de execuție dirijată pentru mai multe informații.

Versiuni de pachete

Codul de pe această pagină a fost dezvoltat folosind următoarele cerințe. Recomandăm utilizarea acestor versiuni sau a unora mai noi.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1

Această pagină oferă o prezentare generală a intrărilor și ieșirilor primitivelor Qiskit Runtime care execută sarcini de lucru pe resursele de calcul IBM Quantum®. Aceste primitive îți oferă posibilitatea de a defini în mod eficient sarcini de lucru vectorizate folosind o structură de date cunoscută sub numele de Primitive Unified Bloc (PUB). Aceste PUBuri reprezintă unitatea fundamentală de lucru de care are nevoie un QPU pentru a executa aceste sarcini de lucru. Ele sunt utilizate ca intrări pentru metoda run() a primitivelor Sampler și Estimator, care execută sarcina de lucru definită ca un job. Apoi, după ce job-ul s-a finalizat, rezultatele sunt returnate într-un format care depinde atât de PUBurile utilizate, cât și de opțiunile de runtime specificate de primitivele Sampler sau Estimator.

Prezentare generală a PUBurilor

Când apelezi metoda run() a unei primitive, principalul argument necesar este o list cu unul sau mai multe tupluri — câte unul pentru fiecare Circuit executat de primitivă. Fiecare dintre aceste tupluri este considerat un PUB, iar elementele obligatorii ale fiecărui tuplu din listă depind de primitiva utilizată. Datele furnizate acestor tupluri pot fi, de asemenea, organizate într-o varietate de forme pentru a oferi flexibilitate în cadrul unei sarcini de lucru prin broadcasting — regulile căreia sunt descrise într-o secțiune ulterioară.

Estimator PUB

Pentru primitiva Estimator, formatul PUB ar trebui să conțină cel mult patru valori:

  • Un singur QuantumCircuit, care poate conține unul sau mai multe obiecte Parameter
  • O listă cu unul sau mai multe observabile, care specifică valorile de așteptare de estimat, organizate într-un tablou (de exemplu, un singur observabil reprezentat ca un tablou 0-d, o listă de observabile ca un tablou 1-d și așa mai departe). Datele pot fi în oricare dintre formatele ObservablesArrayLike, cum ar fi Pauli, SparsePauliOp, PauliList sau str.
    notă

    Dacă ai două observabile care comută în PUBuri diferite, dar cu același Circuit, acestea nu vor fi estimate folosind aceeași măsurătoare. Fiecare PUB reprezintă o bază diferită pentru măsurătoare și, prin urmare, sunt necesare măsurători separate pentru fiecare PUB. Pentru a te asigura că observabilele care comută sunt estimate folosind aceeași măsurătoare, acestea trebuie grupate în același PUB.

  • O colecție de valori ale parametrilor pentru a lega Circuit-ul. Aceasta poate fi specificată ca un singur obiect de tip tablou în care ultimul indice este peste obiectele Parameter ale Circuit-ului, sau omisă (sau echivalent, setată la None) dacă Circuit-ul nu are obiecte Parameter.
  • (Opțional) o precizie țintă pentru valorile de așteptare de estimat

Sampler PUB

Pentru primitiva Sampler, formatul tuplului PUB conține cel mult trei valori:

  • Un singur QuantumCircuit, care poate conține unul sau mai multe obiecte Parameter Notă: Aceste Circuit-uri ar trebui să includă și instrucțiuni de măsurare pentru fiecare dintre Qubit-urile care urmează să fie eșantionate.
  • O colecție de valori ale parametrilor pentru a lega Circuit-ul împotriva θk\theta_k (necesară doar dacă sunt utilizate obiecte Parameter care trebuie legate la momentul execuției)
  • (Opțional) un număr de shots cu care să se măsoare Circuit-ul

Următorul cod demonstrează un exemplu de set de intrări vectorizate pentru primitiva Estimator și le execută pe un Backend IBM® ca un singur obiect RuntimeJobV2 .

# 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()

Reguli de broadcasting

PUB-urile agregează elemente din mai multe array-uri (observabile și valori de parametri) urmând aceleași reguli de broadcasting ca NumPy. Această secțiune rezumă pe scurt acele reguli. Pentru o explicație detaliată, consultă documentația NumPy privind regulile de broadcasting.

Reguli:

  • Array-urile de intrare nu trebuie să aibă același număr de dimensiuni.
    • Array-ul rezultat va avea același număr de dimensiuni ca array-ul de intrare cu cea mai mare dimensiune.
    • Dimensiunea fiecărei dimensiuni este cea mai mare dimensiune corespunzătoare.
    • Dimensiunile lipsă sunt considerate a avea dimensiunea unu.
  • Comparațiile de formă încep cu dimensiunea cea mai din dreapta și continuă spre stânga.
  • Două dimensiuni sunt compatibile dacă dimensiunile lor sunt egale sau dacă una dintre ele este 1.

Exemple de perechi de array-uri care fac 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

Exemple de perechi de array-uri care nu fac 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 returnează câte o estimare a valorii de așteptare pentru fiecare element al formei rezultate în urma broadcasting-ului.

Iată câteva exemple de tipare comune exprimate în termeni de broadcasting de array-uri. Reprezentarea lor vizuală corespunzătoare este prezentată în figura care urmează:

Seturile de valori de parametri sunt reprezentate prin array-uri n x m, iar array-urile de observabile sunt reprezentate prin unul sau mai multe array-uri cu o singură coloană. Pentru fiecare exemplu din codul anterior, seturile de valori de parametri sunt combinate cu array-ul lor de observabile pentru a crea estimările valorilor de așteptare rezultante.

  • Exemplul 1: (broadcasting al unui singur observabil) are un set de valori de parametri care este un array 5x1 și un array de observabile 1x1. Singurul element din array-ul de observabile este combinat cu fiecare element din setul de valori de parametri pentru a crea un singur array 5x1 în care fiecare element este o combinație a elementului original din setul de valori de parametri cu elementul din array-ul de observabile.

  • Exemplul 2: (zip) are un set de valori de parametri 5x1 și un array de observabile 5x1. Rezultatul este un array 5x1 în care fiecare element este o combinație a celui de-al n-lea element din setul de valori de parametri cu cel de-al n-lea element din array-ul de observabile.

  • Exemplul 3: (outer/produs) are un set de valori de parametri 1x6 și un array de observabile 4x1. Combinarea lor duce la un array 4x6 creat prin combinarea fiecărui element din setul de valori de parametri cu fiecare element din array-ul de observabile, astfel încât fiecare valoare de parametru devine o întreagă coloană în rezultat.

  • Exemplul 4: (generalizare standard nd) are un array de set de valori de parametri 3x6 și două array-uri de observabile 3x1. Acestea se combină pentru a crea două array-uri de ieșire 3x6 într-un mod similar cu exemplul anterior.

Această imagine ilustrează mai multe reprezentări vizuale ale broadcasting-ului de array-uri

# 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)
SparsePauliOp

Fiecare SparsePauliOp contează ca un singur element în acest context, indiferent de numărul de Pauli conținuți în SparsePauliOp. Astfel, în scopul acestor reguli de broadcasting, toate elementele de mai jos au aceeași formă:

a = SparsePauliOp("Z") # shape ()
b = SparsePauliOp("IIIIZXYIZ") # shape ()
c = SparsePauliOp.from_list(["XX", "XY", "IZ"]) # shape ()

Următoarele liste de operatori, deși echivalente în ceea ce privește informațiile conținute, au forme diferite:

list1 = SparsePauliOp.from_list(["XX", "XY", "IZ"]) # shape ()
list2 = [SparsePauliOp("XX"), SparsePauliOp("XY"), SparsePauliOp("IZ")] # shape (3, )

Prezentare generală a rezultatelor primitive

Odată ce unul sau mai multe PUB-uri sunt trimise la un QPU pentru execuție și un job se finalizează cu succes, datele sunt returnate sub forma unui obiect container PrimitiveResult, accesat prin apelarea metodei RuntimeJobV2.result(). PrimitiveResult conține o listă iterabilă de obiecte PubResult care conțin rezultatele execuției pentru fiecare PUB. În funcție de primitive-ul folosit, aceste date vor fi fie valori de așteptare și barele lor de eroare, în cazul Estimator-ului, fie eșantioane ale ieșirii Circuit-ului, în cazul Sampler-ului.

Fiecare element al acestei liste corespunde fiecărui PUB trimis la metoda run() a primitive-ului (de exemplu, un job trimis cu 20 de PUB-uri va returna un obiect PrimitiveResult care conține o listă cu 20 de PubResult-uri, câte unul corespunzător fiecărui PUB).

Fiecare dintre aceste obiecte PubResult posedă atât un atribut data, cât și un atribut metadata. Atributul data este un DataBin personalizat, care conține valorile efective ale măsurătorilor, abaterile standard și alte informații similare. Acest DataBin are diverse atribute în funcție de forma sau structura PUB-ului asociat, precum și de opțiunile de mitigare a erorilor specificate de primitive-ul folosit pentru a trimite job-ul (de exemplu, ZNE sau PEC). Între timp, atributul metadata conține informații despre runtime și opțiunile de mitigare a erorilor utilizate (explicate mai târziu în secțiunea Metadate ale rezultatelor de pe această pagină).

Urmează o schiță vizuală a structurii de date PrimitiveResult:

└── 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
├── ...
├── ...
└── ...

Mai simplu spus, un singur job returnează un obiect PrimitiveResult și conține o listă cu unul sau mai multe obiecte PubResult. Aceste obiecte PubResult stochează apoi datele de măsurare pentru fiecare PUB care a fost trimis în job.

Fiecare PubResult posedă formate și atribute diferite în funcție de tipul de primitive folosit pentru job. Detaliile sunt explicate mai jos.

Estimator output

Fiecare PubResult pentru primitive-ul Estimator conține cel puțin un tablou de valori de așteptare (PubResult.data.evs) și abateri standard asociate (fie PubResult.data.stds, fie PubResult.data.ensemble_standard_error, în funcție de resilience_level utilizat), dar poate conține mai multe date în funcție de opțiunile de mitigare a erorilor care au fost specificate.

Fragmentul de cod de mai jos descrie formatul PrimitiveResult (și al PubResult-ului asociat) pentru job-ul creat mai sus.

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 ]]

Cum calculează Estimator-ul eroarea

Pe lângă estimarea mediei observabilelor transmise în PUB-urile de intrare (câmpul evs din DataBin), Estimator-ul încearcă de asemenea să furnizeze o estimare a erorii asociate acelor valori de așteptare. Toate interogările Estimator-ului vor popula câmpul stds cu o cantitate similară erorii standard a mediei pentru fiecare valoare de așteptare, dar unele opțiuni de mitigare a erorilor produc informații suplimentare, cum ar fi ensemble_standard_error.

Consideră un singur observabil O\mathcal{O}. În absența ZNE, poți gândi fiecare shot al execuției Estimator-ului ca furnizând o estimare punctuală a valorii de așteptare O\langle \mathcal{O} \rangle. Dacă estimările punctuale se află într-un vector Os, atunci valoarea returnată în ensemble_standard_error este echivalentă cu următoarea (în care σO\sigma_{\mathcal{O}} este deviația standard a estimării valorii de așteptare și NshotsN_{shots} este numărul de shot-uri):

σONshots,\frac{ \sigma_{\mathcal{O}} }{ \sqrt{N_{shots}} },

care tratează toate shot-urile ca parte a unui singur ansamblu. Dacă ai solicitat twirling pe Gate-uri (twirling.enable_gates = True), poți sorta estimările punctuale ale O\langle \mathcal{O} \rangle în seturi care împart un twirl comun. Numește aceste seturi de estimări O_twirls, și există num_randomizations (numărul de twirl-uri) dintre ele. Atunci stds este eroarea standard a mediei O_twirls, astfel:

σONtwirls,\frac{ \sigma_{\mathcal{O}} }{ \sqrt{N_{twirls}} },

unde σO\sigma_{\mathcal{O}} este deviația standard a O_twirls și NtwirlsN_{twirls} este numărul de twirl-uri. Când nu activezi twirling-ul, stds și ensemble_standard_error sunt egale.

Dacă activezi ZNE, atunci stds descrise mai sus devin ponderi într-o regresie neliniară la un model extrapolator. Ceea ce este returnat în final în stds în acest caz este incertitudinea modelului de potrivire evaluată la un factor de zgomot egal cu zero. Când există o potrivire slabă sau o incertitudine mare în potrivire, stds raportate pot deveni foarte mari. Când ZNE este activat, pub_result.data.evs_noise_factors și pub_result.data.stds_noise_factors sunt de asemenea populate, astfel încât să poți efectua propria extrapolație.

Sampler output

Când un job Sampler se finalizează cu succes, obiectul PrimitiveResult returnat conține o listă de obiecte SamplerPubResult, câte unul per PUB. Compartimentele de date ale acestor obiecte SamplerPubResult sunt obiecte de tip dicționar care conțin câte un BitArray per ClassicalRegister din Circuit.

Clasa BitArray este un container pentru date de shot ordonate. Mai în detaliu, stochează bitstring-urile eșantionate ca octeți într-un tablou bidimensional. Axa cea mai din stânga a acestui tablou parcurge shot-urile ordonate, în timp ce axa cea mai din dreapta parcurge octeții.

Ca prim exemplu, să analizăm următorul Circuit cu zece qubiți:

# generate a ten-qubit GHZ circuit
circuit = QuantumCircuit(10)
circuit.h(0)
circuit.cx(range(0, 9), range(1, 10))

# append measurements with the `measure_all` method
circuit.measure_all()

# transpile the circuit
transpiled_circuit = pm.run(circuit)

# run the Sampler job and retrieve the results
sampler = Sampler(mode=backend)
job = sampler.run([transpiled_circuit])
result = job.result()

# the data bin contains one BitArray
data = result[0].data
print(f"Databin: {data}\n")

# to access the BitArray, use the key "meas", which is the default name of
# the classical register when this is added by the `measure_all` method
array = data.meas
print(f"BitArray: {array}\n")
print(f"The shape of register `meas` is {data.meas.array.shape}.\n")
print(f"The bytes in register `alpha`, shot by shot:\n{data.meas.array}\n")
Databin: DataBin(meas=BitArray(<shape=(), num_shots=4096, num_bits=10>))

BitArray: BitArray(<shape=(), num_shots=4096, num_bits=10>)

The shape of register `meas` is (4096, 2).

The bytes in register `alpha`, shot by shot:
[[ 3 254]
[ 0 0]
[ 3 255]
...
[ 0 0]
[ 3 255]
[ 0 0]]

Uneori poate fi convenabil să convertești din formatul de octeți al BitArray în bitstring-uri. Metoda get_count returnează un dicționar care mapează bitstring-urile la numărul de apariții ale acestora.

# optionally, convert away from the native BitArray format to a dictionary format
counts = data.meas.get_counts()
print(f"Counts: {counts}")
Counts: {'1111111110': 199, '0000000000': 1337, '1111111111': 1052, '1111111000': 33, '1110000000': 65, '1100100000': 2, '1100000000': 25, '0010001110': 1, '0000000011': 30, '1111111011': 58, '1111111010': 25, '0000000110': 7, '0010000001': 11, '0000000001': 179, '1110111110': 6, '1111110000': 33, '1111101111': 49, '1110111111': 40, '0000111010': 2, '0100000000': 35, '0000000010': 51, '0000100000': 31, '0110000000': 7, '0000001111': 22, '1111111100': 24, '1011111110': 5, '0001111111': 58, '0000111111': 24, '1111101110': 10, '0000010001': 5, '0000001001': 2, '0011111111': 38, '0000001000': 11, '1111100000': 34, '0111111111': 45, '0000000100': 18, '0000000101': 2, '1011111111': 11, '1110000001': 13, '1101111000': 1, '0010000000': 52, '0000010000': 17, '0000011111': 15, '1110100001': 1, '0111111110': 9, '0000000111': 19, '1101111111': 15, '1111110111': 17, '0011111110': 5, '0001101110': 1, '0111111011': 6, '0100001000': 2, '0010001111': 1, '1111011000': 1, '0000111110': 4, '0011110010': 1, '1110111100': 2, '1111000000': 8, '1111111101': 27, '0000011110': 6, '0001000000': 5, '1111010000': 3, '0000011011': 4, '0001111110': 9, '1111011110': 6, '1110001111': 2, '0100000001': 7, '1110111011': 3, '1111101101': 2, '1101111110': 5, '1110000010': 7, '0111111000': 1, '1110111000': 1, '0000100001': 2, '1110100000': 6, '1000000001': 2, '0001011111': 1, '0000010111': 1, '1011111100': 1, '0111110000': 5, '0110111111': 2, '0010000010': 1, '0001111100': 4, '0011111001': 2, '1111110011': 1, '1110000011': 5, '0000001011': 8, '0100000010': 3, '1111011111': 13, '0010111000': 2, '0100111110': 1, '1111101000': 2, '1110110000': 2, '1100000001': 1, '0001110000': 3, '1011101111': 2, '1111000001': 2, '1111110001': 8, '1111110110': 4, '1100000010': 3, '0011000000': 2, '1110011111': 3, '0011101111': 3, '0010010000': 2, '0000100010': 1, '1100001110': 1, '0001111011': 4, '1010000000': 3, '0000001110': 5, '0000001010': 2, '0011111011': 4, '0100100000': 2, '1111110100': 1, '1111100011': 3, '0000110110': 1, '0001111101': 2, '1111100001': 2, '1000000000': 5, '0010000011': 3, '0010011111': 3, '0100001111': 1, '0100000111': 1, '1011101110': 1, '0011110111': 1, '1100000111': 1, '1100111111': 3, '0001111010': 1, '1101111011': 1, '0111111100': 2, '0100000110': 2, '0100000011': 2, '0001101111': 3, '0001000001': 1, '1111110010': 1, '0010100000': 1, '0011100000': 4, '1010001111': 1, '0101111111': 2, '1111101001': 1, '1110111101': 1, '0000011101': 1, '1110001000': 2, '0001111001': 1, '0101000000': 1, '1111111001': 5, '0001110111': 2, '0000111001': 1, '0100001011': 1, '0000010011': 1, '1011110111': 1, '0011110001': 1, '0000001100': 2, '0111010111': 1, '0001101011': 1, '1110010000': 2, '1110000100': 1, '0010111111': 3, '0111011100': 1, '1010001000': 1, '0000101110': 1, '0011111100': 2, '0000111100': 2, '1110011110': 1, '0011111000': 2, '0110100000': 1, '1001101111': 1, '1011000000': 1, '1101000000': 1, '1110001011': 1, '1110110111': 1, '0110111110': 1, '0011011111': 1, '0111100000': 1, '0000110111': 1, '0000010010': 2, '1111101100': 2, '1111011101': 1, '1101100000': 1, '0010111110': 1, '1101101110': 1, '1111001111': 1, '1101111100': 1, '1011111010': 1, '0001100000': 1, '1101110111': 1, '1100001011': 1}

Când un Circuit conține mai mult de un registru clasic, rezultatele sunt stocate în obiecte BitArray diferite. Exemplul următor modifică fragmentul anterior prin împărțirea registrului clasic în două registre distincte:

# generate a ten-qubit GHZ circuit with two classical registers
circuit = QuantumCircuit(
qreg := QuantumRegister(10),
alpha := ClassicalRegister(1, "alpha"),
beta := ClassicalRegister(9, "beta"),
)
circuit.h(0)
circuit.cx(range(0, 9), range(1, 10))

# append measurements with the `measure_all` method
circuit.measure([0], alpha)
circuit.measure(range(1, 10), beta)

# transpile the circuit
transpiled_circuit = pm.run(circuit)

# run the Sampler job and retrieve the results
sampler = Sampler(mode=backend)
job = sampler.run([transpiled_circuit])
result = job.result()

# the data bin contains two BitArrays, one per register, and can be accessed
# as attributes using the registers' names
data = result[0].data
print(f"BitArray for register 'alpha': {data.alpha}")
print(f"BitArray for register 'beta': {data.beta}")
BitArray for register 'alpha': BitArray(<shape=(), num_shots=4096, num_bits=1>)
BitArray for register 'beta': BitArray(<shape=(), num_shots=4096, num_bits=9>)

Utilizarea obiectelor BitArray pentru post-procesare performantă

Deoarece tablourile oferă în general o performanță mai bună față de dicționare, este recomandat să efectuezi orice post-procesare direct pe obiectele BitArray, nu pe dicționare de numărători. Clasa BitArray oferă o serie de metode pentru a efectua unele operații comune de post-procesare:

print(f"The shape of register `alpha` is {data.alpha.array.shape}.")
print(f"The bytes in register `alpha`, shot by shot:\n{data.alpha.array}\n")

print(f"The shape of register `beta` is {data.beta.array.shape}.")
print(f"The bytes in register `beta`, shot by shot:\n{data.beta.array}\n")

# post-select the bitstrings of `beta` based on having sampled "1" in `alpha`
mask = data.alpha.array == "0b1"
ps_beta = data.beta[mask[:, 0]]
print(f"The shape of `beta` after post-selection is {ps_beta.array.shape}.")
print(f"The bytes in `beta` after post-selection:\n{ps_beta.array}")

# get a slice of `beta` to retrieve the first three bits
beta_sl_bits = data.beta.slice_bits([0, 1, 2])
print(
f"The shape of `beta` after bit-wise slicing is {beta_sl_bits.array.shape}."
)
print(f"The bytes in `beta` after bit-wise slicing:\n{beta_sl_bits.array}\n")

# get a slice of `beta` to retrieve the bytes of the first five shots
beta_sl_shots = data.beta.slice_shots([0, 1, 2, 3, 4])
print(
f"The shape of `beta` after shot-wise slicing is {beta_sl_shots.array.shape}."
)
print(
f"The bytes in `beta` after shot-wise slicing:\n{beta_sl_shots.array}\n"
)

# calculate the expectation value of diagonal operators on `beta`
ops = [SparsePauliOp("ZZZZZZZZZ"), SparsePauliOp("IIIIIIIIZ")]
exp_vals = data.beta.expectation_values(ops)
for o, e in zip(ops, exp_vals):
print(f"Exp. val. for observable `{o}` is: {e}")

# concatenate the bitstrings in `alpha` and `beta` to "merge" the results of the two
# registers
merged_results = BitArray.concatenate_bits([data.alpha, data.beta])
print(f"\nThe shape of the merged results is {merged_results.array.shape}.")
print(f"The bytes of the merged results:\n{merged_results.array}\n")
The shape of register `alpha` is (4096, 1).
The bytes in register `alpha`, shot by shot:
[[1]
[1]
[1]
...
[0]
[0]
[1]]

The shape of register `beta` is (4096, 2).
The bytes in register `beta`, shot by shot:
[[ 0 135]
[ 0 247]
[ 1 247]
...
[ 0 0]
[ 1 224]
[ 1 255]]

The shape of `beta` after post-selection is (0, 2).
The bytes in `beta` after post-selection:
[]
The shape of `beta` after bit-wise slicing is (4096, 1).
The bytes in `beta` after bit-wise slicing:
[[7]
[7]
[7]
...
[0]
[0]
[7]]

The shape of `beta` after shot-wise slicing is (5, 2).
The bytes in `beta` after shot-wise slicing:
[[ 0 135]
[ 0 247]
[ 1 247]
[ 1 128]
[ 1 255]]

Exp. val. for observable `SparsePauliOp(['ZZZZZZZZZ'],
coeffs=[1.+0.j])` is: 0.068359375
Exp. val. for observable `SparsePauliOp(['IIIIIIIIZ'],
coeffs=[1.+0.j])` is: 0.06396484375

The shape of the merged results is (4096, 2).
The bytes of the merged results:
[[ 1 15]
[ 1 239]
[ 3 239]
...
[ 0 0]
[ 3 192]
[ 3 255]]

Metadatele rezultatelor

Pe lângă rezultatele execuției, atât obiectele PrimitiveResult, cât și PubResult conțin un atribut de metadate referitor la jobul care a fost trimis. Metadatele care conțin informații pentru toate PUB-urile trimise (cum ar fi diversele opțiuni de runtime disponibile) se găsesc în PrimitiveResult.metadata, în timp ce metadatele specifice fiecărui PUB se găsesc în PubResult.metadata.

notă

În câmpul de metadate, implementările primitive pot returna orice informație despre execuție care le este relevantă, și nu există perechi cheie-valoare garantate de primitive-ul de bază. Prin urmare, metadatele returnate pot fi diferite în diferite implementări primitive.

# Print out the results metadata
print("The metadata of the PrimitiveResult is:")
for key, val in result.metadata.items():
print(f"'{key}' : {val},")

print("\nThe metadata of the PubResult result is:")
for key, val in result[0].metadata.items():
print(f"'{key}' : {val},")
The metadata of the PrimitiveResult is:
'execution' : {'execution_spans': ExecutionSpans([DoubleSliceSpan(<start='2026-01-15 08:07:33', stop='2026-01-15 08:07:36', size=4096>)])},
'version' : 2,

The metadata of the PubResult result is:
'circuit_metadata' : {},

Pentru joburile Sampler, poți examina și metadatele rezultatelor pentru a înțelege când au fost rulate anumite date; aceasta se numește execution span.