Wejścia i wyjścia prymitywów
Dostępna jest już wersja beta nowego modelu wykonywania. Ukierunkowany model wykonywania zapewnia większą elastyczność przy dostosowywaniu przepływu pracy z ograniczaniem błędów. Więcej informacji znajdziesz w przewodniku Directed execution model.
Wersje pakietów
Kod na tej stronie został opracowany przy użyciu poniższych wymagań. Zalecamy korzystanie z tych lub nowszych wersji.
qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
Ta strona zawiera omówienie wejść i wyjść prymitywów Qiskit Runtime, które wykonują zadania na zasobach obliczeniowych IBM Quantum®. Prymitywy te umożliwiają wydajne definiowanie zwektoryzowanych zadań przy użyciu struktury danych zwanej Primitive Unified Bloc (PUB). PUBy stanowią podstawową jednostkę pracy, którą QPU musi wykonać. Używa się ich jako wejść do metody run() prymitywów Sampler i Estimator, które uruchamiają zdefiniowane zadanie jako job. Po jego zakończeniu wyniki są zwracane w formacie zależnym zarówno od użytych PUBów, jak i od opcji środowiska uruchomieniowego określonych dla prymitywów Sampler lub Estimator.
Omówienie PUBów
Podczas wywoływania metody run() prymitywu głównym wymaganym argumentem jest list zawierająca jedną lub więcej krotek – po jednej dla każdego Circuit wykonywanego przez prymityw. Każda z tych krotek jest traktowana jako PUB, a wymagane elementy każdej krotki na liście zależą od użytego prymitywu. Dane przekazywane do tych krotek można również układać w różnych kształtach, aby zapewnić elastyczność zadania dzięki mechanizmowi rozgłaszania (broadcasting) – zasady tego mechanizmu opisano w dalszej sekcji.
Estimator PUB
W przypadku prymitywu Estimator format PUB powinien zawierać co najwyżej cztery wartości:
- Jeden
QuantumCircuit, który może zawierać jeden lub więcej obiektówParameter - Listę jednej lub więcej obserwowalnych, określających wartości oczekiwane do oszacowania, ułożonych w tablicę (np. pojedyncza obserwowalna reprezentowana jako tablica 0-wymiarowa, lista obserwowalnych jako tablica 1-wymiarowa itd.). Dane mogą być w dowolnym z formatów
ObservablesArrayLike, takich jakPauli,SparsePauliOp,PauliListlubstr.uwagaJeśli masz dwie przemieniające się obserwowalne w różnych PUBach, ale z tym samym Circuit, nie zostaną one oszacowane przy użyciu tego samego pomiaru. Każdy PUB reprezentuje inną bazę pomiarową, dlatego dla każdego PUBu wymagane są osobne pomiary. Aby przemieniające się obserwowalne były oszacowywane przy użyciu tego samego pomiaru, muszą być zgrupowane w tym samym PUBie.
- Zbiór wartości parametrów do powiązania z Circuit. Można go podać jako pojedynczy obiekt tablicopodobny, gdzie ostatni indeks odpowiada obiektom
ParameterCircuit, lub pominąć (albo równoważnie ustawić naNone), jeśli Circuit nie zawiera obiektówParameter. - (Opcjonalnie) docelową precyzję szacowania wartości oczekiwanych
Sampler PUB
W przypadku prymitywu Sampler format krotki PUB zawiera co najwyżej trzy wartości:
- Jeden
QuantumCircuit, który może zawierać jeden lub więcej obiektówParameterUwaga: Te Circuit powinny również zawierać instrukcje pomiarowe dla każdego Qubitu, który ma być próbkowany. - Zbiór wartości parametrów do powiązania z Circuit (potrzebny tylko, jeśli używane są obiekty
Parameter, które muszą być powiązane w czasie wykonywania) - (Opcjonalnie) liczbę strzałów (shots), z którą Circuit ma być mierzony
Poniższy kod demonstruje przykładowy zestaw zwektoryzowanych wejść dla prymitywu Estimator i wykonuje je na Backend IBM® jako pojedynczy obiekt 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()
Reguły rozgłaszania (broadcasting)
PUBy agregują elementy z wielu tablic (obserwowalne i wartości parametrów), stosując te same reguły rozgłaszania co NumPy. Ta sekcja pokrótce je podsumowuje. Szczegółowe wyjaśnienie znajdziesz w dokumentacji reguł rozgłaszania NumPy.
Reguły:
- Tablice wejściowe nie muszą mieć tej samej liczby wymiarów.
- Tablica wynikowa będzie miała tę samą liczbę wymiarów co tablica wejściowa o największej liczbie wymiarów.
- Rozmiar każdego wymiaru jest największym rozmiarem odpowiedniego wymiaru.
- Brakujące wymiary przyjmuje się jako mające rozmiar jeden.
- Porównania kształtów zaczyna się od wymiaru najbardziej wysuniętego na prawo i kontynuuje w lewo.
- Dwa wymiary są zgodne, jeśli ich rozmiary są równe lub jeśli jeden z nich wynosi 1.
Przykłady par tablic, które się rozgłaszają:
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
Przykłady par tablic, które się nie rozgłaszają:
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 zwraca jedną wartość oczekiwaną dla każdego elementu rozgłoszonego kształtu.
Poniżej przedstawiono kilka przykładów typowych wzorców wyrażonych w kategoriach rozgłaszania tablic. Ich wizualna reprezentacja jest pokazana na poniższym rysunku:
Zestawy wartości parametrów są reprezentowane przez tablice n x m, a tablice obserwowalnych – przez jedną lub więcej tablic jednokolumnowych. W każdym z poniższych przykładów zestawy wartości parametrów są łączone z odpowiadającą im tablicą obserwowalnych, tworząc wynikowe oszacowania wartości oczekiwanych.
-
Przykład 1: (rozgłoszenie pojedynczej obserwowalnej) – zestaw wartości parametrów to tablica 5x1, a tablica obserwowalnych to tablica 1x1. Jeden element tablicy obserwowalnych jest łączony z każdym elementem zestawu wartości parametrów, tworząc pojedynczą tablicę 5x1, gdzie każdy element jest kombinacją oryginalnego elementu zestawu wartości parametrów z elementem tablicy obserwowalnych.
-
Przykład 2: (zip) – zestaw wartości parametrów to tablica 5x1, a tablica obserwowalnych to tablica 5x1. Wyjściem jest tablica 5x1, gdzie każdy element jest kombinacją n-tego elementu zestawu wartości parametrów z n-tym elementem tablicy obserwowalnych.
-
Przykład 3: (iloczyn zewnętrzny/product) – zestaw wartości parametrów to tablica 1x6, a tablica obserwowalnych to tablica 4x1. Ich kombinacja daje tablicę 4x6, tworzoną przez połączenie każdego elementu zestawu wartości parametrów z każdym elementem tablicy obserwowalnych – każda wartość parametru staje się w ten sposób całą kolumną w wyjściu.
-
Przykład 4: (standardowe uogólnienie nd) – tablica zestawu wartości parametrów to tablica 3x6, a dwie tablice obserwowalnych to tablice 3x1. Łączą się one, tworząc dwie wyjściowe tablice 3x6 w sposób podobny do poprzedniego przykładu.
# 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)
Każdy SparsePauliOp liczy się jako pojedynczy element w tym kontekście, niezależnie od liczby operatorów Pauliego zawartych w SparsePauliOp. Zatem na potrzeby tych reguł rozgłaszania wszystkie poniższe elementy mają ten sam kształt:
a = SparsePauliOp("Z") # shape ()
b = SparsePauliOp("IIIIZXYIZ") # shape ()
c = SparsePauliOp.from_list(["XX", "XY", "IZ"]) # shape ()
Poniższe listy operatorów, choć równoważne pod względem zawartych informacji, mają różne kształty:
list1 = SparsePauliOp.from_list(["XX", "XY", "IZ"]) # shape ()
list2 = [SparsePauliOp("XX"), SparsePauliOp("XY"), SparsePauliOp("IZ")] # shape (3, )
Przegląd wyjść prymitywów
Po wysłaniu jednego lub więcej PUBów do QPU i pomyślnym ukończeniu zadania dane są zwracane jako obiekt kontenera PrimitiveResult, dostępny przez wywołanie metody RuntimeJobV2.result(). PrimitiveResult zawiera iterowalną listę obiektów PubResult, które przechowują wyniki wykonania dla każdego PUBu. W zależności od użytego prymitywu dane te będą albo wartościami oczekiwanymi wraz z przedziałami błędów (w przypadku Estimatora), albo próbkami wyjścia Circuit (w przypadku Samplera).
Każdy element tej listy odpowiada jednemu PUBowi przesłanemu do metody run() prymitywu (na przykład zadanie zgłoszone z 20 PUBami zwróci obiekt PrimitiveResult zawierający listę 20 obiektów PubResult, po jednym dla każdego PUBu).
Każdy z tych obiektów PubResult posiada zarówno atrybut data, jak i metadata. Atrybut data to dostosowany obiekt DataBin, który zawiera rzeczywiste wartości pomiarów, odchylenia standardowe i inne dane. Ten DataBin ma różne atrybuty zależnie od kształtu lub struktury powiązanego PUBu oraz od opcji mitygacji błędów określonych przez prymityw użyty do zgłoszenia zadania (na przykład ZNE lub PEC). Z kolei atrybut metadata zawiera informacje o opcjach środowiska uruchomieniowego i mitygacji błędów (omówionych w dalszej części tej strony, w sekcji Metadane wyników).
Poniżej przedstawiono wizualny zarys struktury danych PrimitiveResult:
- Estimator Output
- Sampler Output
└── 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
├── ...
├── ...
└── ...
Mówiąc prosto: pojedyncze zadanie zwraca obiekt PrimitiveResult zawierający listę jednego lub więcej obiektów PubResult. Te obiekty PubResult przechowują dane pomiarowe dla każdego PUBu przesłanego do zadania.
Każdy PubResult ma różny format i atrybuty zależnie od typu prymitywu użytego w zadaniu. Szczegóły opisano poniżej.
Wyjście Estimatora
Każdy PubResult dla prymitywu Estimator zawiera co najmniej tablicę wartości oczekiwanych (PubResult.data.evs) oraz powiązane odchylenia standardowe (albo PubResult.data.stds, albo PubResult.data.ensemble_standard_error — w zależności od użytego resilience_level), lecz może zawierać więcej danych zależnie od określonych opcji mitygacji błędów.
Poniższy fragment kodu opisuje format PrimitiveResult (oraz powiązanego PubResult) dla zadania utworzonego powyżej.
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 ]]
Jak Estimator oblicza błąd
Oprócz oszacowania średniej wartości obserwowalnych przekazanych w wejściowych PUBach (pole evs w DataBin), Estimator stara się również dostarczyć oszacowanie błędu powiązanego z tymi wartościami oczekiwanymi. Wszystkie zapytania do Estimatora wypełnią pole stds wartością podobną do standardowego błędu średniej dla każdej wartości oczekiwanej, jednak niektóre opcje mitygacji błędów generują dodatkowe informacje, takie jak ensemble_standard_error.
Rozważ pojedynczą obserwowalną . Przy braku ZNE możesz traktować każdy strzał (shot) wykonania Estimatora jako punktowe oszacowanie wartości oczekiwanej . Jeśli punktowe oszacowania są zapisane w wektorze Os, to wartość zwrócona w ensemble_standard_error jest równoważna poniższemu wyrażeniu (gdzie jest odchyleniem standardowym oszacowania wartości oczekiwanej, a to liczba strzałów):
co traktuje wszystkie strzały jako część jednego zespołu. Jeśli zażądano gate twirling (twirling.enable_gates = True), możesz posortować punktowe oszacowania w zbiory dzielące wspólny twirl. Nazwij te zbiory oszacowań O_twirls — jest ich num_randomizations (liczba twirli). Wtedy stds jest standardowym błędem średniej zbioru O_twirls, czyli:
gdzie jest odchyleniem standardowym O_twirls, a to liczba twirli. Gdy nie włączysz twirling, stds i ensemble_standard_error są sobie równe.
Wyjście Samplera
Po pomyślnym ukończeniu zadania przez Sampler zwracany obiekt PrimitiveResult zawiera listę obiektów SamplerPubResult — po jednym na każdy PUB. Zasobniki danych tych obiektów SamplerPubResult są obiektami słownikopodobnymi zawierającymi po jednym BitArray na każdy ClassicalRegister w Circuit.
Klasa BitArray to kontener na uporządkowane dane pomiarów. Dokładniej: przechowuje próbkowane ciągi bitów jako bajty wewnątrz dwuwymiarowej tablicy. Lewa oś tej tablicy przebiega po kolejnych pomiarach (shots), prawa — po bajtach.
Jako pierwszy przykład przyjrzyjmy się poniższemu dziesięcio-qubitowemu Circuit:
# 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]]
Czasami wygodnie jest przekonwertować format bajtów z BitArray na ciągi bitów. Metoda get_count zwraca słownik odwzorowujący ciągi bitów na liczbę ich wystąpień.
# 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}
Gdy Circuit zawiera więcej niż jeden rejestr klasyczny, wyniki są przechowywane w oddzielnych obiektach BitArray. Poniższy przykład modyfikuje poprzedni fragment, dzieląc rejestr klasyczny na dwa odrębne rejestry:
# 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>)
Wykorzystanie obiektów BitArray do wydajnego post-processingu
Ponieważ tablice generalnie oferują lepszą wydajność niż słowniki, zaleca się wykonywanie wszelkiego post-processingu bezpośrednio na obiektach BitArray, a nie na słownikach zliczeń. Klasa BitArray oferuje szereg metod do wykonywania typowych operacji post-processingu:
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]]
Metadane wyników
Oprócz wyników wykonania, zarówno obiekty PrimitiveResult, jak i PubResult zawierają atrybut metadanych dotyczących przesłanego zadania. Metadane zawierające informacje o wszystkich przesłanych PUB-ach (takie jak różne opcje środowiska uruchomieniowego) można znaleźć w PrimitiveResult.metatada, natomiast metadane specyficzne dla każdego PUB-a są dostępne w PubResult.metadata.
W polu metadanych implementacje prymitywów mogą zwracać dowolne informacje o wykonaniu, które są dla nich istotne — nie ma żadnych par klucz-wartość gwarantowanych przez bazowy prymityw. W związku z tym zwracane metadane mogą różnić się w zależności od implementacji prymitywu.
# 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' : {},
W przypadku zadań Sampler możesz również przejrzeć metadane wyników, aby zrozumieć, kiedy określone dane zostały uruchomione; jest to nazywane zakresem wykonania.