こんにちはAHS: 最初のアナログハミルトニアンシミュレーションを実行します - Amazon Braket

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

こんにちはAHS: 最初のアナログハミルトニアンシミュレーションを実行します

このセクションでは、最初のアナログハミルトニアンシミュレーションの実行について説明します。

相互作用スピンチェーン

多くの相互作用するパーティクルのシステムの正規例については、8 つのスピンのリング (それぞれが「アップ」∣↑⟩ および「ダウン」∣↓⟩ 状態) を考えてみましょう。このモデルシステムは、小さいものの、自然発生する磁性材料の興味深い現象を既にいくつか示しています。この例では、連続するスピンが反対方向を指す、いわゆる強磁性の順序を準備する方法を示します。

上下反転矢印を含む 8 つの円ノードを接続する図。

手配

1 つのニュートラル原子を使用して各スピンを支え、「アップ」スピン状態と「ダウン」スピン状態は、それぞれ原子の興奮した Rydberg 状態とグラウンド状態でエンコードされます。まず、2 D 配置を作成します。上記のスピンリングは、次のコードでプログラムできます。

前提条件 : Braket SDKをインストールする必要があります。( Braket ホスト型ノートブックインスタンスを使用している場合は、ノートブックがSDKプリインストールされています。) プロットを再現するには、シェルコマンド を使用して matplotlib を個別にインストールする必要がありますpip install matplotlib

import numpy as np import matplotlib.pyplot as plt # required for plotting from braket.ahs.atom_arrangement import AtomArrangement a = 5.7e-6 # nearest-neighbor separation (in meters) register = AtomArrangement() register.add(np.array([0.5, 0.5 + 1/np.sqrt(2)]) * a) register.add(np.array([0.5 + 1/np.sqrt(2), 0.5]) * a) register.add(np.array([0.5 + 1/np.sqrt(2), - 0.5]) * a) register.add(np.array([0.5, - 0.5 - 1/np.sqrt(2)]) * a) register.add(np.array([-0.5, - 0.5 - 1/np.sqrt(2)]) * a) register.add(np.array([-0.5 - 1/np.sqrt(2), - 0.5]) * a) register.add(np.array([-0.5 - 1/np.sqrt(2), 0.5]) * a) register.add(np.array([-0.5, 0.5 + 1/np.sqrt(2)]) * a)

また、 を使用してプロットすることもできます。

fig, ax = plt.subplots(1, 1, figsize=(7,7)) xs, ys = [register.coordinate_list(dim) for dim in (0, 1)] ax.plot(xs, ys, 'r.', ms=15) for idx, (x, y) in enumerate(zip(xs, ys)): ax.text(x, y, f" {idx}", fontsize=12) plt.show() # this will show the plot below in an ipython or jupyter session
両方の軸の正の値と負の値に分散されたポイントを示す散布図。

インタラクション

強磁性相を準備するには、隣接するスピン間の相互作用を誘導する必要があります。このために van der Waals インタラクションを使用します。これは、ニュートラルアトムデバイス ( Aquila からのデバイス QuEra)。 スピン表現を使用すると、このインタラクションのハミルトニアン用語は、すべてのスピンペア (j,k) の合計として表現できます。

この相互作用をすべてのスピンペア (j,k) の合計として表したハミルトニアン相互作用方程式。

