Skip to main content
IBM Quantum Platform

プリミティブな入出力

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

    qiskit[all]~=2.5.1
    

このページでは、 Qiskit SDK プリミティブの入力と出力の概要を説明します。 これらのプリミティブを使用することで、「 プリミティブ統合ブロック( PUB ) 」と呼ばれるデータ構造を活用し、ベクトル化されたワークロードを効率的に定義することができます。 これらのPUBは、ワークロード実行における基本的な処理単位です。 これらは、Sampler および Estimator プリミティブのメソッド run() への入力として使用され、定義されたワークロードをジョブとして実行します。 その後、ジョブが完了すると、使用されたPUBや指定されたオプションに応じて、結果が所定の形式で返されます。


パブの概要

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

見積もりツール PUB

Estimatorプリミティブの場合、 PUB のフォーマットは最大4つの値を含むべきである:

  • 1つの QuantumCircuit には1つ以上のオブジェクトが含まれる。 Parameter オブジェクト
  • 推定する期待値を指定する1つ以上の観測値を配列に並べたリスト(例えば、1つの観測値は0-d配列、観測値のリストは1-d配列、など)。 データは、 PauliSparsePauliOpPauliListstr のような ObservablesArrayLike フォーマットのいずれかである。
    Note

    異なるPUBに属するが同じ回路を持つ2つの通勤可能な観測可能量が存在する場合、それらは同じ測定値を用いて推定されることはない。 各 PUB は異なる測定基準を表すため、各 PUB ごとに個別の測定が必要となる。 通勤観測量が同一の測定値を用いて推定されることを保証するには、それらを同一の PUB 内にグループ化する必要がある。

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

サンプラー PUB

Samplerプリミティブの場合、 PUB タプルの形式には最大3つの値が含まれます:

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

以下のコードは、プリミティブ Estimator へのベクトル化された入力の例を示しています。

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.primitives import StatevectorEstimator


import numpy as np

# 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 without providing a backend
pm = generate_preset_pass_manager(optimization_level=1)
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, 10),
        np.linspace(-4 * np.pi, 4 * np.pi, 10),
    ]
).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 = StatevectorEstimator()
estimator_pub = (transpiled_circuit, observables, params)

# Run the transpiled circuit
# using the set of parameters and observables.

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
# The following would work if the middle dimension were 2,
# instead of 5.
A2     (3d array):  6 x 5 x 4

Estimator ブロードキャストされた形状の各要素に対して、1つの期待値の推定値を返します。

以下は、配列放送で表現される一般的なパターンの例である。 それに伴う視覚的な表現を次の図に示す:

パラメータ値セットはn×mの配列で表現され、観測可能配列は1つ以上の1列の配列で表現される。 前のコードの各例について、パラメータ値セットは、結果の期待値推定値を作成するために、それらの観測可能な配列と組み合わされる。

  • 例1 : (broadcast single observable)は 5x1 配列と 1x1 observables配列のパラメータ値セットを持っています。 observables配列の1つの項目は、パラメータ値セットの各項目と組み合わされ、各項目がパラメータ値セットの元の項目とobservables配列の項目の組み合わせである1つの 5x1 配列を作成する。

  • 例2 : (zip)は 5x1 パラメータ値セットと 5x1 observables配列を持っています。 出力は 5x1 の配列で、各項目はパラメータ値セットのn番目の項目とobservables配列のn番目の項目の組み合わせである。

  • 例3 : (outer/product)は 1x6 パラメータ値セットと 4x1 observables配列を持っています。 これらの組み合わせの結果、 4x6 配列が作成される。この配列は、パラメータ値セットの各項目とobservables配列の各項目を組み合わせることによって作成されるため、各パラメータ値は出力の列全体となる。

  • 例4 : (Standard ndの一般化)は、 3x6 パラメータ値セット配列と、 3x1 2つの観測値配列を持っています。 これらを組み合わせて、先の例と同様の方法で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 は1つの要素としてカウントされる。 したがって、この放送ルールでは、以下の要素はすべて同じ形状を持つ:

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

以下の演算子のリストは、含まれる情報の点では同等だが、形状が異なる:

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

プリミティブ出力の概要

1つ以上のPUBがQPUに送信されて実行され、ジョブが正常に完了すると、データはコンテナ PrimitiveResult オブジェクトとして返されます。 には、各 PrimitiveResultPUB の実行結果を含む PubResult オブジェクトの反復可能なリストが含まれています。 たとえば、20個のPUBを指定してジョブを実行すると、各 PUB に対応する20個のリストを含むオブジェクト PrimitiveResult``PubResults が返されます。

これらの PubResult オブジェクトはそれぞれ、属性 data と、オプションの metadata 属性を持ちます。 この data 属性は、Estimatorの場合は期待値の推定値を、Samplerの場合は回路出力のサンプル値を含む、 DataBin カスタマイズされたものです。

この data 属性には、標準偏差など、実装固有のその他の情報も含まれる場合があります。 この metadata 属性には、関連する PUB の実行に関する、実装固有の追加情報を含めることができます。

