アルゴリズムにおける回路のサンプリング¶
Sampler primitive は、回路をサンプリングして確率分布を抽出するアルゴリズムを設計するために使用します。
背景¶
Sampler
primitive の役割は2つあり、 backend.run()
の代わりに量子デバイスやシミュレーターへの エントリーポイント として機能すること。さらに、測定回数から確率分布を抽出するための アルゴリズム的な抽象化 としての機能もあります。
Sampler
と backend.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 ( Sampler と Estimator) を介して実際のバックエンドとリモート シミュレーターにアクセスします。 ローカル シミュレーションを実行するには、 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()
でゴールに到達するために必要なステップは以下の通りです:
回路の実行
結果オブジェクトからカウントを取得
カウントとショットを使用して確率分布を計算する
まず、回路をクラウドシミュレーターで実行し、結果オブジェクトを出力します。
注釈
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でノイズシミュレーションを実行 を参照してください。