Japanese
言語
English
Japanese
Spanish

注釈

このページは docs/tutorials/how-to-getting-started-with-sampler.ipynb から生成されました。

Sampler primitive 入門

このチュートリアルでは、Qiskit Rumtime Sampler primitiveを設定する方法、設定に使用できるさまざまなoption、およびsession内でprimitiveを効率的に呼び出す方法を説明します。

Primitives

Primitiveは、モジュール化されたアルゴリズムやアプリケーションを簡単に構築するためのコア関数です。

単にカウントを返す代わりに、より即座に意味のある情報を返すことができます。

さらに、IBM Quantumのハードウェアとソフトウェアの最新の進歩にアクセスするためのシームレスな方法を提供します。

Qiskit Runtimeの初期リリースには、次の2つのprimitiveが含まれています。

Sampler: 入力回路から準確率を生成する。

Estimator: 入力回路と観測量から期待値を計算する。

このチュートリアルでは、 Sampler primitiveに焦点を当てます。Estimator primtive 入門 というチュートリアルは別にあります。

Sampler primitive を使う

Backend の基底クラスと同様に、Qiskit Terraには Sampler 基底クラスが定義されており、ユーザーがすべての Sampler 実装と対話する方法を標準化します。これにより、基礎となる実装が異なっていても、期待値計算を行うためのシミュレーターやデバイスの選択を簡単に変更することができます。

このセクションでは、Qiskit Terraのデフォルトの実装であるローカルの状態ベクトル・シミュレーターを使用します。

1. 回路の作成

研究のための正確な量子状態でシステムを準備するために、少なくとも1つの量子回路が必要になります。すべての例に回路が含まれていますが、Qiskit を使用して独自の回路を作成することができます。 Qiskit を使用して回路を作成する方法については、回路の基本チュートリアル を参照してください。

[1]:
from qiskit.circuit.random import random_circuit

circuit = random_circuit(2, 2, seed=0, measure=True).decompose(reps=1)
display(circuit.draw("mpl"))
../_images/tutorials_how-to-getting-started-with-sampler_10_0.png

2. Samplerクラスの初期化

次のステップは、Sampler クラスのインスタンスを作ることです。これは、Sampler primitiveの仕様に準拠した どのような``Sampler`` クラスでも構いません。分かりやすくするため、Qiskit Terraの qiskit.primitives.Sampler クラスを使用します。これは、 Statevector construct (すなわち代数シミュレーション) に基づいています。

[2]:
from qiskit.primitives import Sampler

sampler = Sampler()

3. Samplerの呼び出しおよび結果の取得

回路出力の準確率分散を推定するには、先ほど作成した Sampler インスタンスの run() メソッドを呼び出し、入力パラメータとして回路を渡します。このメソッドの呼び出しは非同期で、Job オブジェクトを戻します。 このオブジェクトを使って job_id()status() のような情報を問い合わせることができます。

[3]:
job = sampler.run(circuit)
print(f">>> Job ID: {job.job_id()}")
print(f">>> Job Status: {job.status()}")
>>> Job ID: 979495e7-7f0d-4b92-acbc-19da7dad864d
>>> Job Status: JobStatus.DONE

ジョブの result() メソッドは、準確率分散とジョブのメタデータ双方を含む SamplerResult を返します。

[4]:
result = job.result()
print(f">>> {result}")
print(f"  > Quasi-distribution: {result.quasi_dists[0]}")
>>> SamplerResult(quasi_dists=[{0: 0.4999999999999999, 1: 0.0, 2: 0.4999999999999998, 3: 0.0}], metadata=[{}])
  > Quasi-distribution: {0: 0.4999999999999999, 1: 0.0, 2: 0.4999999999999998, 3: 0.0}

このように、入力を変えて再度 run() メソッドを呼び出し続けることができます。

[5]:
circuit = random_circuit(2, 2, seed=1, measure=True).decompose(reps=1)

job = sampler.run(circuit)
result = job.result()

display(circuit.draw("mpl"))
print(f">>> Quasi-distribution: {result.quasi_dists[0]}")
../_images/tutorials_how-to-getting-started-with-sampler_20_0.png
>>> Quasi-distribution: {0: 0.9999999999999991, 1: 6.580329297619248e-33, 2: 0.0, 3: 0.0}

run() メソッドに複数の入力を提供することもできます。

[6]:
circuits = (
    random_circuit(2, 2, seed=0, measure=True).decompose(reps=1),
    random_circuit(2, 2, seed=1, measure=True).decompose(reps=1),
)