ここでは、nj =jj∣↑⟩⟨↑∣ はスピン j が「アップ」状態である場合にのみ 1 の値を取り、それ以外の場合は 0 の値を取る演算子です。強度は V j,k=C 6/(d j,k​6 で、C 6は固定係数、d j,kはスピン j と k の間のユークリッド距離です。この相互作用用語の即時効果は、スピン j とスピン k の両方が「アップ」であるすべての状態が (V の量で) エネルギーが増加していることですj,k。AHS プログラムの残りの部分を慎重に設計することで、この相互作用により、隣接するスピンが両方とも「Rydberg ブロック」として一般的に知られている影響である「アップ」状態になるのを防ぐことができます。

運転フィールド

AHS プログラムの開始時に、すべてのスピン (デフォルトで) は「ダウン」状態で始まり、いわゆる強磁性相になります。強磁性相を準備するという目標に留意して、スピンをこの状態から「アップ」状態が優先される多体状態へスムーズに移行する時間依存のコヒーレント駆動フィールドを指定します。対応するハミルトニアンは次のように記述できます。

ハミルトニアン駆動関数の計算を表す数式。

ここで、 Ω(t)、φ(t)、Δ(t) は、すべてのスピンに均一に影響する駆動フィールドの時間依存的なグローバル振幅 (別名 Rabi 周波数 )、フェーズ、および調整です。ここで、S −,k=k​k∣↓⟩⟨↑∣ および S +,k=(S −,k=k​k∣↑⟩⟨↓∣ はスピン k の下降演算子と上昇演算子であり、n k=kk∣↑↑⟩⟨↑∣ は以前と同じ演算子です。駆動フィールドの Ω 部分は、すべてのスピンの「ダウン」状態と「アップ」状態を同時にコヒーレントに連結し、Δ 部分は「アップ」状態のエネルギー報酬を制御します。

強磁性相から非強磁性相へのスムーズな移行をプログラムするには、次のコードで駆動フィールドを指定します。

from braket.timings.time_series import TimeSeries from braket.ahs.driving_field import DrivingField # smooth transition from "down" to "up" state time_max = 4e-6 # seconds time_ramp = 1e-7 # seconds omega_max = 6300000.0 # rad / sec delta_start = -5 * omega_max delta_end = 5 * omega_max omega = TimeSeries() omega.put(0.0, 0.0) omega.put(time_ramp, omega_max) omega.put(time_max - time_ramp, omega_max) omega.put(time_max, 0.0) delta = TimeSeries() delta.put(0.0, delta_start) delta.put(time_ramp, delta_start) delta.put(time_max - time_ramp, delta_end) delta.put(time_max, delta_end) phi = TimeSeries().put(0.0, 0.0).put(time_max, 0.0) drive = DrivingField( amplitude=omega, phase=phi, detuning=delta )

次のスクリプトを使用して、運転フィールドの時系列を視覚化できます。

fig, axes = plt.subplots(3, 1, figsize=(12, 7), sharex=True) ax = axes[0] time_series = drive.amplitude.time_series ax.plot(time_series.times(), time_series.values(), '.-'); ax.grid() ax.set_ylabel('Omega [rad/s]') ax = axes[1] time_series = drive.detuning.time_series ax.plot(time_series.times(), time_series.values(), '.-'); ax.grid() ax.set_ylabel('Delta [rad/s]') ax = axes[2] time_series = drive.phase.time_series # Note: time series of phase is understood as a piecewise constant function ax.step(time_series.times(), time_series.values(), '.-', where='post'); ax.set_ylabel('phi [rad]') ax.grid() ax.set_xlabel('time [s]') plt.show() # this will show the plot below in an ipython or jupyter session
経時的な phi、delta、omega を示す 3 つのグラフ。上のサブプロットは、6 rads/s をわずかに超える増加を示し、0 に戻るまで 4 秒間留まります。中央のサブプロットは、関連する派生の線形成長を示し、下部のサブプロットはゼロに近い平坦な線を示しています。

AHS プログラム

レジスタ、駆動フィールド (および暗黙的な van der Waals インタラクション) は、Analog Hamiltonian Simulation プログラム を構成しますahs_program

from braket.ahs.analog_hamiltonian_simulation import AnalogHamiltonianSimulation ahs_program = AnalogHamiltonianSimulation( register=register, hamiltonian=drive )

ローカルシミュレーターでの実行

この例では小さい (15 スピン未満) ため、AHS互換性のある で実行する前にQPU、 Braket に付属するローカルAHSシミュレータで実行できますSDK。ローカルシミュレーターは Braket で無料で利用できるためSDK、これはコードが正しく実行できるようにするためのベストプラクティスです。

ここでは、ローカルシミュレーターが量子状態の時間進化を追跡し、最終状態からサンプルを描画するため、ショット数を高い値 (つまり、100 万) に設定することができます。したがって、合計ランタイムをわずかに増加しながら、ショット数を増やすことができます。

from braket.devices import LocalSimulator device = LocalSimulator("braket_ahs") result_simulator = device.run( ahs_program, shots=1_000_000 ).result() # takes about 5 seconds

シミュレータの結果の分析

各スピンの状態 (「ダウン」の場合は「d」、「アップ」の場合は「u」、空のサイトの場合は「e」のいずれか) を推測する次の関数を使用してショット結果を集計し、各設定がショット間で発生した回数をカウントできます。

from collections import Counter def get_counts(result): """Aggregate state counts from AHS shot results A count of strings (of length = # of spins) are returned, where each character denotes the state of a spin (site): e: empty site u: up state spin d: down state spin Args: result (braket.tasks.analog_hamiltonian_simulation_quantum_task_result.AnalogHamiltonianSimulationQuantumTaskResult) Returns dict: number of times each state configuration is measured """ state_counts = Counter() states = ['e', 'u', 'd'] for shot in result.measurements: pre = shot.pre_sequence post = shot.post_sequence state_idx = np.array(pre) * (1 + np.array(post)) state = "".join(map(lambda s_idx: states[s_idx], state_idx)) state_counts.update((state,)) return dict(state_counts) counts_simulator = get_counts(result_simulator) # takes about 5 seconds print(counts_simulator)
{'udududud': 330944, 'dudududu': 329576, 'dududdud': 38033, ...}

以下はcounts、ショット全体で各状態設定が観測された回数をカウントするディクショナリです。また、次のコードを使用して視覚化することもできます。

from collections import Counter def has_neighboring_up_states(state): if 'uu' in state: return True if state[0] == 'u' and state[-1] == 'u': return True return False def number_of_up_states(state): return Counter(state)['u'] def plot_counts(counts): non_blockaded = [] blockaded = [] for state, count in counts.items(): if not has_neighboring_up_states(state): collection = non_blockaded else: collection = blockaded collection.append((state, count, number_of_up_states(state))) blockaded.sort(key=lambda _: _[1], reverse=True) non_blockaded.sort(key=lambda _: _[1], reverse=True) for configurations, name in zip((non_blockaded, blockaded), ('no neighboring "up" states', 'some neighboring "up" states')): plt.figure(figsize=(14, 3)) plt.bar(range(len(configurations)), [item[1] for item in configurations]) plt.xticks(range(len(configurations))) plt.gca().set_xticklabels([item[0] for item in configurations], rotation=90) plt.ylabel('shots') plt.grid(axis='y') plt.title(f'{name} configurations') plt.show() plot_counts(counts_simulator)
隣接する「up」状態設定のない多数のショットを示す棒グラフ。
いくつかの隣接する「up」状態設定のショットを示す棒グラフで、状態は高い値から低い値に下がっています。

プロットから、次の観測値を読み、強磁性相を正常に準備したことを確認できます。

  1. 一般に、ブロックされていない状態 (隣接する 2 つのスピンが「アップ」状態ではない状態) は、隣接するスピンの少なくとも 1 つのペアが両方とも「アップ」状態である状態よりも一般的です。

  2. 一般的に、設定がブロックされていない限り、「up」の興奮が多い状態が推奨されます。

  3. 最も一般的な状態は、実際には完全な強磁性状態"dudududu"と です"udududud"

  4. 2 番目に一般的な状態は、連続する分離が 1、2、2 の「アップ」興奮が 3 つだけある状態です。これは、van der Waals インタラクションが、近傍の近隣にも影響する (ただし、はるかに小さい) ことを示しています。

QuEraの Aquila での実行 QPU

前提条件 : Braket をインストールするだけでなくSDK、Amazon Braket を初めて使用する場合は、必要な開始手順 を完了していることを確認してください。

注記

Braket がホストするノートブックインスタンスを使用している場合、 Braket SDKはインスタンスにプリインストールされています。

すべての依存関係がインストールされている場合、 に接続できます。Aquila QPU.

from braket.aws import AwsDevice aquila_qpu = AwsDevice("arn:aws:braket:us-east-1::device/qpu/quera/Aquila")

プログラムを AHSに適したものにするには QuEra マシンでは、すべての値を四捨五入して、Aquila QPU。(これらの要件は、名前に「解決」が付いたデバイスパラメータによって管理されます。 ノートブックaquila_qpu.properties.dict()で を実行することで確認できます。 Aquila の機能と要件の詳細については、「Aquila ノートブックの概要」を参照してください。) discretize メソッドを呼び出すことでこれを行うことができます。

discretized_ahs_program = ahs_program.discretize(aquila_qpu)

これで、 でプログラムを実行できるようになりました (現在 100 ショットのみを実行)。Aquila QPU.

注記

でこのプログラムを実行する Aquila プロセッサにはコストがかかります。Amazon Braket にはコストトラッカーSDKが含まれており、お客様はコスト制限を設定し、ほぼリアルタイムでコストを追跡できます。

task = aquila_qpu.run(discretized_ahs_program, shots=100) metadata = task.metadata() task_arn = metadata['quantumTaskArn'] task_status = metadata['status'] print(f"ARN: {task_arn}") print(f"status: {task_status}")
task ARN: arn:aws:braket:us-east-1:123456789012:quantum-task/12345678-90ab-cdef-1234-567890abcdef task status: CREATED

量子タスクの実行にかかる時間に大きなばらつきがあるため (可用性ウィンドウとQPU使用率に応じて)、量子タスク をメモしておくことをお勧めします。そのためARN、後で次のコードスニペットを使用してステータスを確認できます。

# Optionally, in a new python session from braket.aws import AwsQuantumTask SAVED_TASK_ARN = "arn:aws:braket:us-east-1:123456789012:quantum-task/12345678-90ab-cdef-1234-567890abcdef" task = AwsQuantumTask(arn=SAVED_TASK_ARN) metadata = task.metadata() task_arn = metadata['quantumTaskArn'] task_status = metadata['status'] print(f"ARN: {task_arn}") print(f"status: {task_status}")
*[Output]* task ARN: arn:aws:braket:us-east-1:123456789012:quantum-task/12345678-90ab-cdef-1234-567890abcdef task status: COMPLETED

ステータスが COMPLETED (Amazon Braket コンソール の量子タスクページから確認することもできます) になると、次の方法で結果をクエリできます。

result_aquila = task.result()

QPU 結果の分析

以前と同じget_counts関数を使用すると、カウントを計算できます。

counts_aquila = get_counts(result_aquila) print(counts_aquila)
*[Output]* {'udududud': 24, 'dudududu': 17, 'dududdud': 3, ...}

と でプロットしますplot_counts

plot_counts(counts_aquila)
隣接する「up」状態の有無にかかわらず、ショットのレートを示す棒グラフ。

ショットのごく一部に空のサイトがあることに注意してください (「e」でマークされています)。これは、 の原子準備の不完全さごとに 1~2% であるためです。Aquila QPU。これとは別に、結果は、ショット数が少ないため、予想される統計的変動内でシミュレーションと一致します。

次のステップ

おめでとうございます。ローカルAHSシミュレーターと を使用して Amazon Braket で最初のAHSワークロードを実行しました。Aquila QPU.

Rydberg 物理学の詳細については、Analog Hamiltonian Simulation と Aquila デバイス、サンプルノートブック を参照してください。