以下は、 PrimitiveResult データ構造の視覚的アウトラインである:

└── PrimitiveResult
    ├── PubResult[0]
    │   ├── metadata
    │   └── data  ## In the form of a DataBin object,
    |       |     ## which includes data such as the following:
    │       ├── 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,
    |       |     ## which includes data such as the following:
    |       ├── 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
    ├── ...
    ├── ...
    └── ...
Note

上記は、返される可能性のあるデータの例です。 実際に返されるデータは、実装によって異なります。

見積もり出力

前述の通り、Estimatorプリミティブ PubResult で返されるデータは実装によって異なります。 たとえば、期待値の配列 (PubResult.data.evs) と、それに対応する標準偏差 (PubResult.data.stds) が含まれている場合があります。

以下のコード・スニペットは、上記で作成したジョブの PrimitiveResult (および関連する PubResult )フォーマットについて説明しています。

print(
    f"The result of the submitted job had {len(result)} PUB and "
    f"has a value:\n {result}\n"
)
print(
    f"The associated PubResult of this job has the following data bins:"
    f"\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 number of parameters in the circuit -- "
    "combined with our array of observables having shape (3, 1)."
)

print(
    f"The expectation values measured from this PUB are: \n{result[0].data.evs}"
)

Output:

The result of the submitted job had 1 PUB and has a value:
 PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(3, 10), dtype=float64>), stds=np.ndarray(<shape=(3, 10), dtype=float64>), shape=(3, 10)), metadata={'target_precision': 0.0, 'circuit_metadata': {}})], metadata={'version': 2})

The associated PubResult of this job has the following data bins:
 DataBin(evs=np.ndarray(<shape=(3, 10), dtype=float64>), stds=np.ndarray(<shape=(3, 10), dtype=float64>), shape=(3, 10))

And this DataBin has attributes: dict_keys(['evs', 'stds'])
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: 
[[ 3.06161700e-16  4.52395120e-01  4.36594428e-01  2.16506351e-01
   6.33718361e-01 -6.33718361e-01 -2.16506351e-01 -4.36594428e-01
  -4.52395120e-01 -3.06161700e-16]
 [ 1.22464680e-16  6.42787610e-01  9.84807753e-01  8.66025404e-01
   3.42020143e-01 -3.42020143e-01 -8.66025404e-01 -9.84807753e-01
  -6.42787610e-01 -1.22464680e-16]
 [ 4.89858720e-16  2.62002630e-01 -1.11618897e-01 -4.33012702e-01
   9.25416578e-01 -9.25416578e-01  4.33012702e-01  1.11618897e-01
  -2.62002630e-01 -4.89858720e-16]]

サンプラー出力

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

BitArray クラスは、順番に並べられたショットデータのコンテナです。 より詳細には、サンプリングされたビット列をバイトとして2次元配列に格納する。 この配列の一番左の軸はオーダーされたショットを表し、一番右の軸はバイトを表す。

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

from qiskit.primitives import StatevectorSampler

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

sampler = StatevectorSampler()

# run the Sampler job and retrieve the results

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

Output:

Databin: DataBin(meas=BitArray(<shape=(), num_shots=1024, num_bits=10>))

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

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

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

場合によっては、の BitArray バイト形式をビット列に変換すると便利なことがあります。 この get_count メソッドは、ビット列とその出現回数を対応付けした辞書を返します。

# optionally convert the native BitArray format to a dictionary format
counts = data.meas.get_counts()
print(f"Counts: {counts}")

Output:

Counts: {'1111111111': 517, '0000000000': 507}

回路に複数の古典レジスタが含まれる場合、結果は異なる 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

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}")

Output:

BitArray for register 'alpha': BitArray(<shape=(), num_shots=1024, num_bits=1>)
BitArray for register 'beta': BitArray(<shape=(), num_shots=1024, 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")

Output:

The shape of register `alpha` is (1024, 1).
The bytes in register `alpha`, shot by shot:
[[1]
 [0]
 [1]
 ...
 [0]
 [1]
 [1]]

The shape of register `beta` is (1024, 2).
The bytes in register `beta`, shot by shot:
[[  1 255]
 [  0   0]
 [  1 255]
 ...
 [  0   0]
 [  1 255]
 [  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 (1024, 1).
The bytes in `beta` after bit-wise slicing:
[[7]
 [0]
 [7]
 ...
 [0]
 [7]
 [7]]

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

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

The shape of the merged results is (1024, 2).
The bytes of the merged results:
[[  3 255]
 [  0   0]
 [  3 255]
 ...
 [  0   0]
 [  3 255]
 [  3 255]]


結果メタデータ

実行結果に加え、および PubResult オブジェクト PrimitiveResult には、送信されたジョブに関するオプションのメタデータ属性が含まれています。 返されるメタデータ(ある場合)は、実装に依存します。

# 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},")

Output:

The metadata of the PrimitiveResult is:
'version' : 2,

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

次のステップ

推奨事項
このページは役に立ちましたか?
バグや誤字の報告、またはコンテンツの要求はGitHubで行ってください。