job = sampler.run(circuits)
result = job.result()

[display(cir.draw("mpl")) for cir in circuits]
print(f">>> Quasi-distribution: {result.quasi_dists}")
../_images/tutorials_how-to-getting-started-with-sampler_22_0.png
../_images/tutorials_how-to-getting-started-with-sampler_22_1.png
>>> Quasi-distribution: [{0: 0.4999999999999999, 1: 0.0, 2: 0.4999999999999998, 3: 0.0}, {0: 0.9999999999999991, 1: 6.580329297619248e-33, 2: 0.0, 3: 0.0}]

もしくはパラメーター化された回路を使用します。

[7]:
from qiskit.circuit.library import RealAmplitudes

circuit = RealAmplitudes(num_qubits=2, reps=2).decompose(reps=1)
circuit.measure_all()
parameter_values = [0, 1, 2, 3, 4, 5]

job = sampler.run(circuit, parameter_values)
result = job.result()

display(circuit.draw("mpl"))
print(f">>> Parameter values: {parameter_values}")
print(f">>> Quasi-distribution: {result.quasi_dists[0]}")
../_images/tutorials_how-to-getting-started-with-sampler_24_0.png
>>> Parameter values: [0, 1, 2, 3, 4, 5]
>>> Quasi-distribution: {0: 0.17158451004815306, 1: 0.0041370682135240654, 2: 0.20402129418492707, 3: 0.6202571275533961}

Qiskit Runtime Samplerの使用

このセクションでは、Qiskit Runtimeの Sampler primitiveの実装方法について説明します。

1. アカウントの初期化

QisKit Runtimeの Sampler はマネージドサービスなので、まずアカウントを初期化する必要があります。 すると、期待値を計算するために使用するシミュレーターや実際のバックエンドを選択できるようになります。

まだアカウントを設定していない場合は、 getting started guide の手順に従ってください。

[8]:
from qiskit_ibm_runtime import QiskitRuntimeService

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

2. 回路の作成

前のセクションと同様に、Sampler primitive への入力として少なくとも1つの回路が必要です。

[9]:
from qiskit.circuit.random import random_circuit

circuit = random_circuit(2, 2, seed=0, measure=True).decompose(reps=1)
display(circuit.draw("mpl"))
../_images/tutorials_how-to-getting-started-with-sampler_32_0.png

3. Qiskit Runtime Sampler の初期化

ここでは、Qiskit Runtimeの Sampler の実装を使用するため、 qiskit.primitives.Sampler の代わりに qiskit_ibm_runtime.Sampler のインスタンスを初期化します。

Sampler を初期化する場合、backend パラメータを使用して、先に選択したバックエンドをターゲットデバイス (またはシミュレーター) として渡す必要があります。

[10]:
from qiskit_ibm_runtime import Sampler

sampler = Sampler(backend=backend)

4. Samplerの呼び出しおよび結果の取得

その後、 run() メソッドを呼び出して、入力回路と観測量の期待値を計算することができます。

[11]:
job = sampler.run(circuit)
print(f">>> Job ID: {job.job_id()}")
print(f">>> Job Status: {job.status()}")
>>> Job ID: cdkrk4qan60ka16e6v0g
>>> Job Status: JobStatus.RUNNING
[12]:
result = job.result()
print(f">>> {result}")
print(f"  > Quasi-distribution: {result.quasi_dists[0]}")
print(f"  > Metadata: {result.metadata[0]}")
>>> SamplerResult(quasi_dists=[{2: 0.49275, 0: 0.50725}], metadata=[{'header_metadata': {}, 'shots': 4000}])
  > Quasi-distribution: {2: 0.49275, 0: 0.50725}
  > Metadata: {'header_metadata': {}, 'shots': 4000}

オプション

primitiveには、さまざまなカテゴリーにグループ化された数々のオプションが用意されています。resilience_level などの一般的に使用されるオプションは、最初のレベルにあります。

options

Options クラスを使用して、さまざまなオプションを指定できます。

以下の例では、Options クラスのインスタンスを作成します。 optimization_level は最初のレベルのオプションで、入力パラメーターとして渡すことができます。 実行環境に関連するオプションは environment パラメーターを使用して渡されます。

[13]:
from qiskit_ibm_runtime import Options

options = Options(optimization_level=3, environment={"log_level": "INFO"})

