量子回路学習のモデル表現力をFourier展開で捉える

# 量子回路学習のモデル表現力

量子回路学習におけるモデル表現力（どんな関数を近似できうるか？）は、なかなか難しい問題です。
明らかに、量子ビット数が増えるほど、量子回路の層が深くなるほど多様な関数を近似できそうに思えます。[^1]
表現力については素晴らしい論文があり、PennyLaneのデモでも紹介されています。
https://pennylane.ai/qml/demos/tutorial_expressivity_fourier_series.html

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/95627372-487b-00b6-a37f-c89a5f7d0fe7.png)

- 表現力は$\sin$の次数(Fourier展開したときの周波数)で書ける
- 量子ビット数を増やす(parallel)のと層数を増やす(serial)のは、表現力としては同じことになる[^2]
- 特に、$r$ qubit または　$r$層　の回路では$\sin(rx)$までが近似できる。

という、驚きの結論です。
手を動かしてみます。

# 量子ビット数依存性

## コード

```python
import pennylane as qml
from pennylane.templates import StronglyEntanglingLayers
from pennylane import numpy as np
import matplotlib.pyplot as plt 

degree = 2  # degree of the target function
scaling = 1  # scaling of the data
coeffs = [0.15 + 0.15j]*degree  # coefficients of non-zero frequencies
coeff0 = 0.1  # coefficient of zero frequency

def target_function(x):
    """Generate a truncated Fourier series, where the data gets re-scaled."""
    res = coeff0
    for idx, coeff in enumerate(coeffs):
        exponent = np.complex(0, scaling * (idx+1) * x)
        conj_coeff = np.conjugate(coeff)
        res += coeff * np.exp(exponent) + conj_coeff * np.exp(-exponent)
    return np.real(res)

def square_loss(targets, predictions):
    loss = 0
    for t, p in zip(targets, predictions):
        loss += (t - p) ** 2
    loss = loss / len(targets)
    return 0.5*loss

x = np.linspace(-6, 6, 70, requires_grad=False)
target_y = np.array([target_function(x_) for x_ in x])

plt.plot(x, target_y, c='black')
plt.scatter(x, target_y, facecolor='white', edgecolor='black')
plt.ylim(-1, 1)
plt.show();



scaling = 1
r = 1

dev = qml.device('default.qubit', wires=r)

def S(x):
    """Data-encoding circuit block."""
    for w in range(r):
        qml.RX(scaling * x, wires=w)

def W(theta):
    """Trainable circuit block."""
    StronglyEntanglingLayers(theta, wires=range(r))


@qml.qnode(dev)
def parallel_quantum_model(weights, x):

    W(weights[0])
    S(x)
    W(weights[1])
    
    return qml.expval(qml.PauliZ(wires=0))



trainable_block_layers = 3
weights = 2 * np.pi * np.random.random(size=(2, trainable_block_layers, r, 3))

x = np.linspace(-6, 6, 70, requires_grad=False)
random_quantum_model_y = [parallel_quantum_model(weights, x_) for x_ in x]

plt.plot(x, random_quantum_model_y, c='blue')
plt.ylim(-1,1)
plt.show();

def cost(weights, x, y):
    predictions = [parallel_quantum_model(weights, x_) for x_ in x]
    return square_loss(y, predictions)

max_steps = 50
opt = qml.AdamOptimizer(0.3)
batch_size = 25
cst = [cost(weights, x, target_y)]  # initial cost

for step in range(max_steps):

    # select batch of data
    batch_index = np.random.randint(0, len(x), (batch_size,))
    x_batch = x[batch_index]
    y_batch = target_y[batch_index]

    # update the weights by one optimizer step
    weights = opt.step(lambda w: cost(w, x_batch, y_batch), weights)

    # save, and possibly print, the current cost
    c = cost(weights, x, target_y)
    cst.append(c)
    if (step + 1) % 10 == 0:
        print("Cost at step {0:3}: {1}".format(step + 1, c))

predictions = [parallel_quantum_model(weights, x_) for x_ in x]

plt.plot(x, target_y, c='black')
plt.scatter(x, target_y, facecolor='white', edgecolor='black')
plt.plot(x, predictions, c='blue')
plt.ylim(-1,1)
plt.show();

```

degree というパラメータが$\sin$の次数です。
rというのが量子ビット数です。


## 回路図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/c267eae1-81e8-b17e-e97b-2f0f6ee196f5.png)

S(x)がデータxのエンコード、Wが変分ゲートです。
Wには十分な表現力をもたせるように、パラメータ数は多めに入れています。

## 結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878664/91395dfd-78f0-08f7-3ca3-60cb946e79d6.png)


1 qubit の場合は、$\sin(x)$は近似できますが、$\sin(2x)$は近似できません。
2 qubit の場合は、$\sin(2x)$まで近似できますが、$\sin(3x)$はできません。

確かに、qubit数と近似の次数が対応しています！

層数についても同じ動きをすると思いますが、まだうまく試せていません。

# 結論

量子回路学習のモデル表現力について、量子ビット数と層数がFourier展開の次数を決定する。


[^1]: ただし、BP問題と呼ばれる勾配消失（のようなもの）が起きやすくなります。
[^2]: ただし”層”は、変分ゲート＋入力データのエンコードゲート　のセットとします。つまり2層ならデータを二度エンコードすることになります。これをdata re-uploading といいます。実は、「データのエンコードを何回したか」だけで表現力(の$\sin$次数は)決まっていると思えます。