QuTiP不完全ガイド

# 基本編
## QuTiPとは何か
Coming Soon.

## 基本操作
Coming Soon.

# 発展編
## 使用上の注意点や躓きポイント
以下にQuTiPライブラリの注意点や躓きがちなポイントを挙げるが、他の量子計算ライブラリとは異なり、開放量子系などの研究用途で利用することのできる機能が充実しているので、使用をやめようかな〜とかは思わないで欲しい。

### 使用するバージョンについて
QuTiPは比較的新しいライブラリであり、他のライブラリとの互換性などの問題が日々活発に解決されている。特に自分の場合は[ここ](https://groups.google.com/g/qutip/c/R0kXhv1rtO8)で議論されているのと同様のエラーに悩まされた事がある。原因としてはその時に自分が使用していたQuTiPのバージョンが4.5.2であり、バージョンが1.20以降のNumPyとの間で互換性がないためであった。そのバグは[ここ](https://github.com/qutip/qutip/issues/1433)で提起されて、[ここ](https://github.com/qutip/qutip/pull/1440)で修正されて4.5.3以降のバージョンでは修正されている様だ。現在(2022/04/23)の最新版は4.7.0らしいが、".0"のバージョンを使うのは不安なので、自分は4.6.3を使用している。
### Qobjのdimsについて: テンソル積構造にはうるさい
Qobjクラスのインスタンスは次元についての情報を`dims`というプロパティに持っている。このプロパティではテンソル積構造を厳密に取り扱っており、これはQuTiPの良い点でもあり、躓きやすい点でもある。以下で具体例を用いて説明するが、その前にここで説明する問題をできるだけ回避する方法を述べておくと、 **Qobjの中身をアルゴリズミックに更新していく場合にはQobjとして操作するのではなく、一旦NumPyの世界で操作をしてから適切なdimsを指定してQobjを作成することが有効であることがある。**
例えば、2-qubit系の状態空間は複素2次元Hilbert空間同士のテンソル積空間として与えられ、複素4次元Hilbertと同型である。そこで以下の様に2つの4*4行列(Hamiltonianのつもり)を定義してみる。

```py:2通りの方法で行列を定義
# 2qubitのテンソル積空間の演算子をtensor()を用いて定義
H_1 = tensor(sigmaz(), qeye(2))

# 2qubitのテンソル積空間の演算子をnp.array()を通じて定義
np_2 = np.array(    
        [
            [0, 0, 0, 0], 
            [0, 0, 1, 0], 
            [0, 1, 0, 0], 
            [0, 0, 0, 0]
        ]
    )
H_2 = Qobj(np_2)
```

そして、この二つの行列を足してみる。

```py:上記の行列H_1とH_2を足す
H_1 + H_2
```

すると以下の様なエラーを返される。

```
TypeError: Incompatible quantum object dimensions
```

この原因こそが、Qobjの持つdimsプロパティである。実際、

```py:それぞれの行列のdimsを確認
H_1.dims
# [[2, 2], [2, 2]]

H_2.dims
# [[4], [4]]
```

からわかる様に、2つのQobjの次元が異なっており、テンソル積の構造についての情報がQobjに埋め込まれていることがわかる。このエラーを回避する理想的な方法はプログラマー自身がしっかりとテンソル積構造をQobjに教えてあげることである。具体的には先程定義した`H_2`というQojにdimsを明示的に設定すればよく、

```py:先程のコード修正版(一部抜粋)
H_2 = Qobj(np_2, dims=[[2,2],[2,2]])
```

の様に定義すればよい。

### コヒーレント状態の定義の仕方について

量子光学などでもお馴染みのコヒーレント状態$|\alpha\rangle$は消滅演算子$\hat{ a }$を用いて

```math
\hat{a}|{\alpha}\rangle = \alpha|\alpha\rangle
```

の様な固有方程式を満たす状態$|\alpha\rangle$として定義される。
QuTiPにおいて消滅演算子は`destroy(6)`で、コヒーレント状態は`coherent(6)`の様に定義されている(ただし6は適当)ので、この固有値方程式を再現してみようと思い、$\alpha = 1$のコヒーレント状態$| \alpha \rangle$について左辺と右辺を以下の様に計算してみた。

```py:左辺
coherent(6, 1)
# Quantum object: dims = [[6], [1]], shape = (6, 1), type = ket
# Qobj data =
# [[0.60652947]
#  [0.60654435]
#  [0.42878001]
#  [0.24817702]
#  [0.12139736]
#  [0.06359643]]
```

```py:右辺
destroy(6) * coherent(6, 1)
# Quantum object: dims = [[6], [1]], shape = (6, 1), type = ket
# Qobj data =
# [[0.60654435]
#  [0.60638651]
#  [0.42985521]
#  [0.24279472]
#  [0.14220593]
#  [0.        ]]
```
右辺の第6成分が0になっているのは`destroy(6)`の[仕様](https://nbviewer.ipython.org/github/jrjohansson/qutip-lectures/blob/master/Lecture-0-Introduction-to-QuTiP.ipynb#:~:text=Ops...%20The%20result%20is%20not%20identity!%20Why%3F%20Because%20we%20have%20truncated%20the%20Hilbert%20space.%20But%20that%27s%20OK%20as%20long%20as%20the%20highest%20Fock%20state%20isn%27t%20involved%20in%20the%20dynamics%20in%20our%20truncated%20Hilbert%20space.%20If%20it%20is%2C%20the%20approximation%20that%20the%20truncation%20introduces%20might%20be%20a%20problem.)なので問題ないとして、それ以外の成分も一致していないではないか。と言うわけで[公式ドキュメント](https://qutip.org/docs/4.1/apidoc/functions.html?highlight=coherent#qutip.states.coherent)を参照すると、QuTiPにおけるコヒーレント状態の定義の方法には`operator(デフォルト)`と`analytic`の2通りがあるらしい、デフォルト設定の前者は生成されたコヒーレント状態ベクトルが"N次元空間のベクトルとして"正規化されており、後者は正規化をしない(つまり"∞次元空間のベクトルとして"正規化された)ベクトルを生成する。ただし、ここでNとは`coherent(N)`の様に引数に与えた値でありここでの例だとN=6である。
と言うわけで、`analytic`モードでコヒーレント状態を生成して、前述の固有方程式を確認すると

```py:左辺
coherent(6, 1, method = 'analytic')
# Quantum object: dims = [[6], [1]], shape = (6, 1), type = ket
# Qobj data =
# [[0.60653066]
#  [0.60653066]
#  [0.42888194]
#  [0.2476151 ]
#  [0.12380755]
#  [0.05536842]]
```

```py:右辺
destroy(6) * coherent(6, 1, method = 'analytic')
# Quantum object: dims = [[6], [1]], shape = (6, 1), type = ket
# Qobj data =
# [[0.60653066]
#  [0.60653066]
#  [0.42888194]
#  [0.2476151 ]
#  [0.12380755]
#  [0.        ]]
```

となり、両者の値が完全に一致していることがわかる。安心安心。