Options は自動補完をサポートします。Options クラスのインスタンスを作成したら、自動補完を使用して利用可能なオプションを確認できます。 カテゴリーを選択した場合は、再び自動補完を使用して、そのカテゴリーの下で利用可能なオプションを確認できます。

[14]:
from qiskit_ibm_runtime import Options

options = Options()
options.resilience_level = 1
options.execution.shots = 2048

Sampler クラスのインスタンスを作成する場合、作成したばかりの options を渡すことができます。 これらのオプションは run() を使って計算を行うときに適用されます。

[15]:
sampler = Sampler(backend=backend, options=options)
result = sampler.run(circuit).result()
print(f">>> Metadata: {result.metadata[0]}")
>>> Metadata: {'header_metadata': {}, 'shots': 2048, 'readout_mitigation_overhead': 1.0, 'readout_mitigation_time': 0.028210751246660948}

run() メソッドでオプションを渡すこともできます。 これにより、 Sampler インスタンスを作成するときに指定したオプションを、特定の実行のために上書きします。

ほとんどのユーザーはジョブ・レベルでのオプションのみ上書きするので、 カテゴリーを指定する必要はありません。 例えば、execution={"shots": 1024} (これも有効です) の代わりに shots=1024 を指定します 。

[16]:
sampler = Sampler(backend=backend, options=options)
result = sampler.run(circuit, shots=1024).result()
print(f">>> Metadata: {result.metadata[0]}")
>>> Metadata: {'header_metadata': {}, 'shots': 1024, 'readout_mitigation_overhead': 1.0, 'readout_mitigation_time': 0.002864845097064972}

エラーの抑止と軽減

optimization_level および ``resilience_level `` を使用して、エラーの抑止と軽減を構成します。

Sampleroptimization_level 0-3 と resilience_level 0-1 をサポートしています。

[17]:
from qiskit_ibm_runtime import Options

# optimization_level=3 adds dynamical decoupling
# resilience_level=1 adds readout error mitigation
options = Options(optimization_level=3, resilience_level=1)
[18]:
sampler = Sampler(backend=backend, options=options)
result = sampler.run(circuit).result()
print(f">>> Quasi-distribution: {result.quasi_dists[0]}")
print(f">>> Metadata: {result.metadata[0]}")
>>> Quasi-distribution: {0: 0.50175, 2: 0.49825}
>>> Metadata: {'header_metadata': {}, 'shots': 4000, 'readout_mitigation_overhead': 1.0, 'readout_mitigation_time': 0.04642554186284542}

セッション

Qiskit Runtime セッション は、量子コンピューターの反復呼び出しの集合をグループ化することができます。セッションは、プログラムの最初のジョブがセッション内で開始したときに開始されます。セッションが有効であれば、セッション内の後続のジョブは反復アルゴリズム内の人為的な遅延を最小化するようスケジューラーによって優先順位が付けられます。トランスパイルされた回路のように、セッション内で使用されるデータも、不要なオーバーヘッドを避けるためにキャッシュされます。

セッションの時間

セッションが開始されると、セッション・タイムアウトの最大値が割り当てられます。この値は max_time パラメーターを使用して設定できます。

タイムアウト値を指定しない場合、最初のジョブの最大実行時間に設定されます。これは次のうち小さい方の値です。

この制限時間に達すると、セッションは永久にクローズされます。

セッションには 対話的 タイムアウト値もあります。 そのウィンドウ内に待ち行列に入ったセッション・ジョブがない場合、セッションは一時的に無効化され、通常のジョブ選択が再開されます。 この対話的タイムアウト値はシステムによって設定され、上書きすることはできません。

セッション内での Sampler.run の呼び出し

自動的にセッションを開閉するコンテキスト・マネージャー (with ...:) を使って、Qiskit Runtimeセッションを作成できます。セッション内で Sampler.run を1回以上呼び出すことができます。

[19]:
from qiskit_ibm_runtime import Session, Estimator

with Session(backend=backend, max_time="1h"):
    sampler = Sampler()

    result = sampler.run(circuit).result()
    print(f">>> Quasi-distribution from the first run: {result.quasi_dists[0]}")

    result = sampler.run(circuit).result()
    print(f">>> Quasi-distribution from the second run: {result.quasi_dists[0]}")
>>> Quasi-distribution from the first run: {0: 0.498, 2: 0.502}
>>> Quasi-distribution from the second run: {0: 0.498, 2: 0.502}

セッション内での複数primitiveの呼び出し

