見出し画像

QAP.01:Hello,量子アニーリング【量子コンピュータ/アニーリング@Python/Fixstars Amplify】

【はじめに】

量子アニーリングによるプログラムの挙動をざっくりいうと、

与えられた数式(+制約条件)の中で、その答えをできるだけ小さくする変数の組み合わせを見つける。

というものである。


【例】「x + 2y - 3z」をできるだけ小さくする変数(x, y, z)の組み合わせ

変数:x、y、z」を使って「数式:x + 2y - 3z」の計算結果を考えてみる。ただし、変数のとりうる値は「0」か「1」とする。

目的の数式に対して変数(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変数」を用意している。

・イジング変数:「-1」 または「1」をとる
・QUBO変数:「0」または「1」をとる

今回は最初に挙げた例題をふまえて「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]

▲ q_0, q_1, q_2の3つのQUBO変数を生成できている。

※QUBO変数とイジング変数の相互変換
なお、「QUBO変数」と「イジング変数」は相互変換可能なので、どっちの変数でやっても実はOK。

画像2

【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)

【ここまでの実行結果】
q_0 + 2 q_1 - 3 q_2

※実際には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」に格納されている。

【実行結果】
energy = -3.0, {2: 1, 1: 0, 0: 0} Result is [q_0, q_1, q_2] = [0. 0. 1.]
▲(q_0, q_1, q_2)=(0, 0, 1)で値 -3.0が答えとして返ってきた(正解)

なお、「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}") 

【実行結果】
energy = -3.0, {2: 1, 1: 0, 0: 0} Result is [q_0, q_1, q_2] = [0. 0. 1.]

▲(x, y, z)= (q_0, q_1, q_2) = (0, 0, 1)の時に、値:-3.0という結果が返ってきた。



もっと応援したいなと思っていただけた場合、よろしければサポートをおねがいします。いただいたサポートは活動費に使わせていただきます。