メインコンテンツへスキップ

プリミティブの入力と出力

新しい実行モデル(現在ベータ版として公開中)

新しい実行モデルのベータ版が公開されました。このDirected実行モデルは、エラー軽減ワークフローをカスタマイズする際の柔軟性を高めます。詳細については、Directed実行モデルガイドをご覧ください。

パッケージバージョン

このページのコードは、以下の要件に基づいて開発されました。 これらのバージョン以降をご使用されることをお勧めします。

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

このページでは、IBM Quantum® の計算リソース上でワークロードを実行するQiskit Runtimeプリミティブの入出力の概要を説明します。これらのプリミティブでは、Primitive Unified Bloc(PUB) と呼ばれるデータ構造を用いて、ベクトル化されたワークロードを効率的に定義することができます。PUBはQPUがワークロードを実行するための基本的な作業単位です。PUBはSamplerおよびEstimatorプリミティブのrun()メソッドへの入力として使用され、定義されたワークロードをジョブとして実行します。ジョブが完了すると、使用したPUBおよびSamplerまたはEstimatorプリミティブで指定したランタイムオプションに応じた形式で結果が返されます。

PUBの概要

プリミティブのrun()メソッドを呼び出す際、必須の引数は1つ以上のタプルからなるlistで、それぞれのタプルがプリミティブによって実行される各回路に対応します。各タプルはPUBとみなされ、リスト内の各タプルに必要な要素は使用するプリミティブによって異なります。各タプルに与えるデータは、ブロードキャストによりワークロードの柔軟性を高めるため、さまざまな形状に配置することができます。ブロードキャストのルールについては後述のセクションで説明します。

Estimator PUB

Estimatorプリミティブでは、PUBのフォーマットには最大4つの値を含めることができます。

  • 1つのQuantumCircuit。1つ以上のParameterオブジェクトを含めることもできます。
  • 1つ以上のオブザーバブルのリスト。推定する期待値を指定し、配列として並べます(例:0次元配列として表現した単一のオブザーバブル、1次元配列として表現したオブザーバブルのリストなど)。データはPauliSparsePauliOpPauliListstrなどのObservablesArrayLike形式のいずれかで指定できます。
    備考

    同じ回路を持つ異なるPUBに2つの交換可能なオブザーバブルがある場合、それらは同一の測定を使って推定されることはありません。各PUBは異なる測定基底を表すため、PUBごとに個別の測定が必要です。交換可能なオブザーバブルを同じ測定で推定するには、同じPUB内にまとめる必要があります。

  • 回路にバインドするパラメータ値のコレクション。最後のインデックスが回路のParameterオブジェクトを指す単一の配列状オブジェクトとして指定するか、回路にParameterオブジェクトがない場合は省略(または等価的にNone)できます。
  • (任意)推定する期待値の目標精度

Sampler PUB

Samplerプリミティブでは、PUBタプルのフォーマットには最大3つの値を含めることができます。

  • 1つのQuantumCircuit。1つ以上のParameterオブジェクトを含めることもできます。 注意:これらの回路には、サンプリング対象の各Qubitに対する測定命令も含める必要があります。
  • 回路にバインドするパラメータ値のコレクション θk\theta_k(実行時にバインドする必要があるParameterオブジェクトがある場合のみ必要)
  • (任意)回路を測定するショット数

以下のコードは、Estimatorプリミティブへのベクトル化された入力のセット例を示し、それらを単一のRuntimeJobV2オブジェクトとしてIBM®バックエンド上で実行するものです。

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

ブロードキャストのルール

PUBは複数の配列(オブザーバブルとパラメータ値)の要素をNumPyと同じブロードキャストのルールに従って集約します。このセクションではそれらのルールを簡単にまとめます。詳細についてはNumPyブロードキャストルールのドキュメントをご参照ください。

ルール:

  • 入力配列は同じ次元数である必要はありません。
    • 結果の配列は、最も大きい次元数を持つ入力配列と同じ次元数になります。
    • 各次元のサイズは、対応する次元の最大サイズになります。
    • 存在しない次元はサイズ1とみなされます。
  • 形状の比較は右端の次元から始まり、左方向に進みます。
  • 2つの次元は、それらのサイズが等しいか、一方が1である場合に互換性があります。

