Japanese
言語
English
Japanese
Spanish

アルゴリズムにおける回路のサンプリング

Sampler primitive は、回路をサンプリングして確率分布を抽出するアルゴリズムを設計するために使用します。

背景

Sampler primitive の役割は2つあり、 backend.run() の代わりに量子デバイスやシミュレーターへの エントリーポイント として機能すること。さらに、測定回数から確率分布を抽出するための アルゴリズム的な抽象化 としての機能もあります。

Samplerbackend.run() は、どちらも回路を入力として取り込みます。主な違いは出力の形式です。backend.run()カウント を出力しますが、 Sampler はそのカウントを処理し、それに関連する 準確率分布 を出力します。

注釈

Backend.run() model: このモデルでは、 qiskit-ibmq-provider (現在は qiskit-ibm-provider に移行)モジュールを使用して、実際のバックエンドとリモートシミュレータにアクセスしました。 ローカル シミュレーションを実行するには、 qiskit-aer から特定のバックエンドをインポートすることができます。これらはすべて backend.run() というインターフェースに従っています。

Code example with qiskit-ibmq-provider & backend.run()
from qiskit import IBMQ

# Select provider
provider = IBMQ.load_account()

# Get backend
backend = provider.get_backend("ibmq_qasm_simulator") # Use the cloud simulator

# Run
result = backend.run(circuits)

Code example for qiskit-aer & backend.run()
from qiskit_aer import AerSimulator # former import: from qiskit import Aer

# Get local simulator backend
backend = AerSimulator()

# Run
result = backend.run(circuits)

Primitive モデル: qiskit-ibm-runtime primitives ( SamplerEstimator) を介して実際のバックエンドとリモート シミュレーターにアクセスします。 ローカル シミュレーションを実行するには、 qiskit_aer.primitives および qiskit.primitives から特定の ローカル プリミティブをインポートできます。 それらはすべて BaseSampler および BaseEstimator インターフェースに従いますが、 Runtime プリミティブのみが、Runtime サービス、セッション、および組み込みのエラー軽減策へのアクセスを提供します

Code example for Runtime Sampler
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler

# Define service
service = QiskitRuntimeService()

# Get backend
backend = service.backend("ibmq_qasm_simulator") # Use a cloud simulator

# Define Sampler
sampler = Sampler(backend=backend)

# Run Quasi-Probability calculation
result = sampler.run(circuits).result()

Code example for Aer Sampler
from qiskit_aer import Sampler

# Get local simulator Sampler
sampler = Sampler()

# Run Quasi-Probability calculation
result = sampler.run(circuits).result()

次に、まず、回路をサンプリングするエンドツーエンドの例を示します。まず、 backend.run() を使用し、次に Sampler を使用してサンプリングします。

エンド・ツー・エンドの例

1. 問題定義

ある量子状態に関連する確率(または準確率)分布を求めます:

注意

重要: Sampler primitive を使用したい場合、回路は 測定を含んでいなければなりません

from qiskit import QuantumCircuit

circuit = QuantumCircuit(4)
circuit.h(range(2))
circuit.cx(0,1)
circuit.measure_all() # measurement!

2. 実機またはクラウドシミュレーター上で確率分布を計算

2.a. [Legacy] backend.run() を使用する

backend.run() でゴールに到達するために必要なステップは以下の通りです:

  1. 回路の実行

  2. 結果オブジェクトからカウントを取得

  3. カウントとショットを使用して確率分布を計算する


まず、回路をクラウドシミュレーターで実行し、結果オブジェクトを出力します。

注釈

ibmq_qasm_simulator をデバイス名に置き換えて、実際のデバイスの完全なワークフローを確認してください。

from qiskit import IBMQ

# Define provider and backend
provider = IBMQ.load_account()
backend = provider.get_backend("ibmq_qasm_simulator")

# Run
result = backend.run(circuit, shots=1024).result()
>>> print("result: ", result)
result:  Result(backend_name='ibmq_qasm_simulator', backend_version='0.11.0',
qobj_id='65bb8a73-cced-40c1-995a-8961cc2badc4', job_id='63fc95612751d57b6639f777',
success=True, results=[ExperimentResult(shots=1024, success=True, meas_level=2,
data=ExperimentResultData(counts={'0x0': 255, '0x1': 258, '0x2': 243, '0x3': 268}),
header=QobjExperimentHeader(clbit_labels=[['meas', 0], ['meas', 1], ['meas', 2], ['meas', 3]],
creg_sizes=[['meas', 4]], global_phase=0.0, memory_slots=4, metadata={}, n_qubits=4,
name='circuit-930', qreg_sizes=[['q', 4]], qubit_labels=[['q', 0], ['q', 1], ['q', 2], ['q', 3]]),
status=DONE, metadata={'active_input_qubits': [0, 1, 2, 3], 'batched_shots_optimization': False,
'device': 'CPU', 'fusion': {'enabled': False}, 'input_qubit_map': [[3, 3], [2, 2], [1, 1], [0, 0]],
'measure_sampling': True, 'method': 'stabilizer', 'noise': 'ideal', 'num_clbits': 4, 'num_qubits': 4,
'parallel_shots': 1, 'parallel_state_update': 16, 'remapped_qubits': False,
'sample_measure_time': 0.001001096}, seed_simulator=2191402198, time_taken=0.002996865)],
date=2023-02-27 12:35:00.203255+01:00, status=COMPLETED, header=QobjHeader(backend_name='ibmq_qasm_simulator',
backend_version='0.1.547'), metadata={'max_gpu_memory_mb': 0, 'max_memory_mb': 386782, 'mpi_rank': 0,
'num_mpi_processes': 1, 'num_processes_per_experiments': 1, 'omp_enabled': True, 'parallel_experiments': 1,
'time_taken': 0.003215252, 'time_taken_execute': 0.00303248, 'time_taken_load_qobj': 0.000169435},
time_taken=0.003215252, client_version={'qiskit': '0.39.5'})

