Python QulacsでToffoliゲートを作成してみた

# はじめに
量子プログラムを書いてみようと思います。
京都大学・QunaSys社のオープンソースライブラリの[Qulacs](http://qulacs.org/index.html)を使って、練習と勉強がてらToffoliゲートをPythonで実装してみました。
Qulacsについては
[PythonフレームワークのQulacsで量子コンピュータに触れてみる](https://qiita.com/sakiyama0101/items/ad2e5c987873261fce71)
にも記事があるので、参考にしてください。

# Toffoliゲート
量子回路上で$|x,y,z\rangle\mapsto |x,y,x\wedge y \oplus z\rangle~(x,y,z=0,1)$を実現します。
したがって$z=0$とすれば、3番目の量子ビットの状態を観測することにより、1,2番目の量子状態のANDが得られるので、それを見てみます。
実装ですが、適当にググって出てきた
[Realization of the quantum Toffoli gate](https://qudev.phys.ethz.ch/content/courses/QSIT10/presentations/QSIT-ToffoliGate.pdf)
のp.5にToffoliを実現する回路図がありますが、~~面倒くさいので~~今回はToffoliゲートを表すユニタリー変換を実装します。
表現行列は以下になります:

```math
\begin{pmatrix}
1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\
0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\
0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\
0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\
0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\
0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\
0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \\
0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 
\end{pmatrix}
```

# コード
Qulacs公式リファレンス見ながら書いてみます。
まず、必要なモジュールをインポートします。

```python
from qulacs import QuantumState
from qulacs import QuantumCircuit
from qulacs import Observable
from qulacs.gate import DenseMatrix
```

Toffoliゲートは3量子ビットの回路なので、その設定と入力状態を設定します。
この例では$|110\rangle$を入力します。
したがって、出力は量子ビット3を観測するので、$|1\rangle$となるはずです。

```python
# 量子ビット数3(0,1,2の3つ)
n = 3
state = QuantumState(n)
# 入力状態は|110>
state.set_computational_basis(0b110)
```

回路にToffoliゲートを追加。

```python
toffoli = [
    [1, 0, 0, 0, 0, 0, 0, 0],
    [0, 1, 0, 0, 0, 0, 0, 0],
    [0, 0, 1, 0, 0, 0, 0, 0],
    [0, 0, 0, 1, 0, 0, 0, 0],
    [0, 0, 0, 0, 1, 0, 0, 0],
    [0, 0, 0, 0, 0, 1, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 1],
    [0, 0, 0, 0, 0, 0, 1, 0]
]
gate = DenseMatrix([0, 1, 2], toffoli)
circuit.add_gate(gate)
```

量子状態を更新します。

```python
circuit.update_quantum_state(state)
```

観測のため、オブザーバブルを設定します。
$Z$方向の上下を観測するので、Pauli $Z$で観測します。
ここちょっと謎なんですが、リファレンス読む限り、下記'Z 0'と書いている部分は'Z 2'だと思うのですが、ゲートなし3量子回路で書いてみて確認したら'Z 0'じゃないと想定した動作をしませんでした。これだけ数える順番逆なのかな？

```python
# オブザーバブルの設定
# 量子ビット数3
# n = 3
observable = Observable(n)
# 量子ビット2の上下を観測
# 何故か'Z 2'ではなく'Z 0'
observable.add_operator(1.0, 'Z 0')
```

最後に結果を表示します。

```python
# 量子ビット2の上下を観測
value = observable.get_expectation_value(state)
# 結果表示
value = (1.0 - value) / 2.0
# 状態ベクトル表記で出力するように細工
print('|' + str(int(value)) + '>')
```

## 結果
ちゃんと$|1\rangle$が返ってきました。
他の入力$|x,y,0\rangle$でも$|x\wedge y\rangle$が返ってきました。

# まとめ
今回はToffoliゲートをPython Qulacsを使って量子プログラムを実装してみて、ちゃんとANDを返す量子回路を書くことができました。
最後に全体コードです。

```python
# インポート
from qulacs import QuantumState
from qulacs import QuantumCircuit
from qulacs import Observable
from qulacs.gate import DenseMatrix

# 量子状態の設定
# 量子ビット数3(0,1,2の3つ)
n = 3
state = QuantumState(n)
state.set_computational_basis(0b110)

# 入力状態を表示
print('initial state: ' + str(state.get_vector()))

# 回路にToffoliゲート追加
circuit = QuantumCircuit(n)
toffoli = [
    [1, 0, 0, 0, 0, 0, 0, 0],
    [0, 1, 0, 0, 0, 0, 0, 0],
    [0, 0, 1, 0, 0, 0, 0, 0],
    [0, 0, 0, 1, 0, 0, 0, 0],
    [0, 0, 0, 0, 1, 0, 0, 0],
    [0, 0, 0, 0, 0, 1, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 1],
    [0, 0, 0, 0, 0, 0, 1, 0]
]
gate = DenseMatrix([0, 1, 2], toffoli)
circuit.add_gate(gate)
circuit.update_quantum_state(state)

# オブザーバブルの設定
# 量子ビット数3
observable = Observable(n)
# 量子ビット2の上下を観測
# 何故か'Z 2'ではなく'Z 0'
observable.add_operator(1.0, 'Z 0')

# 量子ビット2の上下を観測
value = observable.get_expectation_value(state)
# 結果表示
value = (1.0 - value) / 2.0
print('|' + str(int(value)) + '>')
```