前回は量子回路をテンソルとして扱うことを最後に述べました。テンソルとして扱う際には実際の量子コンピュータと同様で限定された接続や量子もつれといった現実的な考え方を扱うことが多くなります。
理想的な量子コンピュータは自由に量子もつれを扱って、QPEやQAOA(Adiabatic Algorithm)のようなFTQC向けの考え方を接続の多い量子もつれ状態を考える場合には、状態ベクトルを中心に考えても良いと思います。
一方で接続が限定されている、量子ビットが多い、誤り訂正がない、などのNISQからFTQCヘ向かう段階のマシンとしてはテンソルネットワークの方がその扱いが近く、より大きな量子ビットやアプリケーションの開発に寄与できそうです。
ここでは、量子古典ハイブリッドの量子機械学習の仕組みを見ます。QAOAのようなAnsatzが量子断熱計算に則っているような計算は限定的な接続の量子コンピュータでは満足にその理論を活かすことができないと考えられます。
パラメータ化量子回路と量子古典ハイブリッド機械学習の概要
パラメータを量子回路に導入した量子機械学習は量子古典ハイブリッド機械学習の基本的な考え方です。
1、データ入力層(量子)
2、パラメータ層(量子)
3、測定・期待値・損失関数(古典)
4、最適化とパラメータ更新(古典)
主にこれら4つのステップで構成されます。
古典機械学習と異なる代表的な部分は、
1、データ入力を量子ゲートを利用して行う
2、パラメータ層も量子ゲートで構築を行う。量子ビット数は増減ができない。
3、計算結果は確率的になっているため、測定を通じて得た計算結果から期待値を求めて損失関数へ。
4、勾配計算では有限差分法の他に量子特有の手法もある。
などとなります。
データ入力
データの入力には量子ゲートを利用します。量子ゲートを利用して行える量子状態の生成には複数のエンコーディング方法がありますが、今回はスタンダードな任意回転角度に規格化して埋め込む方法を紹介します。
とても簡単で、RYゲートやRXゲートなどは任意回転ゲートといって、好きな角度を埋め込めます。その中にデータを入れます。
PQC層
変分層は適当です。RXとかRZとか1量子ビットゲートを用意し、その後にそれっぽくCXとかで繋ぎます。
複数量子ビットある時のCXの繋ぎ方ですが、これもいろいろあります。ループで繋ぐ場合、全結合にする場合、隣接するところだけ繋ぐとか、ハシゴやアミダにするとかあります。
測定
量子コンピュータは計算するたびに確率的に解が出ます。上記のデータ入力とPQC層を経て得られる答えは複雑な量子重ね合わせや量子もつれを経て得られます。そのため毎回答えが異なります。これらの答えを一回で損失関数に入れることはできません。
期待値
測定だけでは損失関数に値を入れられませんので何かしたら安定した指標が必要です。そこで期待値を利用します。期待値は状態ベクトル|psi>とハミルトニアンHを使って、< psi | H | psi > を計算すれば出ます。ただ、量子状態を出すことは難しいので、サンプリングを利用して求めます。
一般的にハミルトニアンは測定する軸を表しており、通常はZで測定をするので、H = Zを使います。量子状態は1量子ビットに対して、| psi > = [a,b]と表せますので、E = < psi | H | psi > を計算して、E = [a, b]* @ Z @ [a, b] = |a|**2 - |b|**2 となります。( (0が出た回数) - (1が出た回数) ) / (全実行回数)となります。この値は理想的には一定になります。
テンソルネットワークの場合には、より直感的に期待値を求めることができます。
損失関数
上記期待値をベースに損失関数を使って誤差計算などできます。MSEやRMSEなどが利用できます。
E = 1/n sum (y' - y)**2
最適化
最適化は損失関数のパラメータに対する微分係数を用いて行います。最適化は最急降下法などを利用します。
x' = x - alpha * grad(f(x))
微分の取り方についても注意する必要があり、有限差分法やパラメータシフト法などがあります。
まとめ
一連の手順を利用することでパラメータ化量子回路を利用した量子機械学習を利用することができます。
実習
今回は y = sin(x)のグラフを学習する量子回路を1量子ビットで実現してみます。
学習データとラベルの準備
今回はx = 0 から +2piまでの角度でy = sin(x)の値を準備します。chatGPTに聞きます。
「pythonでxの値を0から+2piまでの一定間隔で配列格納し、別の変数にy=sin(x)の値を格納したい。」
import numpy as np
0から+2piまでの区間を、100個の等分点で分割した数列を生成する場合
x = np.linspace(0, 2*np.pi, 100)
sin関数を適用してyを計算する
y = np.sin(x)
入力回路
次に量子回路を準備します。
from blueqat import Circuit
#量子回路とパラメータを受け取って、RXで入力します。
def circ_input(x_input):
return Circuit().rx(x_input)[0]
PQC回路の作成
次にPQC回路を作成します。量子回路とパラメータから機械的に作成します。
def circ_pqc(param):
return Circuit().rx(param[0])[0].ry(param[1])[0].rz(param[2])[0]
測定の準備。ハミルトニアンの設定。
ハミルトニアンはZを使います。測定をして期待値を求めますが、今回はTN回路が実装されていますので、ハミルトニアンを指定して実行。
from blueqat.pauli import Z
hamiltonian = 1*Z(0)
def circ_measure(circ, hamiltonian):
return circ.run(hamiltonian = hamiltonian)
ラベルとの誤差を評価
今回は純粋に二乗誤差を求める
def loss(expt, y_input):
return (expt - y_input)**2
量子回路をまとめる
全部まとめます
def circ_all(x_input, params, y_input):
circ = (circ_input(x_input) + circ_pqc(params))
expt = circ_measure(circ, hamiltonian)
ls = loss(expt, y_input)
return circ, expt, ls
パラメータを初期化
初期化します。
param_init = [np.random.rand()*2*np.pi for i in range(3)]
result = circ_all(x[0], param_init, y[0])
result[0].run(backend="draw")
Yの値を図示
y_temp = []
for i in range(len(x)):
result = circ_all(x[i], param, y[i])
y_temp.append(result[1])
import matplotlib.pyplot as plt
plt.plot(x, y)
plt.plot(x, y_temp)
plt.show()
勾配を計算して更新する
ある点をランダムに選択し、勾配を計算して更新します。
#前方差分 (f(x+h) - f(x)) / h
#学習率e
h = 0.01
e = 0.1
for i in range(100):
k = np.random.randint(0,100)
result = circ_all(x[k], param, y[k])
loss_base = result[2]
#print(loss_base)
loss_temp = np.zeros(3)
for j in range(3):
param_temp = param.copy()
#print(param_temp)
param_temp[j] += h
#print(param_temp)
result\_temp = circ\_all(x\[k\], param\_temp, y\[k\])
loss\_temp\[j\] = result\_temp\[2\]
#print(result\_temp)
for j in range(3):
param[j] -= (loss_temp[j] - loss_base)/h * e
最終の図示
y_final = []
for i in range(len(x)):
result = circ_all(x[i], param, y[i])
y_final.append(result[1])
plt.plot(x, y)
plt.plot(x, y_temp)
plt.plot(x,y_final)
plt.show()
きちんと学習できました。もちょっと頑張るとピッタリ重なります。途中過程も貼っておきます。