QAP.01:Hello,量子アニーリング【量子コンピュータ/アニーリング@Python/Fixstars Amplify】
【はじめに】
量子アニーリングによるプログラムの挙動をざっくりいうと、
というものである。
【例】「x + 2y - 3z」をできるだけ小さくする変数(x, y, z)の組み合わせ
目的の数式に対して変数(x, y, z)の0/1の組み合わせは様々あるが、結論としては(x, y, z)=(0, 0, 1)が最も小さい値となる組み合わせとなる。
こんな風に与えられた数式に対して、いい感じの答えとなる組み合わせを探す際に、量子アニーリングの仕組みをつかうのが量子アニーリングのプログラミングである。
もちろん、既存のアルゴリズム、ライブラリでも十分なことも多々ある。選択肢の一つとして「量子アニーリングによるやり方もあるよ」ぐらいの気持ちで考えておこう。
【実行環境について】
この連載では主に「Fixstars Amplify」や「D-Wave Leap」を「Google Colab」上で実行していく。(無料の範囲でやる)
■Fixstars Amplify
■D-Wave Leap
どちらを使うにしても、無料の範囲で登録してアクセストークンを取得しておく必要がある。
【例題】:x + 2y - 3zを最小にするx, y, zを探す
今回は「Fixstars Amplify」の基本的な使い方も兼ねて、
「Fixstars Amplify」+「Google Colab」でプログラミングしてみる。
【1】ライブラリのインストール(Colab上にインストール)
! pip install amplify
! pip install --upgrade amplify
【2】クライアントオブジェクトの生成
「FixstarsClientオブジェクト」を生成し、実行時の設定やトークンを設定する
from amplify.client import FixstarsClient
client = FixstarsClient()
client.parameters.timeout = 1000 # タイムアウトは1000ミリ秒(1秒)
client.parameters.outputs.duplicate = True # みつかった解が複数あってもOK
client.parameters.outputs.num_outputs = 0 # 0: 見つかった解を全て出力
# トークン設定
client.token = "XXXXXXXXXXXXXXXXXXXXXXX" # 無料登録して取得したトークンを指定する
※トークンは掲載上「XXX... ...XXX」としているが、実際は「Fixstars Amplify」に無料登録して取得したトークンを設定する。
【3】変数を用意する(イジング変数/QUBO変数)
「Fixstars Amplify」では組み合わせを見つけるための変数として「イジング変数」と「QUBO変数」を用意している。
今回は最初に挙げた例題をふまえて「QUBO変数」を用意することにする。
■QUBO変数の作り方
「SymbolGeneratorオブジェクト」経由で「BinaryPolyオブジェクト」を生成することで「QUBO変数」を用意できる。
from amplify import BinaryPoly
from amplify import SymbolGenerator
# 数式:x + 2y - 3zの係数部分相当
coef = [1, 2, -3]
gen = SymbolGenerator(BinaryPoly) # BinaryPolyを生成させる
q = gen.array(len(coef)) # BinaryPolyを指定の数だけ生成
print(q)
▲ q_0, q_1, q_2の3つのQUBO変数を生成できている。
※QUBO変数とイジング変数の相互変換
なお、「QUBO変数」と「イジング変数」は相互変換可能なので、どっちの変数でやっても実はOK。
【4】目的関数を作る
QUBO変数が用意できたので、
「目的関数(組み合わせを探す数式):x + 2y - 3z」を作る。
※今回は変数は「(x, y, z) = (q_0, q_1, q_2)」と対応させている。
数式は単純に表記通り記述すればいい。(※)
f = coef[0]*q[0] + coef[1]* q[1] + coef[2]*q[2]
print(f)
※実際にはfor文を使ったり、アインシュタイン縮約表記を使ったりする
# for文+sumを使う場合
f = sum(coef[i]*q[i] for i in range(len(coef)))
f
# アインシュタイン縮約表記を使う場合
from amplify import einsum
f = einsum("i,i",q,coef)
f
【5】Solverオブジェクトを生成して量子アニーリング計算をする
目的関数が完成したら、あとは量子アニーリング計算をさせるだけ。
「Fixstars Amplify」では「Solverオブジェクト(Client設定仕込み済み)」経由で「目的関数」を解かせる。
# Solverオブジェクト生成
from amplify import Solver
solver = Solver(client)
# 目的関数を与えて問題を解かせる
result = solver.solve(f)
【6】計算結果を確認する
答えが求められていれば、何らかの「SolverResultオブジェクト」が返ってきている。※イテレータで回せるようになっているようで、サンプルによると答えが求められているかどうかは「len()」でチェックするらしい。
# 計算結果があるかを確認
if len(result) == 0:
print("no answer")
▲今回は答えが求められるので何も表示されない想定。
問題によっては「複数の答え(条件に合う変数の組み合わせ)」がでることもある。そのため、計算結果はイテレータで順次取り出していく。
from amplify import decode_solution
for sol in result:
energy = sol.energy
values = sol.values
res = decode_solution(q, values)
print(f"energy = {energy}, {values} Result is {q} = {res}")
▲ 目的関数をできるだけ小さくしようとした結果でた答えが「energy」、その時の変数の組み合わせが「values」に格納されている。
なお、「values」は「dict型」として返ってきているが、QUBO変数の順番通りに並んでいるとは限らない。
そこで「Fixstars Amplify」には作成した「QUBO変数」と「計算したvalues」をいい感じに対応させて出力する「decode_solution()」というものがある。今回はこれを使って整形した。
【7】全体コード
最後にコード全体を示しておく。
【例題】
x + 2y - 3zを最小にするx, y, zを探す
【実行環境】
・Fixstars Amplify + Google Colab
■事前準備:ライブラリのインストール
! pip install amplify
! pip install --upgrade amplify
■ここからが全体コード
from amplify.client import FixstarsClient
from amplify import BinaryPoly
from amplify import SymbolGenerator
from amplify import Solver
from amplify import decode_solution
client = FixstarsClient()
client.parameters.timeout = 1000 # タイムアウトは1000ミリ秒(1秒)
client.parameters.outputs.duplicate = True # みつかった解が複数あってもOK
client.parameters.outputs.num_outputs = 0 # 0: 見つかった解を全て出力
# トークン設定
client.token = "XXXXXXXXXXXXXXXXXXXXXXX" # 無料登録して取得したトークンを指定する
# 係数
coef = [1, 2, -3]
# QUBO変数を3つ用意
gen = SymbolGenerator(BinaryPoly)
q = gen.array(len(coef))
print(q) # [q_0, q_1, q_2]
# 目的関数
f = coef[0]*q[0] + coef[1]* q[1] + coef[2]*q[2]
print(f) # q_0 + 2 q_1 - 3 q_2
# Solverを生成
solver = Solver(client)
# 目的関数を渡して計算させる
result = solver.solve(f)
# 結果の確認(回答有無チェック)
if len(result) == 0:
print("no answer")
# 答えを出力
for sol in result:
energy = sol.energy
values = sol.values
res=decode_solution(q, values)
print(f"energy = {energy}, {values} Result is {q} = {res}")
▲(x, y, z)= (q_0, q_1, q_2) = (0, 0, 1)の時に、値:-3.0という結果が返ってきた。