TensorFlow Quantumで量子回路学習をやる 

TensorFlowQuantumが公開されたので早速遊んでみた。
[tutorials](https://github.com/tensorflow/quantum/tree/master/docs/tutorials)にmnistの識別のサンプルコードがあったので(手書き数字の識別と言っていいのか?ってくらい元データをダウンスケールしているが、、、そのため別の題材で識別問題に挑戦中)、それを参考に、TensorFlowQuantumを使って回帰問題に取り組んでみた。コードは[github.com/koke-saka/quantum-machine-learning](https://github.com/koke-saka/quantum-machine-learning/blob/master/tf-quantum/QuantumCircuitLearning-TFQ.ipynb)

機械学習については素人のため、機械学習的に危ういところがあったら、優しく指摘してほしい。

# QuantumCircuitLearning
量子回路の構造を制限することで勾配計算を可能にした[QuantumCircuitLearning](https://arxiv.org/abs/1803.00745)の実装が[QuantumNativeDojo](https://dojo.qulacs.org/ja/latest/notebooks/5.2_Quantum_Circuit_Learning.html)にあり、それを参考に三角関数の学習をTensorFlowQuantumでやってみる。

# 学習データの準備

詳細は[github.com/koke-saka/quantum-machine-learning](https://github.com/koke-saka/quantum-machine-learning/blob/master/tf-quantum/QuantumCircuitLearning-TFQ.ipynb)へ。
とりあえず200個のサンプルを用意し、ランダムに１５０個を訓練セット、５０個をテストセットに振り分けた。

# 入力状態の作成
mnistの学習ではスレッショルドをひいて画像を２値画像にしてそれをqubitの0,1に対応させていた。（正確には1の場合にXゲートをかましていた）
ここでは[QuantumNativeDojo](https://dojo.qulacs.org/ja/latest/notebooks/5.2_Quantum_Circuit_Learning.html)にならい $\arcsin(x)$ と$\arccos(x^2)$をそれぞれRY、RXゲートの回転角に対応させて、入力状態のエンコードを行った。TensorFlowQuantumではどうやら予めその状態を準備しておくようだ。(Qulacsでは逐一回路を作り直していた気がする。)

```python
def convert_to_circuit(x):
    """Encode truncated classical image into quantum datapoint."""
    y = np.arcsin(x)
    z = np.arccos(x**2)
    qubits = cirq.GridQubit.rect(5, 1)
    circuit = cirq.Circuit()
    for i in range(5):
        circuit.append(cirq.ry(y).on(qubits[i]))
        circuit.append(cirq.rz(z).on(qubits[i]))
    return circuit


x_train_circ = [convert_to_circuit(x) for x in x_train]
x_test_circ = [convert_to_circuit(x) for x in x_test]
x_train_tfcirc = tfq.convert_to_tensor(x_train_circ)
x_test_tfcirc = tfq.convert_to_tensor(x_test_circ)
```

`convert_to_circuit()`は自前で用意する必要がある。[tutorial/mnist](https://github.com/tensorflow/quantum/blob/master/docs/tutorials/mnist.ipynb)の該当部分を見ると理解が深まると思う。
`convert_to_tensor()`したやつを今後の学習で用いる。

# パラメトリック量子回路（ニューラルネットワーク）の作成

## 未解決
readoutとdataqubitsは別々に用意する必要があるのか不明。
QuantumNativeDojoのように出力量子ビットにも入力をエンコードしたいのだが、今の所できないでいる。

## とりあえずの実装

```python
def create_quantum_model(c_depth=3):
    data_qubits = cirq.GridQubit.rect(5,1)
    readout = cirq.GridQubit(-1,-1)
    circuit = cirq.Circuit()

    
    builder = CircuitLayerBuilder(
        data_qubits = data_qubits,
        readout = readout
    )
    
    for i in range(c_depth):
        builder.add_entangler(circuit,5)
        builder.add_layer(circuit, gate = cirq.XX, prefix='xx'+str(i))
        builder.add_layer(circuit, gate = cirq.ZZ, prefix='zz'+str(i))
        builder.add_layer(circuit, gate = cirq.XX, prefix='xx1'+str(i))
    
    return circuit, cirq.Z(readout)

model_circuit, model_readout = create_quantum_model()
```
クラス`CircuitLayerBuilder`については[github](https://github.com/koke-saka/quantum-machine-learning/blob/master/tf-quantum/QuantumCircuitLearning-TFQ.ipynb)を参照。隣接する量子ビットにCZゲートをかます`add_entangler`を自前で用意。

こんな感じで量子回路ができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592741/89180919-a5cb-4122-f03e-560260bde95b.png)

# モデルの作成、学習
モデルを作成する準備が整った。モデルを作って学習させてみよう。
回帰問題のため、損失関数はmseにした。

```python
model = tf.keras.Sequential([
    # The input is the data-circuit, encoded as a tf.string
    tf.keras.layers.Input(shape=(), dtype=tf.string),
    # The PQC layer returns the expected value of the readout gate, range [-1,1].
    tfq.layers.PQC(model_circuit, model_readout),
])

model.compile(
    loss=tf.keras.losses.mse,
    optimizer=tf.keras.optimizers.Adam(),
    metrics=['mae'])

qnn_history = model.fit(
      x_train_tfcirc, y_train,
      batch_size=25,
      epochs=EPOCHS,
      verbose=1,
      validation_data=(x_test_tfcirc,y_test)
)
```

テストセットと予測値の比較が下の図である。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592741/2c062a46-a290-dcf9-83a1-54f384cdfffc.png)

なんとなく学習できているようだが、最大最小が小さく出ているのが気になる。readoutはパウリのZ行列の期待値なので１から-1の間を取りうるはずなのだが、、

# 終わりに
とりあえず、TensorFlowQuantumで遊ぶことはできた。不具合の原因は今後考えることにする。TensorFlowQuantum触ってみるかってなる人が増えればと嬉しい。