Solver

このセクションでは論理模型のソルバーについて説明します。

ソルバーオブジェクトの構築

ソルバークラスはイジングマシンのクライアントを内包することでハードウェアを抽象化し、入力模型・論理模型を解くためのインターフェースを提供します。ソルバークラスを用いる事で様々なイジングマシンに対して仕様の差異を意識する必要がなくなり、統一的なインターフェースでマシンを実行し解を求めることが可能になります。

ソルバーはクライアントオブジェクトを用いて次のように構築されます。

from amplify import BinaryPoly, gen_symbols, Solver
from amplify.client import FixstarsClient

client = FixstarsClient()
client.url = "http://xxx.xxx.xxx.xxx"
client.token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
client.parameters.timeout = 1000  # タイムアウト1秒

solver = Solver(client)

後からクライアントオブジェクトを与えることも出来ます。

from amplify import Solver
from amplify.client import FixstarsClient

client = FixstarsClient()
client.url = "http://xxx.xxx.xxx.xxx"
client.token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
client.parameters.timeout = 1000  # タイムアウト1秒

solver = Solver()
solver.client = client

ソルバーの実行と解の取得

solve() メソッドに入力模型や論理模型を与えることでマシンを実行できます。

入力模型

入力模型については次の入力が可能です。行列オブジェクトの入力に対して第二引数は定数項を表します。

Note

クライアントクラスの solve() メソッドとは異なり三次以上の多項式の入力が可能です。

論理模型

論理模型については次の入力が可能です。

実行例

solve() メソッドの実行結果は下記のアトリビュートを持つオブジェクトです。

  • solutions : 実行結果のリストを取得します。各要素は以下のアトリビュートを持ちます。

    • energy : エネルギー値 (入力模型の評価値) を取得します。

    • values : 入力変数の値をリストで取得します。

    • frequency : 同一の解の個数を取得します。

from amplify import BinaryPoly, gen_symbols, Solver
from amplify.client import FixstarsClient

q = gen_symbols(BinaryPoly, 8)
f = 2 * q[0] * q[1] * q[2] - q[0] * q[1] + q[2] + 1

client = FixstarsClient()
client.url = "http://xxx.xxx.xxx.xxx"
client.token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
client.parameters.timeout = 1000  # タイムアウト1秒

solver = Solver(client)
result = solver.solve(f)
>>> f
2.000000 q_0 q_1 q_2 - q_0 q_1 + q_2 + 1.000000
>>> [f"energy = {s.energy}, q[0] = {s.values[0]}, q[1] = {s.values[1]}, q[2] = {s.values[2]}" for s in result.solutions]
['energy = 0.0, q[0] = 1, q[1] = 1, q[2] = 0']

制約条件によるフィルタ

solve() メソッドに制約条件を与えた場合には、自動的に制約条件を満たさない解をフィルタします。次の例は q[0] + q[1] == 1 となる制約を課しています。

from amplify import BinaryPoly, gen_symbols, Solver
from amplify.client import FixstarsClient

q = gen_symbols(BinaryPoly, 8)
f = 2 * q[0] * q[1] * q[2] - q[0] * q[1] + q[2] + 1
c = equal_to(q[0] + q[1], 1)  # 制約: q[0] + q[1] == 1

client = FixstarsClient()
client.url = "http://xxx.xxx.xxx.xxx"
client.token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
client.parameters.timeout = 1000  # タイムアウト1秒

solver = Solver(client)
result = solver.solve(f + c)
>>> f
2.000000 q_0 q_1 q_2 - q_0 q_1 + q_2 + 1.000000
>>> [f"energy = {s.energy}, q[0] = {s.values[0]}, q[1] = {s.values[1]}, q[2] = {s.values[2]}" for s in result.solutions]
['energy = 1.0, q[0] = 0, q[1] = 1, q[2] = 0']

制約を満たす解が得られなかった場合は、実行結果の solutions は空のリストになります。次の例では矛盾する制約を課すことでこの様子を確認しています。

from amplify import BinaryPoly, gen_symbols, Solver
from amplify.client import FixstarsClient

q = gen_symbols(BinaryPoly, 8)
f = 2 * q[0] * q[1] * q[2] - q[0] * q[1] + q[2] + 1
c1 = equal_to(q[0] + q[1], 1)  # 制約: q[0] + q[1] == 1
c2 = equal_to(q[0] + q[1], 2)  # 制約: q[0] + q[1] == 2 (c1と矛盾)

client = FixstarsClient()
client.url = "http://xxx.xxx.xxx.xxx"
client.token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
client.parameters.timeout = 1000  # タイムアウト1秒

solver = Solver(client)
result = solver.solve(f + c1 + c2)
>>> f
2.000000 q_0 q_1 q_2 - q_0 q_1 + q_2 + 1.000000
>>> [f"energy = {s.energy}, q[0] = {s.values[0]}, q[1] = {s.values[1]}, q[2] = {s.values[2]}" for s in result.solutions]
[]

ソルバークラスの内部

ソルバークラスの内部では次の手順でマシンを実行し解を出力します。

  1. ソルバークラスにイジングマシンのクライアントオブジェクトを与える

  2. 入力模型または論理模型を受けとる

  3. ハードウェア仕様に基づき論理模型を物理模型に変換

    • 疎結合グラフを持つマシンに対してはグラフ埋め込み

  4. 物理模型をクライアントに与えて実行

  5. 実行結果である物理模型の解を論理模型の解に変換

    • グラフ埋め込みによる物理変数制約を解決

  6. 論理模型の解から制約条件をチェックしフィルタ

  7. フィルタされた論理模型の解を入力模型の解に変換

このうち、クライアントが返却する「物理模型の解」と、制約条件をフィルタする前の「論理模型の解」については、次のようにして solve() メソッドの実行後に取得が可能です。

from amplify import BinaryPoly, gen_symbols, Solver
from amplify.constraint import equal_to
from amplify.client import DWaveClient

q = gen_symbols(BinaryPoly, 8)
f = 2 * q[0] * q[1] * q[2] - q[0] * q[1] + q[2] + 1
c = equal_to(q[0] + q[1], 1)  # 制約: q[0] + q[1] == 1

client = DWaveClient()
client.token = "XXXX-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
client.parameters.num_reads = 100  # 実行回数

solver = Solver(client)
solver_result = solver.solve(f + c)
logical_result = solver.logical_result
client_result = solver.client_result
>>> [f"energy = {s.energy}, client_result  = {s.values}" for s in client_result.solutions[:1]]
['energy = 1.0, client_result  = [0, 0, 1, 0, 0, 0, 1, 0, 3, 3, 3, ...]']
>>> [f"energy = {s.energy}, logical_result = {s.values}" for s in logical_result.solutions[:1]]
['energy = 1.0, logical_result = [0, 0, 1, 0]']
>>> [f"energy = {s.energy}, solver_result  = {s.values}" for s in solver_result.solutions[:1]]
['energy = 1.0, solver_result  = [0, 1, 0]']

WIP

出力解の途中経過を取得するインターフェースは今後変更される可能性があります。