# モデルの変換 Amplify SDK では、実数変数や整数変数を含むモデルや、任意の次数の多項式を含むモデルを作成できます。一方で、組み合わせ最適化ソルバーは一般に扱える変数の種類や次数、制約条件の種類や入力の可否に制限があり、また、特定の構造を持った二次の多項式しか受け取れない場合もあります。 Amplify SDK は、 {py:func}`~amplify.solve` 関数にソルバーが直接扱えないモデルを入力されたときに、ソルバーが扱える形への変換処理を可能な限り自動で行います。具体的には、変数変換や次数下げなどモデルを変換する処理と、グラフ埋め込みと呼ばれる 2 次の多項式をソルバーが受け取れる形に変換する処理が行われ、その後ソルバーの実行が行われます。 ## 変換処理の概要 まず、Amplify SDK はソルバーが対応できる変数の種類や目的関数・制約条件の次数に応じて変数変換や次数下げを行います。変数変換や次数下げを行ったあとのモデルを**中間モデル** (Intermediate Model) とよびます。中間モデルがソルバーに入力できる形になっているならば、ソルバーに中間モデルを求解させます。ソルバーから返ってきた解に対して、入力モデルに行った変数変換の逆変換を行い、入力モデルの解を計算します。 ```{image} ../_images/conversion_intermediate_light.drawio.svg :width: 70% :align: center :class: only-light ``` ```{image} ../_images/conversion_intermediate_dark.drawio.svg :width: 70% :align: center :class: only-dark ``` ソルバーの種類によっては、中間モデルをそのままソルバーに入力できない場合があります。変数の種類と次数の制限以外に、与えられる 2 次の項の制限があるためです。この場合、Amplify SDK はさらに**グラフ埋め込み** とよばれる操作を行い、中間モデルをソルバーに入力できる形式に変換します。ソルバーから返ってきた解に対して、中間モデルに行ったグラフ埋め込みの逆変換をかけることで中間モデルの解が計算できます。さらに中間モデルの解に対して、入力モデルに行った変数変換の逆変換をかけることで、入力モデルの解となります。 ```{image} ../_images/conversion_embedding_light.drawio.svg :width: 90% :align: center :class: only-light ``` ```{image} ../_images/conversion_embedding_dark.drawio.svg :width: 90% :align: center :class: only-dark ``` (create-intermediate-model)= ### 中間モデルの構築 変換処理の準備として、ソルバークライアントから次の情報を取得してソルバーの扱える問題の種類を取得し、出力する中間モデルの持つ目的関数と制約条件の次数を決定します。 - 目的関数として扱える変数の種類とそれぞれの次数 - 等式制約として扱える変数の種類とそれぞれの次数 - 不等式制約として扱える変数の種類とそれぞれの次数 ソルバーの扱える最大の次数は変数の種類ごとに与えられます。以下は {py:class}`~amplify.FixstarsClient` の例です。 ```{testcode} from amplify import FixstarsClient client = FixstarsClient() ``` ```{doctest} >>> client.acceptable_degrees.objective # 目的関数の次数 {VariableType.Binary: Degree.Quadratic, VariableType.Ising: Degree.Zero, VariableType.Integer: Degree.Zero, VariableType.Real: Degree.Zero} >>> client.acceptable_degrees.equality_constraints # 等式制約の次数 {VariableType.Binary: Degree.Zero, VariableType.Ising: Degree.Zero, VariableType.Integer: Degree.Zero, VariableType.Real: Degree.Zero} >>> client.acceptable_degrees.inequality_constraints # 不等式制約の次数 {VariableType.Binary: Degree.Zero, VariableType.Ising: Degree.Zero, VariableType.Integer: Degree.Zero, VariableType.Real: Degree.Zero} ``` {py:class}`~amplify.FixstarsClient` では、目的関数はバイナリ変数の2次までで、等式制約と不等式制約は直接扱えないことがわかります。 ```{note} 扱える次数はクライアントごとに異なりソルバークライアントの設定によっても変化します。詳しくは [](solvers.md) を参照してください。 ``` その後、Amplify SDK は次のようにして中間モデルへの変換を行います。 ```{card} 中間モデル構築の処理手順 :shadow: md :class-card: sd-border-2 sd-my-3 1. 変数変換と次数下げを行うことで、目的関数をソルバーが扱える形に変換可能か確認する 2. 変数変換と次数下げを行うことで、等式・不等式制約をソルバーが扱える形に変換可能か確認する 3. ソルバーが扱えない制約条件が存在するならばそのペナルティ関数を計算し、ペナルティ関数が変数変換と次数下げで目的関数と同条件に変換可能か確認する 4. 目的関数と全ての制約条件が変換可能なら、変数変換と次数下げを実行する 5. 使用していない変数を削除して変数とモデルを再構築する 6. 入力モデルと中間モデルの間の変数変換マップを作成する ``` ### グラフ埋め込み グラフ埋め込みが必要なソルバーの場合、中間モデルへの変換に加えてグラフ埋め込みを行います。 グラフ埋め込みが必要かどうかはソルバーごとに決まっており、グラフ埋め込みが必要なソルバーは[ソルバークライアントの一覧](#solver-clients)にて {bdg-warning}`Graph` というラベルが付与されています。 まず、ソルバークライアントからソルバー固有のグラフ構造を取得します。以下は {py:class}`~amplify.DWaveSamplerClient` の例です。 ```{testcode} from amplify import DWaveSamplerClient client = DWaveSamplerClient() client.token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ``` ```{doctest} >>> graph = client.graph # DWaveSampler のグラフ構造 # doctest: +SKIP >>> graph.type # doctest: +SKIP 'Pegasus' >>> len(graph.nodes) # doctest: +SKIP 5627 >>> len(graph.edges) # doctest: +SKIP 40279 ``` {py:class}`~amplify.DWaveSamplerClient` に対応するソルバーが持つグラフ構造は、ノード 5627 個とエッジ 40279 個からなるペガサスグラフであることが分かります。 Amplify SDK は以下の手順によって 2 次の多項式に対してグラフ埋め込みを行い、ソルバーが扱えるようにします。 ```{card} グラフ埋め込みの処理手順 :shadow: md :class-card: sd-border-2 sd-my-3 1. 中間モデルのそれぞれの制約条件のペナルティ関数を目的関数に足すことにより、中間モデルを目的関数のみからなる最適化問題 (制約なし中間モデル) に変換する 2. 制約なし中間モデルの目的関数のグラフ表現からソルバー固有のグラフ (物理グラフ) へのグラフ埋め込みを試みる 3. 中間モデルと物理グラフの間の変数の対応マップ (チェイン) を作成する 4. チェインに基づいて 1. で得られた多項式の変換を行い、ソルバーが扱える形式の多項式を得る ``` ### ソルバーの実行と結果の取得 以上のモデル変換とグラフ埋め込みによってソルバーの入力可能な形式に変換ができました。その後、Amplify SDK は {py:func}`~amplify.solve` 関数に与えられたソルバークライアントに対して `solve` メソッドを呼び出し、ソルバーによる最適化を実行します。ソルバークライアントは、ソルバーの要求するリクエストデータの作成から API や最適化関数の呼び出しを経て、ソルバーのレスポンスの解析までを行います。 Amplify SDK はソルバーから返却された解に対して、グラフ埋め込みによる変数変換処理と中間モデルの構築における変数変換それぞれの**逆変換**を段階的に行い、入力モデルの解を取得して {py:attr}`~amplify.Result.solutions` アトリビュートに格納します。 {py:func}`~amplify.solve` 関数は返値の {py:attr}`~amplify.Result` クラスに一連の変換・逆変換処理やソルバーの実行結果に関する情報を保存します。以下は代表的な情報を取得するための{py:attr}`~amplify.Result` クラスのアトリビュートです。 ```{list-table} :width: 100% :header-rows: 1 :widths: 3 3 6 1 * - アトリビュート - データ型 - 概要 - 詳細 * - {py:attr}`~amplify.Result.solutions` - {py:attr}`~amplify.Result.SolutionList` - 入力モデルの解や目的関数の値などの情報を格納 - [{octicon}`link`](#result-info-on-solutions) * - {py:attr}`~amplify.Result.intermediate` - {py:attr}`~amplify.Result.ModelConversion` - 中間モデルとその変数変換情報を格納 - [{octicon}`link`](#intermediate-result) * - {py:attr}`~amplify.Result.embedding` - {py:class}`~amplify.Result.GraphConversion` - 中間モデルから物理グラフへのグラフ埋め込み情報を格納 (グラフ埋め込みが必要な場合のみ) - [{octicon}`link`](#embedding-result) * - {py:attr}`~amplify.Result.client_result` - `Client.Result` - ソルバークライアントの実行結果を格納 - [{octicon}`link`](#client-solve) ``` ```{seealso} 中間モデルとグラフ埋め込みの実行結果に関する情報の詳細は、それぞれ [](intermediate.md) と [](graph.md) を参照してください。 ``` (conversion-parameters)= ## モデル変換のパラメータ モデル変換の際に使用されるパラメータを以下に示します。これらは、{py:func}`~amplify.solve` 関数のキーワード引数として与えることができます。 ```{list-table} :header-rows: 1 :width: 100% :widths: 3 3 6 1 * - パラメータ名 - モデル変換の種類 - 概要 - 詳細 * - `integer_encoding_method` - 変数変換 - 整数変数をバイナリ変数に変換変換するアルゴリズム - [{octicon}`link`](#encode-integer) * - `quadratization_method` - 次数下げ - 次数下げに使用するアルゴリズム - [{octicon}`link`](#quadratization-method) * - `substitution_multiplier` - 次数下げ - 次数下げで生成される制約条件の重み - [{octicon}`link`](#substitution-multiplier) * - `embedding_method` - グラフ埋め込み - グラフ埋め込みに使用するアルゴリズム - [{octicon}`link`](#embedding-method) * - `embedding_timeout` - グラフ埋め込み - グラフ埋め込みのタイムアウト値 (秒) - [{octicon}`link`](#embedding-timeout) * - `chain_strength` - グラフ埋め込み - 多項式のグラフ埋め込みの際のパラメータ - [{octicon}`link`](#chain-strength) ``` たとえば、{py:func}`~amplify.solve` 関数の中で、整数変数をバイナリ変数に変換変換する際に使用するアルゴリズムを {py:class}`~amplify.IntegerEncodingMethod.Unary` に設定したい場合は、以下のようにします。 ```{testcode} :hide: from amplify import solve, Model, FixstarsClient model = Model() client = FixstarsClient() ``` ```{testcode} result = solve(model, client, integer_encoding_method="Unary") ``` また、以下のパラメータは、不等式制約のペナルティ生成方法を指定します。不等式制約を表す制約条件オブジェクト {py:class}`~amplify.Constraint` を {py:func}`~amplify.less_equal` などのヘルパー関数や {py:class}`~amplify.Constraint` クラスのコンストラクタを使って構築する際に、キーワード引数 `method` により指定できます。 ```{list-table} :header-rows: 1 :width: 100% :widths: 3 3 6 1 * - パラメータ名 - モデル変換の種類 - 概要 - 詳細 * - `penalty_formulation` - ペナルティ生成 - ペナルティ生成のアルゴリズム - [{octicon}`link`](#ineq-penalty) ``` たとえば、{py:func}`~amplify.solve` 関数の中で、不等式制約のペナルティを生成する際に使用するアルゴリズムを {py:class}`~amplify.PenaltyFormulation.IntegerVariable` に設定したい場合は、以下のようにします。 ```{testcode} :hide: from amplify import VariableGenerator, less_equal q = VariableGenerator().array("Binary", 3) ``` ```{testcode} le_constraint = less_equal( q[0] + q[1] + q[2], 2, penalty_formulation="IntegerVariable" ) ``` (conversion-next-step)= ## 次のステップ 以下のページでは、Amplify の変換処理をより詳しく知りたい方のために、Amplify の行うモデル変換処理・ペナルティ関数の生成による制約条件の実現・グラフ埋め込み処理について説明しています。 `````{grid} 1 :gutter: 3 ````{grid-item-card} 変数変換と次数下げ :link: intermediate :link-type: doc 解きたいモデルをソルバーの扱える変数の種類や多項式の次数に合わせるために Amplify SDK の実装している変数変換と次数下げの方法を説明します。 ```{div} sd-text-right [Learn more »](intermediate.md) ``` ```` ````{grid-item-card} 制約条件とペナルティ関数 :link: penalty :link-type: doc 制約条件を扱えないソルバーに対して Amplify SDK がどのようなペナルティ関数を生成し、制約条件を実現しているかを説明します。 ```{div} sd-text-right [Learn more »](penalty.md) ``` ```` ````{grid-item-card} グラフ埋め込み :link: graph :link-type: doc ソルバーに入力可能な二次多項式にソルバー固有の構造を持つ場合に、任意の入力を可能にするために行われるグラフ埋め込みの処理について解説します。 特に、D-Wave の例を用いて Amplify SDK で実装されるグラフ埋め込みの詳細を説明します。 ```{div} sd-text-right [Learn more »](graph.md) ``` ```` ````` ```{toctree} :hidden: intermediate.md penalty.md graph.md ```