セッション内は単一のprimitive関数に限定されていません。このセクションでは、複数のprimitiveを使用する例を示します。

最初に、Estimator primitiveのための回路と観測量を用意します。

[20]:
from qiskit.circuit.random import random_circuit
from qiskit.quantum_info import SparsePauliOp

estimator_circuit = random_circuit(2, 2, seed=0).decompose(reps=1)
display(estimator_circuit.draw("mpl"))

observable = SparsePauliOp("XZ")
print(f">>> Observable: {observable.paulis}")
../_images/tutorials_how-to-getting-started-with-sampler_66_0.png
>>> Observable: ['XZ']

以下の例では、Sampler クラスと Estimator クラスのインスタンスを両方作成し、セッション内で run() メソッドを呼び出す方法を示しています。

[21]:
from qiskit_ibm_runtime import Session, Sampler, Estimator

with Session(backend=backend):
    sampler = Sampler()
    estimator = Estimator()

    result = sampler.run(circuit).result()
    print(f">>> Quasi Distribution from the sampler job: {result.quasi_dists[0]}")

    result = estimator.run(estimator_circuit, observable).result()
    print(f">>> Expectation value from the estimator job: {result.values[0]}")
>>> Quasi Distribution from the sampler job: {2: 0.50025, 0: 0.49975}
>>> Expectation value from the estimator job: 0.848

呼び出しは非同期にすることもできます。別のジョブを送信する前に、前のジョブの結果を待つ必要はありません。

[23]:
from qiskit_ibm_runtime import Session, Sampler, Estimator

with Session(backend=backend):
    sampler = Sampler()
    estimator = Estimator()

    sampler_job = sampler.run(circuit)
    estimator_job = estimator.run(estimator_circuit, observable)

    print(
        f">>> Quasi Distribution from the sampler job: {sampler_job.result().quasi_dists[0]}"
    )
    print(
        f">>> Expectation value from the estimator job: {estimator_job.result().values[0]}"
    )
>>> Quasi Distribution from the sampler job: {2: 0.508, 0: 0.492}
>>> Expectation value from the estimator job: 0.8495

まとめ

以下は、Qiskit Runtime primitive、オプション、そしてセッションを簡単にまとめたものです。

[25]:
from qiskit_ibm_runtime import (
    QiskitRuntimeService,
    Session,
    Sampler,
    Estimator,
    Options,
)

# 1. Initialize account
service = QiskitRuntimeService(channel="ibm_quantum")

# 2. Specify options, such as enabling error mitigation
options = Options(resilience_level=1)

# 3. Select a backend.
backend = service.backend("ibmq_qasm_simulator")

# 4. Create a session
with Session(backend=backend):

    # 5. Create primitive instances
    sampler = Sampler(options=options)
    estimator = Estimator(options=options)

    # 6. Submit jobs
    sampler_job = sampler.run(circuit)
    estimator_job = estimator.run(estimator_circuit, observable)

    # 7. Get results
    print(
        f">>> Quasi Distribution from the sampler job: {sampler_job.result().quasi_dists[0]}"
    )
    print(
        f">>> Expectation value from the estimator job: {estimator_job.result().values[0]}"
    )
>>> Quasi Distribution from the sampler job: {0: 0.50125, 2: 0.49875}
>>> Expectation value from the estimator job: 0.8475

参考資料

Sampler メソッドの詳細は、 ``Sampler API reference <https://qiskit.org/documentation/partners/qiskit_ibm_runtime/stubs/qiskit_ibm_runtime.Sampler.html#qiskit_ibm_runtime.Sampler>`__ を参照してください。

そして利用可能なすべてのオプションは、Options API reference に記載されています。

[26]:
import qiskit_ibm_runtime

qiskit_ibm_runtime.version.get_version_info()
[26]:
'0.8.0'
[27]:
from qiskit.tools.jupyter import *

%qiskit_version_table
%qiskit_copyright

Version Information

Qiskit SoftwareVersion
qiskit-terra0.22.2
qiskit-aer0.11.0
qiskit-ibmq-provider0.19.2
qiskit-nature0.5.0
System information
Python version3.8.1
Python compilerClang 11.0.3 (clang-1103.0.32.62)
Python builddefault, Jul 15 2020 18:48:27
OSDarwin
CPUs8
Memory (Gb)16.0
Mon Nov 07 21:10:31 2022 EST

This code is a part of Qiskit

© Copyright IBM 2017, 2022.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.