次に、出力から確率分布を求めます。

counts = result.get_counts(circuit)
quasi_dists = {}
for key,count in counts.items():
    quasi_dists[key] = count/1024
>>> print("counts: ", counts)
>>> print("quasi_dists: ", quasi_dists)
counts:  {'0000': 255, '0001': 258, '0010': 243, '0011': 268}
quasi_dists:  {'0000': 0.2490234375, '0001': 0.251953125, '0010': 0.2373046875, '0011': 0.26171875}

2.b [New] Sampler Runtime primitiveを使用する

Sampler のユーザー側の構文は backend.run() と非常に似ていますが、準確率分布を 直接 (後処理は不要) に、いくつかの重要なメタデータと共に返すので、ワークフローが簡素化されていることに注意してください。

注釈

ibmq_qasm_simulator をデバイス名に置き換えて、実際のデバイスの完全なワークフローを確認してください。

from qiskit_ibm_runtime import QiskitRuntimeService, Sampler

service = QiskitRuntimeService(channel="ibm_quantum")
backend = service.backend("ibmq_qasm_simulator")

sampler = Sampler(backend=backend)

result = sampler.run(circuit, shots=1024).result()
quasi_dists = result.quasi_dists
>>> print("result: ", result)
>>> print("quasi_dists: ", quasi_dists)
result:  SamplerResult(quasi_dists=[{0: 0.2802734375, 1: 0.2509765625, 2: 0.232421875, 3: 0.236328125}],
metadata=[{'header_metadata': {}, 'shots': 1024, 'readout_mitigation_overhead': 1.0,
'readout_mitigation_time': 0.03801989182829857}])
quasi_dists:  [{0: 0.2802734375, 1: 0.2509765625, 2: 0.232421875, 3: 0.236328125}]

注意

出力形式には注意が必要です。 Sampler では、状態はもはやビット列、例えば "11" ではなく、整数、例えば 3 で表されます。 Sampler の出力をビット列に変換するには、以下のように QuasiDistribution.binary_probabilities() メソッドを使用します。

>>> # convert the output to bit strings
>>> binary_quasi_dist = quasi_dists[0].binary_probabilities()
>>> print("binary_quasi_dist: ", binary_quasi_dist)
binary_quasi_dist:  {'0000': 0.2802734375, '0001': 0.2509765625, '0010': 0.232421875, '0011': 0.236328125}

Sampler Runtime primitive はいくつかの機能とチューニング・オプションを提供します。これらのオプションには移行元のレガシーな代替手段はありませんが、パフォーマンスと結果を改善するのに役立ちます。 詳細については、次を参照してください。

3. その他の実行代替 ( 実行時以外)

以下の移行経路は、非Runtime Primitive を使用して、アルゴリズムをテストするためにローカルシミュレーションを使用します。上で定義した問題を解決するために、ローカルな状態ベクトルシミュレーションを使いたいと仮定します。

3.a [Legacy] Qiskit Aer シミュレーターの使用

from qiskit_aer import AerSimulator

# Define the statevector simulator
simulator = AerSimulator(method="statevector")