ブロードキャスト可能な配列ペアの例:

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

ブロードキャスト不可能な配列ペアの例:

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はブロードキャストされた形状の各要素に対して1つの期待値の推定値を返します。

以下に、配列のブロードキャストで表現した一般的なパターンの例を示します。それらの視覚的な表現はこの後の図に示されています。

パラメータ値セットは n x m の配列で表され、オブザーバブル配列は1つ以上の単一列配列で表されます。前のコードの各例では、パラメータ値セットとオブザーバブル配列を組み合わせて期待値の推定値を作成しています。

  • 例1(単一オブザーバブルのブロードキャスト):パラメータ値セットが5x1の配列で、オブザーバブル配列が1x1です。オブザーバブル配列の1つの要素がパラメータ値セットの各要素と組み合わされ、5x1の配列が生成されます。各要素は、パラメータ値セットの元の要素とオブザーバブル配列の要素の組み合わせです。

  • 例2(zip):5x1のパラメータ値セットと5x1のオブザーバブル配列があります。出力は5x1の配列で、各要素はパラメータ値セットのn番目の要素とオブザーバブル配列のn番目の要素の組み合わせです。

  • 例3(外積/積):1x6のパラメータ値セットと4x1のオブザーバブル配列があります。これらを組み合わせると4x6の配列が作成され、パラメータ値セットの各要素がオブザーバブル配列のすべての要素と組み合わされます。そのため、各パラメータ値は出力の1列全体になります。

  • 例4(標準的なnd一般化):3x6のパラメータ値セット配列と2つの3x1のオブザーバブル配列があります。これらを組み合わせると、前の例と同様の方法で2つの3x6の出力配列が作成されます。

この画像は配列ブロードキャストのいくつかの視覚的な表現を示しています

# 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

SparsePauliOpは、SparsePauliOpに含まれるPauliの数に関わらず、この文脈では単一の要素として数えられます。したがって、これらのブロードキャストルールの観点では、以下のすべての要素は同じ形状を持ちます。

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

以下の演算子リストは、含まれる情報という点では同等ですが、異なる形状を持ちます。

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

プリミティブ出力の概要

1つ以上のPUBがQPUに送信されてジョブが正常に完了すると、データはPrimitiveResultコンテナオブジェクトとして返されます。このオブジェクトはRuntimeJobV2.result()メソッドを呼び出すことでアクセスできます。PrimitiveResultには、各PUBの実行結果を格納したPubResultオブジェクトのイテラブルなリストが含まれます。使用するプリミティブの種類によって、返されるデータはEstimatorの場合は期待値とそのエラーバー、Samplerの場合は回路出力のサンプルとなります。

このリストの各要素は、プリミティブのrun()メソッドに送信した各PUBに対応しています(たとえば、20個のPUBを含むジョブを送信した場合、20個のPubResultを含むPrimitiveResultオブジェクトが返され、それぞれが各PUBに対応します)。

PubResultオブジェクトはdata属性とmetadata属性を持ちます。data属性はカスタマイズされたDataBinで、実際の測定値や標準偏差などが格納されています。このDataBinは、関連するPUBの形状や構造、またジョブ送信に使用したプリミティブが指定するエラー緩和オプション(ZNEPECなど)によってさまざまな属性を持ちます。一方、metadata属性にはランタイムやエラー緩和オプションに関する情報が含まれます(詳細はこのページの結果のメタデータセクションで説明します)。

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

簡単に言うと、1つのジョブはPrimitiveResultオブジェクトを返し、その中に1つ以上のPubResultオブジェクトのリストが含まれます。これらのPubResultオブジェクトには、ジョブに送信された各PUBの測定データが格納されています。

PubResultは、使用したプリミティブの種類によって異なる形式と属性を持ちます。詳細は以下で説明します。

Estimatorの出力

EstimatorプリミティブのPUBに対する各PubResultには、少なくとも期待値の配列(PubResult.data.evs)と関連する標準偏差(使用したresilience_levelに応じてPubResult.data.stdsまたはPubResult.data.ensemble_standard_error)が含まれますが、指定したエラー緩和オプションによってさらに多くのデータが含まれる場合もあります。

以下のコードスニペットは、上記で作成したジョブのPrimitiveResult(および関連するPubResult)の形式を示しています。

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

