テンソルネットワークによる連続関数の最適化
この記事について
量子コンピューター Advent Calendar 2023にて投稿させて頂いた記事になります。
TTOpt
本日はTTOptというある連続関数の最小値/最大値を求めるソフトウェアを少し紹介したいと思います。
TTOptの背景にある技術として、quantized (quantics) tensor train / tensor cross interpolationをまずは簡単に説明したいと思います。
はじめに
本記事でのテンソルの定義は、ただの多次元配列であることに注意してください。
Quantized (Quantics) tensor train (QTT)
簡単のため
はじめに連続変数xの離散化するために、等間隔にメッシュを用いる。そのように離散化された
ここで、
メッシュ上の関数値
として表され、これは、
ここでQTTでは、次の仮定をおくことで圧縮を行う。それは、同程度の長さスケール間の相関が強く、 指数関数的に異なる長さスケール間の相関が弱いという仮定です。この相関構造を自然に満たす(テンソルネットワークの1種が行列積状態/Matrix Product State(MPS) になります。また、多変数関数への拡張も同様に可能です。
Tensor cross interpolation (TCI)
tensor cross interpolation (TCI)は、多次元空間上の点をサンプルしてきて、元々の関数を行列積状態へと近似を行う技術です。ここで、関数に関連するテンソルが低ランク構造を持つことを仮定しています。
まずは簡単のため行列の圧縮について見ていきたいと思います。これをMatrix Cross Interpolation(MCI)と呼びます。
MCIのプロセスは、元々の行列
ここで、
(以下の図は、こちらの論文のFigure.2を参考に作りました。)
この近似には次の二つの重要な性質を持つことが知られています:
- 内挿公式である。選んだ行と列の交差点上で、元の行列と近似行列の要素が等しくなる。
- もし、元の行列のランクと同じ数の行と列を選ぶと、近似行列は元の行列と等しくなる。
ここで、どのように行と列を選ぶのかという問題が起こる。愚直に最適な行と列の組みを探そうとすると、「組合せ爆発」を起こしてします。それを回避するために、いくつかのヒューリスティックな方法が提案されています。その中の一つにmaxvolアルゴリズムがあります。これはある行列の行と列を選択し、その行列式の絶対値(|det
このアルゴリズムはテンソルに拡張することができ、それが「tensor cross interpolation」と呼ばれています。詳細については原論文を参照。
しかし通常のmaxvolでは、絶対値の大きな要素を探すので符号までは考慮しません。これでは、連続関数の最小値最大値問題を解きづらくなってしまいます。そこで論文では、TCIの内部で
実際に使ってみる。
以下のソースコードは、TTOptのgithubのnotebookのqtt.pyから持ってきました。
ここでは答えが厳密にわかる関数
TTOptでは、最初に最大のボンド次元(=rank)を指定する必要があります。
ここでは、rank=2としています(どのような値を設定すれば事前にわからないのが難点かもしれません)
import numpy as np
from ttopt import TTOpt
from ttopt import ttopt_init
np.random.seed(42)
d = 1 # Number of function dimensions:
rank = 2 # Maximum TT-rank while cross-like iterations
def f(X): # Target function
#return np.sin(X) -10
return (X-1)**2 * ((X+1)**2 + 0.01)
#return np.sum(np.abs(X * np.sin(X) + 0.1 * X), axis=1)
# We initialize the TTOpt class instance with the correct parameters:
tto = TTOpt(
f=f, # Function for minimization. X is [samples, dim]
d=d, # Number of function dimensions
a=-10., # Grid lower bound (number or list of len d)
b=+10., # Grid upper bound (number or list of len d)
p=2, # The grid size factor (there will n=p^q points)
q=10, # The grid size factor (there will n=p^q points)
evals=1.E+5, ) # Number of function evaluations
#name='Alpine', # Function name for log (this is optional)
#x_opt_real=np.ones(d), # Real value of x-minima (x; this is for test)
#y_opt_real=0., # Real value of y-minima (y=f(x); this is for test)
#with_log=True)
# And now we launching the minimizer:
tto.optimize(rank)
# We can extract the results of the computation:
x = tto.x_opt # The found value of the minimum of the function (x)
y = tto.y_opt # The found value of the minimum of the function (y=f(x))
k_c = tto.k_cache # Total number of cache usage (should be 0 in this demo)
k_e = tto.k_evals # Total number of requests to func (is always = evals)
k_t = tto.k_total # Total number of requests (k_cache + k_evals)
t_f = tto.t_evals_mean # Average time spent to real function call for 1 point
# ... (see "ttopt.py" and docs for more details)
print(x)
print(y)
# We log the final state:
#print('-' * 70 + '\n' + tto.info() +'\n\n')
実行すると正しく解が得られていることがわかりました。今回は1変数関数の自明な例でしたが、多変数関数の例や人工的な関数などに適用するなど色々と遊んでみると面白いかもしれません。
TToptを使った応用研究
量子アニーリングとTTOptを比較している論文
VQEの最適化にTTOptを使用
Discussion