密度行列冪を考える

# 密度行列冪

密度行列冪(Density matrix exponentiation) は、ハミルトニアン$H$の下での時間発展$U=e^{-iHt}$を
うまく近似できる手法です。

$U$は$H$の行列指数関数ですので、定義通りやると$H^{k}$がたくさん出てきてしまって大変です。

## 密度行列冪の流れ

密度行列冪では、$H$を密度行列$\rho$として持つような都合のいい量子状態$|\phi \rangle$を用意します。
そして、時間発展させたい($U=e^{-iHt}$を作用させたい）量子状態$|\psi \rangle$との間でSWAPします。
正確には、SWAP操作をハミルトニアン$H_{SWAP}$とした下での時間発展$U=e^{-iH_{SWAP}t}$を繰り返します。
するとなぜか$|\psi \rangle$が、$|\phi \rangle$を介して、所望の時間発展$U=e^{-iHt}$を遂げます。

この仕組みは計算で示せますが、今回は

「$U=e^{-iH_{SWAP}t}$をどうやってやるのか？(どうやってゲートに書き下すのか？）」を考えます。

- とはいっても、**試行錯誤してみたもののよくわかりませんでした。**
体系的に考えないとだめそうです。

## qiskitでexp(-iSWAPt)を実装する

1量子ビット同士のSWAPの元での時間発展$U=e^{-iH_{SWAP}dt}$を実装します。
$dt = 0.1$ としてみます。


```python
from qiskit import QuantumCircuit
from qiskit import execute, Aer
from qiskit.extensions import HamiltonianGate, SwapGate
from qiskit.quantum_info.operators import Operator
from qiskit.quantum_info import partial_trace, Statevector
from qiskit import IBMQ, transpile
import numpy as np

dt = 0.1
Op_swap = Operator(SwapGate([0,1])) # get a swap operator

circ = QuantumCircuit(2)
circ.barrier()
circ.hamiltonian(Op_swap,dt,[0,1])  # time evolution 
circ.barrier()

```

.hamiltonian というメソッドは、引数に指定したハミルトニアンのもとでの時間発展を行うゲートを回路へ追加します。
今回はハミルトニアンとしてSWAPゲートを指定しています。
回路図を見たいのですが、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/7e736a73-920c-8d84-c127-8e7077607836.png)

デフォルトでは、ブラックボックスとして書かれてしまいます。

これを基本ゲートにdecomposeしてみます。

### qasm_simulatorでのdecompose

simulatorのgate ($U_{1,2,3}$,CNOT)へdecomposeします。

```python
circ_transpiled = transpile(circ, backend= Aer.get_backend('qasm_simulator'), optimization_level=3)
circ_transpiled.draw('mpl', fold = 30)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/e11d1b5d-607c-df0b-01d0-06ce08a1d39a.png)

CNOTが3つからむあたりは、SWAPゲートの片鱗が見えます。
(ただしCNOTの標的と制御が全て同じbitから伸びています)

$U_{2}$と$U_{3}$が混ざっていますが、実際には$U_{3}$だけで書けるはずです。[^1]
qiskitでは、decompose先の基本ゲートは任意に指定できます。

Pass manager と Unroller という機能を使います。

```python
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import Unroller
```

```python
pass_ = Unroller(['u3', 'cx']) #U3ゲートとCNOTを指定
pm = PassManager(pass_)
new_circ = pm.run(circ) # decompose
new_circ.draw('mpl')
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/b6e06b23-efe4-8488-5fb1-1186a1d789d3.png)

$U_{3}$に統一出来ました。

寄り道として、pass managerで万能ゲートセット{$H,T,CNOT$}へ（近似的に）decomposeできるでしょうか？(Solvay-Kitaev)
やってみると、

`QiskitError: "Error decomposing node u3: 'NoneType' object has no attribute 'data'"`

うーん。未実装のようです。


### ibmqx2の HW native gateへのdecompose

ibmqx2のnative gate (RZ,$\sqrt{X}$,CNOT)へdecomposeします。

```python
provider = IBMQ.load_account()
circ_transpiled = transpile(circ, backend= provider.get_backend('ibmqx2'), optimization_level=3)
circ_transpiled.draw('mpl', fold = 30)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/dccc9ec5-c231-e221-567e-64ba6efbd16e.png)

なかなかの長さの回路になりました。
CNOTは3つのままで済んでますが、中途半端な確度のRzが随所にみられるので、実機動作は怪しそうですね。


同じことがpass manger機能でも出来るはずですが、試しに同じgate {sx,rz,cnot} を指定してdecomposeしてみると、
`QiskitError: "Error decomposing node u3: 'NoneType' object has no attribute 'data'"`
なぜか出来ません。エラーメッセージによるとu3は無いとダメっぽいので、u3を足して {u3,sx,rz,cnot} としてみると、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/5d552183-661e-7543-013a-3a54ad3540cc.png)

いや、U3で止まるんかい。pass manegerの挙動がいまいちわからないのでした。


# まとめ

時間発展はシミュレーターで実装できたが、基本ゲートへの展開の仕方がいまいち不明。。


[^1]: $U_{3}$は任意の1量子ビットゲートを表現できる自由度があります。