AmplifyのモデルをOpenJijのSASamplerに投入する

KenT

TL;DR

Amplifyはイジングマシンを手軽に扱うことのできるSDKです。

イジングマシンを使用するとき、古典マシン(一般的なCPU)でのシミュレーションと性能を比較したいケースが多々あります。
構築したイジングモデルに対してシミュレーテッドアニーリング (SA)でさくっと最適化を試したいなどが想定されます。

D-Wave Ocean SDKやPyQUBO(内部的にはdwave-neal)、OpenJijには、イジングモデル/QUBOに対するSAのソルバーが実装されています。

しかし、Amplifyでは特にSAなどの古典ソルバーは実装されていません。
(無料でイジングマシンが使用できるのでなくても全く問題ないですが)

そこで、今回はAmplifyで構築したモデルをOpenJijの古典ソルバーに投入する流れをまとめます。

Amplifyのモデル

Amplifyでは多項式や論理式の数式オブジェクトを用いて記述し、入力模型を構築していきます。

from amplify import BinaryPoly, SymbolGenerator

gen = SymbolGenerator(BinaryPoly) 
q = gen.array(2)
f = 1 - q[0] * q[1]

その後、構築した入力模型をイジングマシンが取り扱うことが可能な「論理模型」として抽象化します。

from amplify import BinaryQuadraticModel

model = BinaryQuadraticModel(f)

論理模型への変換にはBinaryQuadraticModel IsingQuadraticModelといったクラスを使用します。

イジングマシンを利用する場合は、この論理模型をイジングマシンに投入することになりますが、今回はOpenJijのソルバーに投入するために変換していきます。

OpenJij用の変換

OpenJijのインターフェースはイジングモデルの相互作用 JJ と縦磁場 hh のリスト(QUBOの場合はQUBO行列)を渡すものになっています。

以下の関数でAmplifyの論理模型から必要な形式に変換します。

from amplify import BinaryQuadraticModel, IsingQuadraticModel

def amplify_bqm_to_qubo_dict(model: IsingQuadraticModel):
    Q = {}
    const = 0
    for k, v in model.logical_model_poly.asdict().items():
        if len(k) == 1:
            Q[(k[0], k[0])] = v
        elif len(k) == 2:
            Q[k] = v
        else:
            const = v
    return Q, const

def amplify_iqm_to_ising_dict(model: BinaryQuadraticModel):
    h = {}
    J = {}
    const = 0
    for k, v in model.logical_model_poly.asdict().items():
        if len(k) == 1:
            h[k[0]] = v
        elif len(k) == 2:
            J[k] = v
        else:
            const = v
    return h, J, const

実際に変換関数を用いてOpenJijでSAを実行する流れは以下のようになります。

import openjij as oj
from dataclasses import dataclass

@dataclass
class Solution():
    # amplify.SolverSolution と同様のインターフェース
    energy: float
    frequency: int
    is_feasible: bool
    values: Dict[int, int]

sampler = oj.SASampler()
params = {'num_reads': 10}

# Ising/QUBOを変換してSASamplerで計算実行
if isinstance(model, BinaryQuadraticModel):
    Q, const = self.amplify_bqm_to_qubo_dict(model)
    result = sampler.sample_qubo(Q, **params)
elif isinstance(model, IsingQuadraticModel):
    h, J, const = self.amplify_iqm_to_ising_dict(model)
    result = sampler.sample_ising(h, J, **params)

# 結果の解析
solutions = []
for k, s in enumerate(result.states):
    # 制約違反をチェック
    c_status = list(filter(lambda c: not c[1], model.check_constraints(s)))
    values = {}
    for i in result.indices:
        values[i] = s[i]
    solution = Solution(
        energy=result.energies[k] + const,
        frequency=1,
        is_feasible=len(c_status) == 0,
        values=values,
    )
    solutions.append(solution)

print(solutions)

ここではAmplifyでイジングマシンを利用した場合に得られる結果のクラス SolverSolution と同等のインターフェースになるように Solution クラスを定義して利用しています。

以上でAmplifyで構築したモデルに対して手軽にSAでシミュレーションすることができます。

KenT @bodao

このコンテンツにコメントはありません。

empty
ユーザーのみコメントを投稿できます。