[QuTiP]ブロッホ球のアニメーションの計算方法

# 概要

[前回](https://qiita.com/ryosukehata23/items/243497159d063122486c)の記事で演算子の定義の方法を解説したので，今回はブロッホ球のアニメーションを描写してみます。

今回のコードはすべて[こちら](
https://github.com/ryosukehata/qutip/blob/master/02_QuTiP_Bloch_sphere.ipynb)にありますので，ご参考に。

# ブロッホ球とは

[wikipediaの項目](https://ja.wikipedia.org/wiki/ブロッホ球)を読んでもらった方が正しい記述になると思いますが，私なりの言葉で記載しておきます。
ブロッホ球は，スピンなどの2つの量子状態しかとらない対象を球面上に表す「表記方法」です。
2つの量子状態の時間発展ダイナミクスを球状の動きとして記述することで理解（想像？）しやすいことが利点です。

このブロッホ球で描かれる対象は色々あります。
私が初めてブロッホ球に触れたのは医療現場で画像診断に使われているNMRを理解するためでした。
NMRはスピンエコーを利用して体のスキャン画像を作成しています。
このスピンのダイナミクスを理解するためにブロッホ球を利用していました。

また，量子コンピュータの量子ビットは古典ビットと同じくon-offの二値しかとらないので，その時間発展ダイナミクスはブロッホ球面上の動作として描写できます。
[microsoftのQ#のチュートリアル](https://docs.microsoft.com/en-us/quantum/libraries/standard/prelude?view=qsharp-preview)のpauli operatorの項目にもブロッホ球が出てきます。
ここではゲート操作として出てくるので，知っておくべき内容かなと思います。


# 基本の描写方法

[QuTiPのドキュメント](http://qutip.org/docs/latest/guide/guide-bloch.html)によれば，ブロッホ球のクラスがQuTiPには実装されています。

``` python3
from qutip import Bloch
b = Bloch()
b.show()
```

![empty_Bloch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/195864/72f1870b-3a76-e74e-1b59-758c26a76a75.png)

と，このようにBloch球が描写できます。保存方法は

```python3
b.save(filename)
```

です。Z方向が取ることができる二つの量子状態になっています。
また，適切な操作をすることでx方向やy方向にも初期状態を作ることができます。

適当な操作を行って任意の初期状態を作ったとします。初期状態を描写するためにこのブロッホ球面上にベクトルを書いてみます。|0>方向のベクトルを書きます。

![Bloch_zvector.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/195864/ad8bda43-2aaa-ebb4-f528-6f3133289465.png)

コード

```python3
b = Bloch()
pnt = [0,0,1]
b.add_vectors(pnt)
b.show()
b.save("/Bloch_zvector.png")
```

`pnt`でx, y, zの位置の情報を与えて，`add_vectors`で与えて位置へ中心からベクトル
を描くことができます。
当然ですが，`pnt`をの要素を変えれば描かれるベクトルも変わります。例えば，`pnt = [-1/2, -1/2, 1/2]`であれば，
![Bloch_vector2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/195864/fa651b83-1f1b-aa78-f5f9-fa6022230020.png)
となります。

脇道にそれました。この初期状態|0>のベクトルから次第に変化していく様子を点で描写したいので，次は点の描写のやり方をみてみます。これも簡単で，`add_points`メソッドを使えば良いです。

![Bloch_vec_poi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/195864/17950600-0e77-ca62-fe6e-a194406de770.png)

コード

``` python3
b = Bloch()
pnt = [0,0,1]
b.add_vectors(pnt)
pnt = [0,1/2,1/2]
b.add_points(pnt)
b.show()
b.save(data_dir+"/Bloch_vec_poi.png")
```

青色の点が描写されているのがわかります。
これらを利用して計算を行った結果を可視化してみます。

# 計算結果

いきなりですが，計算結果のアニメーションは次のようになります。

![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/195864/6b7662a2-634e-bce0-4ebd-7fb2c95aca85.gif)

このアニメーションは，[先のドキュメント](http://qutip.org/docs/latest/guide/guide-bloch.html)のexampleのコードを実行したものになります。

初期状態$|0\rangle$が励起状態で，$|1\rangle$への緩和の様子です。
ここではベクトルの方向に沿って，系の運動が行われます。[^1]
[^1]: 多分。天下り的に計算をしていたので検証はしていません。問題があれば指摘してほしいです。
また，$|0\rangle$→$|1\rangle$への緩和を表すために，量子マスター方程式を使ってダイナミクスを解いています。

このあたりの話を真面目に書き出すと趣旨から大きく脱線するので，
- 全体的な外観が知りたければScullyの[Quantum Optics](https://www.amazon.com/Quantum-Optics-Marlan-Scully-ebook/dp/B00AKE1T56?ref_=fsclp_pl_dp_7)
- 緩和の導出や古典統計物理との対応をなどの丁寧な記述であれば，Carmichaelの[Statistical Methods in Quantum Optics 1: Master Equations and Fokker-Planck Equations](https://www.amazon.com/Statistical-Methods-Quantum-Optics-Fokker-Planck/dp/3540548823)
- 緩和の話を中心に知りたければ，GardinerとZollerの[Quntum Noise](https://www.amazon.com/Quantum-Noise-Non-Markovian-Applications-Synergetics/dp/3540223010/ref=sr_1_1?keywords=Quantum+noise&qid=1557144505&s=books&sr=1-1)
私はCarmichaelの本が好きでした。


本筋に戻って，ここで解いているハミルトニアンは

```math
\mathcal{H} = \omega (\cos\theta \sigma_z + \sin\theta \sigma_x)
```

であり，緩和を表すマスター方程式は

```math
\dot{\rho} = -i[\mathcal{H}, \rho] + \Gamma(n+1)(2\sigma_+\rho\sigma_- [\sigma_+\sigma_-, \rho])\\
+\Gamma(n+1)(2\sigma_-\rho\sigma_+ -[\sigma_-\sigma_+, \rho])\\
+\gamma(\sigma_z\rho\sigma_z -2\rho)
```
です。この方程式を解いて，上のアニメーションのようにブロッホ球面上のダイナミクスを計算しています。

これらのダイナミクスを解くためのコードは，

```python3
def qubit_integrate(w, theta, gamma1, gamma2, psi0, tlist):
    # 演算子の定義
    sx = sigmax(); sy = sigmay(); sz = sigmaz(); sm = sigmam()
    # ハミルトニアン
    H = w * (cos(theta) * sz + sin(theta) * sx)
    # マスター方程式
    c_op_list = []
    n_th = 0.5 # temperature
    rate = gamma1 * (n_th + 1)
    if rate > 0.0: c_op_list.append(sqrt(rate) * sm)
    rate = gamma1 * n_th
    if rate > 0.0: c_op_list.append(sqrt(rate) * sm.dag())
    rate = gamma2
    if rate > 0.0: c_op_list.append(sqrt(rate) * sz)


    # evolve and calculate expectation values
    output = mesolve(H, psi0, tlist, c_op_list, [sx, sy, sz])  
    return output.expect[0], output.expect[1], output.expect[2]
```

に当たります。外部から，共鳴周波数$\omega$と$\theta$，緩和レート$\Gamma$, $\gamma$，初期状態$\phi_0$を受け取っています。

演算子の定義

```python3
    # 演算子の定義
    sx = sigmax(); sy = sigmay(); sz = sigmaz(); sm = sigmam()
```
は[前回の記事](https://qiita.com/ryosukehata23/items/243497159d063122486c)のままですね。
ハミルトニアン

``` python3
    # ハミルトニアン
    H = w * (cos(theta) * sz + sin(theta) * sx)
```
は，先程記したものをそのまま打ち込んだものになります。

マスター方程式は

```python3
    n_th = 0.5 # temperature
    rate = gamma1 * (n_th + 1)
    if rate > 0.0: c_op_list.append(sqrt(rate) * sm)
```
で，マスター方程式の1つ目の緩和を表すことができています。
ここまで定義すれば，解きたい時間と解きたい期待値がどれか`msolve`メソッドの入れてあげることで計算ができます。

```python3
    output = mesolve(H, psi0, tlist, c_op_list, [sx, sy, sz])  
```

これの第一引数がハミルトニアン，第二引数が初期状態，第三引数が解きたい時間，第四引数が緩和，第五引数が解きたい期待値がどれか，になります。
第五引数の順番に答えが帰ってくるので，

``` python3
    return output.expect[0], output.expect[1], output.expect[2]
```
で計算結果を返してくれます。私はずっと手でマスター方程式を解いていたので，なんて簡単なんだ！と感動しました。


```python3
def animate(i):
    sphere.clear()
    sphere.add_vectors([np.sin(theta),0,np.cos(theta)])
    sphere.add_points([sx[:i+1],sy[:i+1],sz[:i+1]])
    sphere.make_sphere()
    return ax
```
結果を一点一点アニメーションにしているのが次のコードになります。
ダイナミクスの方向を`add_vectors`メソッドで定義し，時間ごとに少しずつ`add_points`メソッドで点を増加させています。

これを`matplotlib`の`animation.FuncAnimation`で動画化しています。
最後にmp4で保存して，今回の動画を作りました。

```python3
ani.save('bloch_sphere.mp4', fps=20)
```

ただ，このままでは，Qiitaに貼れなかったので，`ffmpeg`を使ってmp4をgifに変換しました。[^2] 
[^2]: 例えば　https://qiita.com/wMETAw/items/fdb754022aec1da88e6e

#　まとめ

- ブロッホ球について簡単に説明しました。
- QuTiPのブロッホ球の描写方法を記載しました。
- ドキュメントどおりにアニメーションを作成しました。