AmazonBraketで学ぶ量子コンピュータ②

# この記事について
量子コンピュータの勉強をはじめて最近一通りAmazonBraketを触り終えました。
最近登場したサービスであることもあり、少し丁寧なドキュメントを残しておくと後続の方が勉強しやすくなったり、参照してわからないところを調べる際に便利かなと思ったので
自分自身の理解向上も兼ねて記事にまとめていこうと思います。

また、量子コンピュータ関係の他の記事は、下記で紹介しています。

https://qiita.com/shnchr/items/41fa7fd1284d74007a7c

※本記事は2021年2月に更新している記事です。更新などにより内容が変わっている可能性もあるのでご注意ください。


# 概要
本記事では量子コンピュータの導入部分に関して解説をしていこうと思います。
参照するのは[こちらの1_Running_quantum_circuits_on_simulators.ipynb](https://github.com/aws/amazon-braket-examples/blob/main/getting_started/1_Running_quantum_circuits_on_simulators.ipynb)というサンプルです。
こちらのサンプルでは[AmazonBraketで学ぶ量子コンピュータ①](https://qiita.com/doiken_/items/a57d15e38aa62e83c99f)で紹介したよりも少しだけ応用的な内容が記載されています。
具体的には、多量子ビット回路を扱ってみたり、AWSで用意されている古典シミュレータ（ローカル、SV1、TN1）を触ってみたり、古典シミュレータを使う利点などを説明しています。
説明に必要なコードは適宜こちらにも記載しながら説明していくのでよろしくお願いします。

# 前提知識
まず簡単に量子コンピュータ（ゲート型）を扱うのに必要な前提知識を説明していきます。
今回は[こちらの1_Running_quantum_circuits_on_simulators.ipynb](https://github.com/aws/amazon-braket-examples/blob/main/getting_started/1_Running_quantum_circuits_on_simulators.ipynb)というサンプルを説明するのに必要な前提知識の説明をするので本当に初めから説明すると長くなりそうです。
ということで、はじめての方は[AmazonBraketで学ぶ量子コンピュータ①](https://qiita.com/doiken_/items/a57d15e38aa62e83c99f)という導入部分を説明している記事もありますのでそちらから読むのも良いかと思います。

## GHZ状態
今回解説するサンプルではGHZ状態を作る回路を作成します。
GHZ状態とは簡単に言うと多量子ビットのもつれ状態のことです。
$N$ビットの量子ビットを用意したとき、以下の通りに示されます。

$$\left|0,0, ...\right> \rightarrow \left|\mathrm{GHZ}\right> = \frac{1}{\sqrt{2}}\left(\left|0,0, ...\right> + \left|1,1, ...\right>\right).$$

回路図で示すと以下のように示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119747/04c6118f-5de5-6ffc-3cf8-4e14f6db8019.png)
（引用：https://github.com/aws/amazon-braket-examples/blob/main/getting_started/1_Running_quantum_circuits_on_simulators.ipynb）

簡単に3量子ビットで少しだけ計算してみましょう。
といってもかなり簡単です。
最初の以下の2量子ビットの回路はどこかで見たことがあるかと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119747/323d78da-13fa-39f8-4276-88d61dc41582.png)

ベル回路です。
[こちらの記事](https://qiita.com/doiken_/items/a57d15e38aa62e83c99f)でも説明しているものです。
この回路を適用することで以下の状態に量子状態をとります。

$$\frac{1}{\sqrt{2}} |00 \rangle + \frac{1}{\sqrt{2}} |11\rangle $$

0, 1量子ビット目は上記の通り表されるので、2量子ビット目も合わせて考えると、上記ベル回路適用後状態と $|0 \rangle$を合わせて

$$\Bigl(\frac{1}{\sqrt{2}} |00 \rangle + \frac{1}{\sqrt{2}} |11\rangle \Bigr) |0 \rangle = \frac{1}{\sqrt{2}} |000 \rangle + \frac{1}{\sqrt{2}} |110\rangle $$

とあらわすことができます。
制御NOTゲートが1量子ビット目と2量子ビット目にもかかっており、1量子ビット目が1のとき、2量子ビット目を反転させるので上記式は以下のようになります。

$$\frac{1}{\sqrt{2}} |000 \rangle + \frac{1}{\sqrt{2}} |110\rangle \xrightarrow{CNOT_{1,2}} \frac{1}{\sqrt{2}} |000 \rangle + \frac{1}{\sqrt{2}} |111\rangle $$

以上より3量子ビットのもつれ状態を作ることができました。
Nビットでも同様にできるので是非計算してみてください。

# AmazonBraket
ここから実際にAmazonBraketを使って説明していきます。
環境を用意されていない方は、まずは[こちらの記事](https://qiita.com/snuffkin/items/29bc6ee24bc2bff55df3)や[ドキュメント](https://aws.amazon.com/jp/braket/)などを参考に環境を用意してください。

環境を用意すると以下のようにデフォルトでサンプルコードが[GitHub](https://github.com/aws/amazon-braket-examples)からインポートされています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119747/99cc2103-4f06-d098-4010-1862b4a85140.png)
今回はこの配下にある
```getting_started/1_Running_quantum_circuits_on_simulators.ipynb```
を見ていきます。

## 事前準備
### import
今回はシミュレータ特有の機能を使ってみるサンプルとなっているのでimport文に少しモジュールが追加されています。
観点としては、

- 量子状態の観測のために `Observable`を用意
- SV1, TN1の使用のために `AwsDevice`を用意

といった感じです。
残りは一般的なモジュールとなるので割愛します。

```python
# AWS imports: Import Braket SDK modules
from braket.circuits import Circuit, Gate, Observable
from braket.devices import LocalSimulator
from braket.aws import AwsDevice
```

### S3バケットの登録
ローカルシミュレータ以外はS3に取得結果の情報を格納する動きになっています。
そのため、S3にバケットを用意してあげて、以下の変数に値を格納しておいてください。

```python
# Enter the S3 bucket you created during onboarding in the code below
my_bucket = "amazon-braket-Your-Bucket-Name" # the name of the bucket
my_prefix = "Your-Folder-Name" # the name of the folder in the bucket
s3_folder = (my_bucket, my_prefix)
```

## 本体
### GHZ回路の定義
GHZ回路を作成します。
以下のような回路です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119747/04c6118f-5de5-6ffc-3cf8-4e14f6db8019.png)
（引用：https://github.com/aws/amazon-braket-examples/blob/main/getting_started/1_Running_quantum_circuits_on_simulators.ipynb）

1. 0量子ビット目にアダマールゲートを適用
2. iiゲート目にを制御ビット、iiゲート目をターゲットビットとした制御NOTゲートを適用

回路オブジェクトを作成することができるため、回路オブジェクトを作って返却してくれる関数を作っています。

```python
# function to build a GHZ state
def ghz_circuit(n_qubits):
    """
    function to return a GHZ circuit ansatz
    input: number of qubits
    """

    # instantiate circuit object（回路オブジェクトのインスタンス）
    circuit = Circuit()
    
    # add Hadamard gate on first qubit（0量子ビット目にアダマールゲートを適用）
    circuit.h(0)

    # apply series of CNOT gates（iiゲート目にを制御ビット、iiゲート目をターゲットビットとした制御NOTゲートを適用）
    for ii in range(0, n_qubits-1):
        circuit.cnot(control=ii, target=ii+1)

    return circuit
```

上記は`n_qubits`個の量子ビットを用いてGHZ回路を作成する関数で以降、この関数を使っていろいろと考察を進めていきます。
具体的には以下のように定義して利用していくことになります。

```python
# define circuit
n_qubits = 10
ghz = ghz_circuit(n_qubits)
```

### 実行
[AmazonBraketで学ぶ量子コンピュータ①](https://qiita.com/doiken_/items/a57d15e38aa62e83c99f)でも紹介しているため、細かい説明は割愛させていただきますが、実行した結果を可視化すると以下のような結果を取得することができます。

![ダウンロード (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119747/ceb3d183-9c57-50c5-a23f-95249839de25.png)

もちろんランダム性のある結果なので多少結果にぶれはあるかと思いますが、
想定通り、`11111111111`、`0000000000`の結果を取得できました。


### 量子状態の取得
AWSのローカルシミュレータでは、回路の実行と同時に観測時における量子状態の取得や理論的な期待値の取得等を行うことができます。

3量子ビットのGHZ回路を考えると最終的に獲得される量子状態は

$$\left|\mathrm{GHZ}\right> = \frac{1}{\sqrt{2}}\left(\left|0,0,0\right> + \left|1,1,1\right>\right) = \left[\frac{1}{\sqrt{2}},0,0,0,0,0,0,\frac{1}{\sqrt{2}}\right]$$

となります。

このとき、Z基底での期待値は$\langle ZZZ\rangle=0$、 $|111 \rangle$の確率振幅は $\left<111|\mathrm{GHZ}\right>=\frac{1}{\sqrt{2}}$ となります。
ついでに観測される確率は$|000 \rangle$ と $|111 \rangle$でそれぞれ 1/2となります。

※Z基底での期待値は$\langle ZZZ\rangle= \langle x |ZZZ | x \rangle$です。（このとき最終的な量子状態を $|x \rangle$としています。）行列計算はテンソル積であること（ZZZの具体的な計算は[こちらを参照](https://qiita.com/doiken_/items/2ff46bde7f107f23de6a#:~:text=%E5%85%B7%E4%BD%93%E4%BE%8B%E2%91%A1%EF%BC%9A3%E9%87%8F%E5%AD%90%E3%83%93%E3%83%83%E3%83%88%E3%81%A7%E8%80%83%E3%81%88%E3%82%8B)）、$| x \rangle =\left[\frac{1}{\sqrt{2}},0,0,0,0,0,0,\frac{1}{\sqrt{2}}\right] $であることに注意すると、$\langle ZZZ\rangle=0$が求まります。

#### 回路の定義
量子状態を取得する際は回路の定義を改めて行う必要があります。
もちろんもともと使用する予定だった回路（今回の場合はGHZ回路）はそのまま再利用すれば良いのですが、量子状態を取得する旨を回路に追加してあげる必要があります。

以下のソースコードでは、まず3量子ビットのGHZ回路を定義しています。
その後結果Typeを状態ベクトル（`state_vector`）で取得することを定義し、各量子ビットにおいて、Z基底での観測を行うことを示しています。
また、特定のベクトルに対する確率振幅も取得することができます。
本サンプルでは $|111\rangle$の確率振幅を取得しています。

```python
# define circuit
n_qubits = 3
ghz = ghz_circuit(n_qubits)      

# add the state_vector ResultType（結果Typeの定義）
ghz.state_vector()
# add the Z \otimes Z \otimes Z expectation value （Z基底での観測）
ghz.expectation(Observable.Z() @ Observable.Z() @ Observable.Z(), target=[0,1,2])
# add the amplitude for |111>（|111>の確率振幅を取得）
ghz.amplitude(state=["111"])
# print circuit including requested result types（結果Typeを含んだ回路の図示）
print(ghz)
```

上記コードにより出力される結果は以下の通りとなります。

```
T  : |0|1|2|   Result Types   |                               
q0 : -H-C---Expectation(Z@Z@Z)-
        |   |                  
q1 : ---X-C-Expectation(Z@Z@Z)-
          | |                  
q2 : -----X-Expectation(Z@Z@Z)-
T  : |0|1|2|   Result Types   |
Additional result types: StateVector, Amplitude(111)
```

期待値を計算するような要素が追加されていることがわかると思います。

では実際に実行してみます。

```python
# run the circuit and output the results
task = device.run(ghz, shots=0)
result = task.result()

# print results
print("Final EXACT state vector:\n", result.values[0])
print("Expectation value <ZZZ>:", np.round(result.values[1], 5))
print("Amplitude <111|Final state>:", result.values[2])
```

注意したいのが`device.run()`の引数の`shots`を0にしている点です。
今回は状態ベクトルの取得と期待値の計算を行うため、実際に測定を実行数必要がありません。
なのでこちらは0に設定します。
逆に `shots > 0`のとき状態ベクトルにはアクセスできません。
また、結果Typeの定義等も回路に加えているため、矛盾がおき、エラーとなります。

上記の結果を見てみてると以下の通りになります。

```
Final EXACT state vector:
 [0.70710678+0.j 0.        +0.j 0.        +0.j 0.        +0.j
 0.        +0.j 0.        +0.j 0.        +0.j 0.70710678+0.j]
Expectation value <ZZZ>: 0.0
Amplitude <111|Final state>: {'111': (0.7071067811865475+0j)}
```

観測値ではないので絶対的な値を取得できます。
前述したとおり、$\langle ZZZ\rangle=0$、 $|111 \rangle$の確率振幅は $\left<111|\mathrm{GHZ}\right>=\frac{1}{\sqrt{2}}$ となっていることがわかるかと思います。

## マネージドシミュレータ
マネージドシミュレータということでかなり簡単に使用できるようになっています。
AWSではマネージドシミュレータということでSV1とTN1を用意しています。
それぞれを使用したいときは以下のようにデバイスの指定を行います。

```python
# set up the managed simulator SV1
device = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/sv1")
```

```python
# set up the managed simulator TN1
device = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/tn1")
```

それ以外は[こちらの記事](https://qiita.com/doiken_/items/a57d15e38aa62e83c99f)で紹介している通り、

① デバイスの定義（上記のやり方）
② 実行
③ 結果の取得
④ 結果の出力

の手順で実行することができます。
ローカルシミュレータと実行方法は`device.run()`でs3バケットを指定しないといけないこと、なるべくタイムアウト時間を設定した方が良いこと以外あまり変わらないので説明は割愛させていただきます（上記記事を見てみてください）。


# 最後に
以上で「AmazonBraketで学ぶ量子コンピュータ②」を終わります。
今回はローカルシミュレータの特殊機能を使ってみたり、他のマネージドシミュレータも使ってみたりしました。
また別記事で他のBraketサンプルの解説を行ないながら量子コンピュータを学んでいける記事を書いていくので是非見てみてください。