Qiskitでトロッター分解を用いた量子シミュレーション

# Qiskitでトロッター分解を用いた量子シミュレーション

Dojoの以下記事をqulacsからqiskitに焼き直してみます。
https://dojo.qulacs.org/ja/latest/notebooks/4.2_trotter_decomposition.html

#スクリプト

```python
pip install qiskit pylatexenc
#必要なモジュールのインポート
from qiskit import IBMQ, QuantumCircuit, ClassicalRegister, QuantumRegister
from qiskit import execute, Aer
from qiskit.qasm import pi
from qiskit.tools.visualization import plot_histogram, circuit_drawer
import numpy as np
# ダイナミクスをシミュレーションする時間
t = 3.0
## 横磁場の強さ
h = 3.
# トロッター分解の分割数
M = 100
# 時間の刻み幅
delta = t/M

## 時間と磁化を記録するリスト
x = [i*delta for i in range(M+1)]
y = []

import matplotlib.pyplot as plt

nqubits = 6

from qiskit.aqua.utils import tensorproduct
from qiskit.quantum_info.analysis import average_data
backend = Aer.get_backend('qasm_simulator')

for p in range(M+1):
  circuit_trotter_Ising = QuantumCircuit(nqubits) 
  for k in range(p):
    for i in range(nqubits):
      circuit_trotter_Ising.cx(i,(i+1)%(nqubits))
      circuit_trotter_Ising.rz(2*delta,(i+1)%nqubits) ## RZ(a)=exp(-i*a/2*Z)に注意. rz(rotation_angle, qbit_label)
      circuit_trotter_Ising.cx(i,(i+1)%(nqubits))
      circuit_trotter_Ising.rx(2*delta*h,i) ## RX(a)=exp(-i*a/2*X)に注意
  circuit_trotter_Ising.measure_all()           
  results = execute(circuit_trotter_Ising, backend, shots=1024).result()
  counts = results.get_counts()
  I_mat = np.array([[1, 0], [0, 1]])
  Z_mat = np.array([[1, 0], [0, -1]])
  obs = tensorproduct(Z_mat,I_mat,I_mat,I_mat,I_mat,I_mat) + tensorproduct(I_mat,Z_mat,I_mat,I_mat,I_mat,I_mat) + tensorproduct(I_mat,I_mat,Z_mat,I_mat,I_mat,I_mat) + + tensorproduct(I_mat,I_mat,I_mat,Z_mat,I_mat,I_mat) + tensorproduct(I_mat,I_mat,I_mat,I_mat,Z_mat,I_mat) + tensorproduct(I_mat,I_mat,Z_mat,I_mat,I_mat,Z_mat)
  obs = obs / nqubits
  expectation = average_data(counts, obs)
  print(expectation)
  y.append(expectation )

print(y)
print(x)
plt.plot(x,y)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/131e82de-5ec9-bcc8-7cae-2c989ccfe015.png)


Dojoの結果（ガタツキがないのは、状態ベクトルから解析的に物理量を計算しているため。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/2840ece8-6522-3dd1-4d3c-95d964574361.png)

まぁ、だいたいあっていそうです。
なぜか t= 0.5 のあたりがずれているけど・・・？


___
**20201227追記**
typoが見つかりました。

```python
  obs = tensorproduct(Z_mat,I_mat,I_mat,I_mat,I_mat,I_mat) + tensorproduct(I_mat,Z_mat,I_mat,I_mat,I_mat,I_mat) + tensorproduct(I_mat,I_mat,Z_mat,I_mat,I_mat,I_mat) + + tensorproduct(I_mat,I_mat,I_mat,Z_mat,I_mat,I_mat) + tensorproduct(I_mat,I_mat,I_mat,I_mat,Z_mat,I_mat) + tensorproduct(I_mat,I_mat,Z_mat,I_mat,I_mat,Z_mat)
```
++もおかしいですが、最後の項に$Z$が二個あります・・・

これを正すと、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/f58d5d21-885e-a5ab-1e46-671111a619ce.png)

一致しました。
ちなみに、オブザーバブルを作るにはqiskit.quantum_info.operatorsを使うことも出来ます。
https://qiskit.org/documentation/locale/ja_JP/tutorials/circuits_advanced/02_operators_overview.html
若干、可読性が上がるかもしれません。

```python
from qiskit.quantum_info.operators import Operator, Pauli
pauli0 = Pauli(label='ZIIIII')
pauli1 = Pauli(label='IZIIII')
pauli2 = Pauli(label='IIZIII')
pauli3 = Pauli(label='IIIZII')
pauli4 = Pauli(label='IIIIZI')
pauli5 = Pauli(label='IIIIIZ')

obs_op = (1/6)*(Operator(pauli0)+Operator(pauli1)+Operator(pauli2)+Operator(pauli3)+Operator(pauli4)+Operator(pauli5))
obs = obs_op.data
```
___
ちなみに回路の深さは、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/6d148525-9d34-aa5a-29fb-cd7bac57638c.png)

という絶望的な値です。

# 解説
基本的にはDojo (Qulacs)のコードを移植すればよいのですが、
- Rzゲート、Rxゲートの位相の定義がプラスマイナス逆
- Qiskitでは任意のオブザーバブルの期待値を計算する機能がない

のが注意点です。後者はqiskit.aquaのライブラリを使うか、あるいはこの記事のように
qiskit.quantum_info.analysis.average_data
を使うかになります。
average_data は、測定結果の頻度と行列形式のオブザーバブルを引数に取ります。

Z基底で測定しているのでZの線形和の期待値しか計算できないように思うのですが、
なぜかオブザーバブルにXが含まれていても値が返ってきます。謎だ。。

___

**2020/12/26追記**
この部分は別記事で解説します。結構重要なところです。
___


# 結論

qiskitでシュレディンガー方程式が解けた。