日立

Hitachi Annealing Cloud Web

日立 の提供する CMOS アニーリングマシンです。

ソルバー仕様:

バイナリ変数

イジング変数

整数変数

実数変数

目的関数

-

2次

-

-

等式制約

-

-

-

-

不等式制約

-

-

-

-

注釈

処理を実行できるマシンは以下の 3 種類があり、type で指定できます。 (デフォルト:type = 4)

  • type = 3: GPU 32bit (int)

    • 256k スピン (512×512 の King's graph に相当) を処理可能

  • type = 4: GPU 32bit (float)

    • 256k スピン (512×512 の King's graph に相当) を処理可能

  • type = 5: ASIC 4bit

    • 147,456 スピン (384×384 の King's graph に相当) を処理可能

type = 3, 5 のマシンは整数係数のモデルしか扱えません。それぞれのタイプで扱える整数値の範囲については API リファレンス を参照してください。ただし、もし目的関数が整数係数であっても、Amplify SDK は amplify.solve() 関数において、モデル変換とグラフ埋め込み処理が行われるため、マシンに渡されるモデルには結果として小数が含まれることがあります。そのような場合 Amplify SDK 内部では符号に応じた無限大方向への切り上げによって整数化するため、必ずしも意図通りの定式化とはならないことに注意してください。 これらのマシンを使用する場合には、直接 amplify.HitachiClient.solve() を用いて実行することを推奨します (こちらを参照してください)。

設定例:
from amplify import HitachiClient

client = HitachiClient()

# API トークンを設定
client.token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# アニーリング実行回数を 3 に設定
client.parameters.num_executions = 3

# スピンのサイトあたりの平均値を出力する
client.parameters.outputs.averaged_spins = True

# エネルギーの平均値を出力する
client.parameters.outputs.averaged_energy = True
実行例:
from amplify import VariableGenerator, Model, solve

# 決定変数と目的関数を定義
g = VariableGenerator()
s = g.array("Ising", 2)
f = s[0] * s[1] + s[0] - s[1] + 1

# モデルを作成
model = Model(f)

# ソルバーを実行
result = solve(model, client)

ソルバーのバージョンを取得:

>>> client.version
'v2'

詳細な実行結果を取得:

>>> result.client_result.result
HitachiClient.Result.Result({
"execution_time": 185832138,
"energies": [
    -3.0,
    -3.0,
    -3.0
],
"spins": [
    [[0, 0, -1], [1, 0, 1]], [[0, 0, -1], [1, 0, 1]], ...
],
"averaged_energy": -3.0,
"averaged_spins": [
    [1, 0, 1], [0, 0, -1]
]
})

クライアントインターフェースを使用して実行する

amplify.HitachiClient.solve() メソッドを使用して、ソルバーの API を直接利用して問題を送信し、結果を取得することができます。この方法は、日立 CMOS アニーリングマシンのチップのトポロジーに最適化された問題や、グラフ埋め込みを自身で行って問題を解く際に有用です。

CMOS アニーリングマシンの API では、King's graph 上の座標 \((x, y)\) でイジング変数を定義します。一方で、Amplify SDK では変数を ID (id) で管理するため、amplify.HitachiClient クラスでは、King's graph 上の座標との対応関係を id = x + y * graph_size として与えることにしています。

使用する CMOS アニーリングマシンの type に対応した graph_size を用いると、この対応関係を満たした変数を以下のように発行することができます。

from amplify import VariableGenerator, HitachiClient

client = HitachiClient()
client.parameters.type = 3
client.token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
graph_size = client.graph.shape[0]  # グラフの一辺のサイズ

g = VariableGenerator()
s = g.array("Ising", (graph_size, graph_size))  # graph_size x graph_size の King's graph 上のイジング変数を生成

生成したイジング変数配列 s は King's graph 上の座標と配列インデックスが対応します。例えば s[x, y] は King's graph 上の座標 \((x, y)\) に対応するイジング変数を表します。

次に、以下を満たす目的関数 Poly を作成します。

  • 二次以下のイジング変数のみで構成された多項式である

  • 制約条件はあらかじめペナルティ関数として目的関数に含める

  • 係数に指定出来る値の種類と範囲に注意する (API リファレンス を参照)

  • 二次項に含まれる 2 つのイジング変数のインデックスは二次元配列上で上下左右斜めのいずれかで隣接している (King's graph 上で隣接している)

例えば以下のような目的関数は上記の条件を満たします。

f = s[0, 0] * s[0, 1] + 2 * s[0, 0] * s[1, 0] - s[0, 1] * s[1, 0] - s[1, 0] * s[1, 1] + 1

作成した目的関数を amplify.HitachiClient.solve() メソッドに与えることで、ソルバーが実行され、レスポンスが返却されます。

>>> res = client.solve(f)
>>> res.result.energies
array([-5.])
>>> res.result.spins
[[(0, 0, 1), (1, 0, -1), (0, 1, -1), (1, 1, -1)]]

ソルバーから返却された各イジング変数の解 \(s'\)spins[[(x, y, s'), ...], ...] という形式で格納されています。ここで s' は King's graph 上の座標 \((x, y)\) におけるイジング変数の値を表します。目的関数に使用されなかった変数については値が返却されないことに注意してください。複数の解が返却されることがあるため、タプルのリストのリストとして格納されています。

変数配列 s と同様の形状で、King's graph 上の座標 \((x, y)\) におけるイジング変数の解を取得するには以下のようにします。

import numpy as np

# graph_size x graph_size のゼロ配列を生成
spin_values = np.zeros((graph_size, graph_size))

# インデックス 0 の結果から解を代入
# (複数の解が返却される場合は適宜インデックスを指定する)
for x, y, s in res.result.spins[0]:
    spin_values[x, y] = s