# Run and get counts
result = simulator.run(circuit, shots=1024).result()
>>> print("result: ", result)
result:  Result(backend_name='aer_simulator_statevector', backend_version='0.11.2',
qobj_id='e51e51bc-96d8-4e10-aa4e-15ee6264f4a0', job_id='c603daa7-2c03-488c-8c75-8c6ea0381bbc',
success=True, results=[ExperimentResult(shots=1024, success=True, meas_level=2,
data=ExperimentResultData(counts={'0x2': 236, '0x0': 276, '0x3': 262, '0x1': 250}),
header=QobjExperimentHeader(clbit_labels=[['meas', 0], ['meas', 1], ['meas', 2], ['meas', 3]],
creg_sizes=[['meas', 4]], global_phase=0.0, memory_slots=4, metadata={}, n_qubits=4, name='circuit-930',
qreg_sizes=[['q', 4]], qubit_labels=[['q', 0], ['q', 1], ['q', 2], ['q', 3]]), status=DONE,
seed_simulator=3531074553, metadata={'parallel_state_update': 16, 'parallel_shots': 1,
'sample_measure_time': 0.000405246, 'noise': 'ideal', 'batched_shots_optimization': False,
'remapped_qubits': False, 'device': 'CPU', 'active_input_qubits': [0, 1, 2, 3], 'measure_sampling': True,
'num_clbits': 4, 'input_qubit_map': [[3, 3], [2, 2], [1, 1], [0, 0]], 'num_qubits': 4, 'method': 'statevector',
'fusion': {'applied': False, 'max_fused_qubits': 5, 'threshold': 14, 'enabled': True}}, time_taken=0.001981756)],
date=2023-02-27T12:38:18.580995, status=COMPLETED, header=QobjHeader(backend_name='aer_simulator_statevector',
backend_version='0.11.2'), metadata={'mpi_rank': 0, 'num_mpi_processes': 1, 'num_processes_per_experiments': 1,
'time_taken': 0.002216379, 'max_gpu_memory_mb': 0, 'time_taken_execute': 0.002005713, 'max_memory_mb': 65536,
'time_taken_load_qobj': 0.000200642, 'parallel_experiments': 1, 'omp_enabled': True},
time_taken=0.0025920867919921875)

次に、出力から確率分布を求めます。

counts = result.get_counts(circuit)
quasi_dists = {}
for key,count in counts.items():
    quasi_dists[key] = count/1024
>>> print("counts: ", counts)
>>> print("quasi_dists: ", quasi_dists)
counts:  {'0010': 236, '0000': 276, '0011': 262, '0001': 250}
quasi_dists:  {'0010': 0.23046875, '0000': 0.26953125, '0011': 0.255859375, '0001': 0.244140625}

3. b. [New] 参照 Sampler または Aer Sampler primitiveの使用

リファレンスの Sampler は、 qiskit.quantum_info モジュール内の Statevector クラスに基づいて、厳密シミュレーションまたはショット・ベースのノイズ・シミュレーションを実行することができます。

from qiskit.primitives import Sampler

sampler = Sampler()

result = sampler.run(circuit).result()
quasi_dists = result.quasi_dists
>>> print("result: ", result)
>>> print("quasi_dists: ", quasi_dists)
result:  SamplerResult(quasi_dists=[{0: 0.249999999999, 1: 0.249999999999,
2: 0.249999999999, 3: 0.249999999999}], metadata=[{}])
quasi_dists:  [{0: 0.249999999999, 1: 0.249999999999, 2: 0.249999999999,
3: 0.249999999999}]

ショットが指定された場合は、ショットに基づくシミュレーションを出力します(厳密ではなくなります):

from qiskit.primitives import Sampler

sampler = Sampler()

result = sampler.run(circuit, shots=1024).result()
quasi_dists = result.quasi_dists
>>> print("result: ", result)
>>> print("quasi_dists: ", quasi_dists)
result:  SamplerResult(quasi_dists=[{0: 0.2490234375, 1: 0.2578125,
2: 0.2431640625, 3: 0.25}], metadata=[{'shots': 1024}])
quasi_dists:  [{0: 0.2490234375, 1: 0.2578125, 2: 0.2431640625, 3: 0.25}]

専用の Sampler を介して Aer simulator に引き続きアクセスできます。 これは、ノイズ モデルでシミュレーションを実行する場合に便利です。 この例では、3.a の結果と一致するようにシミュレーション メソッドが更新されています。

from qiskit_aer.primitives import Sampler as AerSampler # import change!

sampler = AerSampler(run_options= {"method": "statevector"})

result = sampler.run(circuit, shots=1024).result()
quasi_dists = result.quasi_dists
>>> print("result: ", result)
>>> print("quasi_dists: ", quasi_dists)
result:  SamplerResult(quasi_dists=[{1: 0.2802734375, 2: 0.2412109375, 0: 0.2392578125,
3: 0.2392578125}], metadata=[{'shots': 1024, 'simulator_metadata':
{'parallel_state_update': 16, 'parallel_shots': 1, 'sample_measure_time': 0.000409608,
'noise': 'ideal', 'batched_shots_optimization': False, 'remapped_qubits': False,
'device': 'CPU', 'active_input_qubits': [0, 1, 2, 3], 'measure_sampling': True,
'num_clbits': 4, 'input_qubit_map': [[3, 3], [2, 2], [1, 1], [0, 0]], 'num_qubits': 4,
'method': 'statevector', 'fusion': {'applied': False, 'max_fused_qubits': 5,
'threshold': 14, 'enabled': True}}}])
quasi_dists:  [{1: 0.2802734375, 2: 0.2412109375, 0: 0.2392578125, 3: 0.2392578125}]
>>> # Convert the output to bit strings
>>> binary_quasi_dist = quasi_dists[0].binary_probabilities()
>>> print("binary_quasi_dist: ", binary_quasi_dist)
binary_quasi_dist:  {'0001': 0.2802734375, '0010': 0.2412109375, '0000': 0.2392578125, '0011': 0.2392578125}

詳細については、 Qiskit Runtimeでノイズシミュレーションを実行 を参照してください。