Qiskit SDK プリミティブによる厳密シミュレーション
Package versions
このページのコードは、以下の要件を使用して開発されました。 これらのバージョン以上を使用することを推奨します。
qiskit[all]~=2.3.0
Qiskit SDK のリファレンスプリミティブは、ローカルの状態ベクトルシミュレーションを実行します。これらのシミュレーションはデバイスノイズのモデリングをサポートしていませんが、より高度なシミュレーション技術(Qiskit Aer の使用)や実機での実行(Qiskit Runtime プリミティブ)を検討する前に、アルゴリズムを素早くプロトタイピングするのに役立ちます。
Estimator プリミティブは回路の期待値を計算でき、Sampler プリミティブは回路の出力分布からサンプリングできます。
以下のセクションでは、リファレンスプリミティブを使用してワークフローをローカルで実行する方法を説明します。
リファレンス Estimator の使用
ローカルの状態ベクトルシミュレーター上で動作する qiskit.primitives の EstimatorV2 リファレンス実装は、StatevectorEstimator クラスです。回路、オブザーバブル、パラメーターを入力として受け取り、ローカルで計算された期待値を返します。
以下のコードは、続く例 で使用する入力を準備します。オブザーバブルの期待される入力型は qiskit.quantum_info.SparsePauliOp です。この例の回路はパラメーター化されていますが、パラメーター化されていない回路でも Estimator を実行できます。
Estimator に渡す回路には、測定を含めてはなりません。
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
from qiskit import QuantumCircuit
from qiskit.circuit import Parameter
# circuit for which you want to obtain the expected value
circuit = QuantumCircuit(2)
circuit.ry(Parameter("theta"), 0)
circuit.h(0)
circuit.cx(0, 1)
circuit.draw("mpl", style="iqp")
from qiskit.quantum_info import SparsePauliOp
import numpy as np
# observable(s) whose expected values you want to compute
observable = SparsePauliOp(["II", "XX", "YY", "ZZ"], coeffs=[1, 1, -1, 1])
# value(s) for the circuit parameter(s)
parameter_values = [[0], [np.pi / 6], [np.pi / 2]]
Qiskit Runtime プリミティブのワークフローでは、回路とオブザーバブルを QPU がサポートする命令のみを使用するように変換する必要があります(命令セットアーキテクチャ(ISA) 回路およびオブザーバブルと呼ばれます)。リファレンスプリミティブはローカルの状態ベクトルシミュレーションに依存するため、抽象的な命令をそのまま受け取りますが、回路の最適化という観点から、回路をトランスパイルすることは依然として有益な場合があります。
# Generate a pass manager without providing a backend
from qiskit.transpiler import generate_preset_pass_manager
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
Estimator の初期化
qiskit.primitives.StatevectorEstimator をインスタンス化します。
from qiskit.primitives import StatevectorEstimator
estimator = StatevectorEstimator()
実行と結果の取得
この例では、1 つの回路(QuantumCircuit 型)と 1 つのオブザーバブルのみを使用します。
StatevectorEstimator.run メソッドを呼び出して推定を実行します。このメソッドは PrimitiveJob オブジェクトのインスタンスを返します。qiskit.primitives.PrimitiveJob.result メソッドを使用して、ジョブから結果(qiskit.primitives.PrimitiveResult オブジェクトとして)を取得できます。
job = estimator.run([(circuit, observable, parameter_values)])
result = job.result()
print(f" > Result class: {type(result)}")
> Result class: <class 'qiskit.primitives.containers.primitive_result.PrimitiveResult'>
結果から期待値を取得する
プリミティブの結果は PubResult オブジェクトの配列を出力します。配列の各要素は PubResult オブジェクトであり、その data には PUB 内のすべての回路-オブザーバブルの組み合わせに対応する評価値の配列が含まれています。
最初の(この場合、唯一の)回路評価の期待値とメタデータを取得するには、PUB 0 の評価 data にアクセスする必要があります。
print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")
> Expectation value: [4. 3.73205081 2. ]
> Metadata: {'target_precision': 0.0, 'circuit_metadata': {}}
Estimator の実行オプションの設定
デフォルトでは、リファレンス Estimator は quantum_info.Statevector クラスに基づく厳密な状態ベクトル計算を実行します。
ただし、これを変更してサンプリングオーバーヘッド(「ショットノイズ」とも呼ばれます)の影響を導入することができます。
Estimator は precision 引数を受け取ります。この引数は、プリミティブ実装が期待値の推定に対して目標とするエラーバーを表します。これはサンプリングオーバーヘッドで あり、.run() メソッド内でのみ定義されます。これにより、PUB レベルまで細かくオプションを調整できます。
# Estimate expectation values for two PUBs, both with 0.05 precision.
precise_job = estimator.run(
[(circuit, observable, parameter_values)], precision=0.05
)
完全な例については、プリミティブの例 ページをご覧ください。
リファレンス Sampler の使用
qiskit.primitives の SamplerV2 リファレンス実装は StatevectorSampler クラスです。回路とパラメーターを入力として受け取り、出力確率分布からサンプリングした結果を出力状態の準確率分布として返します。
以下のコードは、続く例で使用する入力を準備します。これらの例では単一のパラメーター化された回路を実行しますが、パラメーター化されていない回路でも Sampler を実行できます。
from qiskit import QuantumCircuit
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw("mpl", style="iqp")
Sampler に渡す量子回路には、必ず測定を含める必要があります。
Qiskit Runtime プリミティブのワークフローでは、回路を QPU がサポートする命令のみを使用するように変換する必要があります(ISA 回路と呼ばれます)。リファレンスプリミティブはローカルの状態ベクトルシミュレ ーションに依存するため、抽象的な命令をそのまま受け取りますが、回路の最適化という観点から、回路をトランスパイルすることは依然として有益な場合があります。
# Generate a pass manager without providing a backend
from qiskit.transpiler import generate_preset_pass_manager
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(qc)
SamplerV2 の初期化
qiskit.primitives.StatevectorSampler をインスタンス化します。
from qiskit.primitives import StatevectorSampler
sampler = StatevectorSampler()
実行と結果の取得
# execute 1 circuit with Sampler
job = sampler.run([circuit])
pub_result = job.result()[0]
print(f" > Result class: {type(pub_result)}")
> Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>
プリミティブは複数の PUB を入力として受け取り、各 PUB はそれぞれの結果を持ちます。そのため、さまざまなパラメーター/オブザーバブルの組み合わせで異なる回路を実行し、PUB の結果を取得できます。
from qiskit.transpiler import generate_preset_pass_manager
# create two circuits
circuit1 = circuit.copy()
circuit2 = circuit.copy()
# transpile circuits
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit1 = pm.run(circuit1)
isa_circuit2 = pm.run(circuit2)
# execute 2 circuits using Sampler
job = sampler.run([(isa_circuit1), (isa_circuit2)])
pub_result_1 = job.result()[0]
pub_result_2 = job.result()[1]
print(f" > Result class: {type(pub_result)}")
> Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>
確率分布または測定結果の取得
測定結果のサンプルはビット列またはカウントとして返されます。ビット列は測定結果を示し、測定されたショット順序を保持します。Sampler の結果オブジェクトは、動的回路との互換性のために、入力回路の古典レジスタ名に基づいてデータを整理します。
古典レジスタの名前はデフォルトで "meas" となります。この名前は後で測定ビット列にアクセスする際に使用されます。
# Define quantum circuit with 2 qubits
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw()
┌───┐ ░ ┌─┐
q_0: ┤ H ├──■───░─┤M├───
└───┘┌─┴─┐ ░ └╥┘┌─┐
q_1: ─────┤ X ├─░──╫─┤M├
└───┘ ░ ║ └╥┘
meas: 2/══════════════╩══╩═
0 1
# Transpile circuit
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(circuit)
# Run using sampler
result = sampler.run([circuit]).result()
# Access result data for PUB 0
data_pub = result[0].data
# Access bitstring for the classical register "meas"
bitstrings = data_pub.meas.get_bitstrings()
print(f"The number of bitstrings is: {len(bitstrings)}")
# Get counts for the classical register "meas"
counts = data_pub.meas.get_counts()
print(f"The counts are: {counts}")
The number of bitstrings is: 1024
The counts are: {'11': 515, '00': 509}
実行オプションの変更
デフォルトでは、リファレンス Sampler は quantum_info.Statevector クラスに基づく厳密な状態ベクトル計算を実行します。
ただし、これを変更してサンプリングオーバーヘッド(「ショットノイズ」とも呼ばれます)の影響を導入することができます。このオーバーヘッドを管理するために、Sampler インターフェースは PUB レベルで定義できる shots 引数を受け取ります。
この例では、2 つの回路が定義済みであることを前提としています。
# Sample two circuits at 128 shots each.
sampler.run([isa_circuit1, isa_circuit2], shots=128)
# Sample two circuits at different amounts of shots. The "None"s are necessary
# as placeholders
# for the lack of parameter values in this example.
sampler.run([(isa_circuit1, None, 123), (isa_circuit2, None, 456)])
<qiskit.primitives.primitive_job.PrimitiveJob at 0x7fa430e39dd0>
完全な例については、プリミティブの例 ページをご覧ください。
次のステップ
- より大きな回路を処理できる高性能シミュレーション、またはシミュレーションにノイズモデルを組み込む方法については、Qiskit Aer プリミティブによる厳密シミュレーションとノイズありシミュレーション をご覧ください。
- Quantum Composer をシミュレーションに使用する方法については、IBM Quantum Composer ガイドをご覧ください。
- Qiskit Estimator API リファレンスをお読みください。
- Qiskit Sampler API リファレンスをお読みください。
- V2 プリミティブへの移行 をお読みください。