Estimatorによるエラーの計算方法

Estimatorは、入力PUBに含まれるオブザーバブルの平均値の推定(DataBinevsフィールド)に加えて、各期待値に関連するエラーの推定値も提供しようとします。すべてのEstimatorクエリは、各期待値に対する標準誤差のような量をstdsフィールドに格納しますが、ensemble_standard_errorなど追加情報を生成するエラー緩和オプションもあります。

単一のオブザーバブル O\mathcal{O} を考えます。ZNEを使用しない場合、Estimator実行の各ショットは期待値 O\langle \mathcal{O} \rangle の点推定を提供すると考えることができます。点ごとの推定値がベクトルOsに格納されている場合、ensemble_standard_errorに返される値は以下と等価です(ここで σO\sigma_{\mathcal{O}}期待値の標準偏差の推定値、NshotsN_{shots} はショット数です):

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

これはすべてのショットを単一のアンサンブルの一部として扱います。ゲートのtwirlingtwirling.enable_gates = True)を要求した場合、O\langle \mathcal{O} \rangle の点ごとの推定値を共通のtwirlingを共有するセットに分類できます。これらの推定値のセットをO_twirlsと呼ぶと、num_randomizations個(twirlingの数)のセットが得られます。そしてstdsO_twirlsの平均値の標準誤差となり、次のように表されます:

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

ここで σO\sigma_{\mathcal{O}}O_twirlsの標準偏差、NtwirlsN_{twirls} はtwirlingの数です。twirlingを有効にしない場合、stdsensemble_standard_errorは等しくなります。

ZNEを有効にすると、上記のstdsは外挿モデルへの非線形回帰の重みになります。この場合に最終的にstdsとして返されるのは、ノイズ係数ゼロで評価されたフィットモデルの不確かさです。フィットが不良な場合や不確かさが大きい場合、報告されるstdsは非常に大きくなることがあります。ZNEが有効な場合、pub_result.data.evs_noise_factorspub_result.data.stds_noise_factorsも設定されるため、独自の外挿処理を行うことができます。

Samplerの出力

Samplerジョブが正常に完了すると、返されるPrimitiveResultオブジェクトには、PUBごとに1つのSamplerPubResultのリストが含まれます。これらのSamplerPubResultオブジェクトのデータビンは辞書のようなオブジェクトで、回路内の各ClassicalRegisterに対して1つのBitArrayが含まれます。

BitArrayクラスは、順序付きのショットデータのコンテナです。詳しく言うと、サンプリングされたビット列をバイトとして2次元配列に格納します。この配列の左端の軸は順序付きのショットにわたって走り、右端の軸はバイトにわたって走ります。

最初の例として、次の10量子ビット回路を見てみましょう:

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

BitArrayのバイト形式からビット列に変換すると便利な場合があります。get_countメソッドは、ビット列をそれが出現した回数にマッピングする辞書を返します。

# 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}

回路に複数のクラシカルレジスタが含まれる場合、結果は異なるBitArrayオブジェクトに格納されます。次の例は、前のスニペットをクラシカルレジスタを2つの異なるレジスタに分割した形に変更したものです:

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

高性能な後処理のためのBitArrayオブジェクトの活用

一般的に配列は辞書よりもパフォーマンスに優れているため、カウント数の辞書ではなくBitArrayオブジェクトに対して直接後処理を行うことが推奨されます。BitArrayクラスは、一般的な後処理操作を行うためのさまざまなメソッドを提供しています:

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

結果のメタデータ

実行結果に加えて、PrimitiveResultPubResultの両オブジェクトには、送信されたジョブに関するメタデータ属性が含まれています。送信されたすべてのPUBに関する情報(利用可能なさまざまなランタイムオプションなど)を含むメタデータはPrimitiveResult.metadataで確認でき、各PUBに固有のメタデータはPubResult.metadataで確認できます。

備考

メタデータフィールドでは、プリミティブの実装は実行に関して自身に関連する任意の情報を返すことができ、ベースプリミティブによって保証されたキーと値のペアはありません。そのため、返されるメタデータはプリミティブの実装によって異なる場合があります。

# 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' : {},

Samplerジョブでは、結果のメタデータを確認して特定のデータがいつ実行されたかを理解することもできます。これは実行スパンと呼ばれます。