# ソルバーの直列実行 性能を評価したい場合、ソルバーを何回か繰り返し実行したい場合があります。また、ソルバーによっては、長時間のタイムアウトを指定して 1 回実行するよりも短時間のタイムアウトで何回か繰り返し実行したほうが良い解を見つける可能性が高くなることもあります。Amplify SDK は、そのようなニーズのために、同じ組合せ最適化問題を同じソルバーで複数回連続して実行する機能を提供しています。 ```{seealso} 定式化やソルバーの性能調査などのために複数回を実行した統計を取る場合には [](parallel.md) のほうが適していることもります。そちらも参照してください。 ``` ## 複数実行の例 まず、通常の {py:func}`~amplify.solve` 関数の実行と同様に、モデルとソルバークライアントを作成します。 ```{testcode} from amplify import VariableGenerator, one_hot, AmplifyAEClient, solve from datetime import timedelta gen = VariableGenerator() q = gen.array("Binary", 3) objective = q[0] * q[1] - q[2] constraint = one_hot(q) model = objective + constraint client = AmplifyAEClient() # client.token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" client.parameters.time_limit_ms = timedelta(milliseconds=1000) ``` ```{testcode} :hide: import os client.url = os.getenv("TEST_AE_URL", "") client.token = os.getenv("TEST_AE_TOKEN", "") ``` {py:func}`~amplify.solve` の `num_solves` キーワード引数に整数を与えることで、ソルバーがその回数だけ繰り返し実行されます。 ```{testcode} result = solve(model, client, num_solves=3) ``` 通常の {py:func}`~amplify.solve` の実行と同様に、{py:class}`~amplify.Result` クラスのインスタンスが返ります。返り値には、`num_solves` 回の実行結果がフラットに入っています。 ```{doctest} >>> len(result) 3 ``` {py:attr}`~amplify.Result.best` アトリビュートを用いることにより、`num_solves` 回の実行で返ってきた中で最も良い解を取得できます。 ```{doctest} >>> print(f"objective = {result.best.objective}, q = {q.evaluate(result.best.values)}") objective = -1.0, q = [0. 0. 1.] ``` ## 結果の取得 {py:func}`~amplify.solve` を `num_solves` キーワードを指定して実行した場合、返り値は通常と同様に {py:class}`~amplify.Result` クラスのインスタンスとなっています。 ソルバーが実行された回数は、{py:class}`~amplify.Result` クラスの {py:attr}`~amplify.Result.num_solves` アトリビュートで分かります。通常は {py:func}`~amplify.solve` の `num_solves` キーワード引数と同じ値になります。ただし、ソルバーの実行が何らかの理由により失敗した場合、この値は指定した値よりも小さくなることがあります。 ```{doctest} >>> result.num_solves 3 ``` インデックスアクセスやイテレートアクセスにより、ソルバーが `num_solves` 回の実行で返した全ての解を集約して得ることができます。デフォルトでは、良い解の順にソートされて入っており、何回目の実行で返されたかの区別はありません。 ```{doctest} >>> print(f"objective = {result[0].objective}, q = {q.evaluate(result[0].values)}") objective = -1.0, q = [0. 0. 1.] ``` 特定の実行により返された解のみを取得するには、{py:attr}`~amplify.Result.split` プロパティを使用します。 ```{doctest} >>> first_result = result.split[0] # `result` から初回の実行にかかった部分のみを抽出 >>> type(first_result) >>> len(first_result) 1 >>> print(f"objective = {first_result.best.objective}, q = {q.evaluate(first_result.best.values)}") objective = -1.0, q = [0. 0. 1.] ``` `num_solves` が指定された場合、次の 2 つの {py:class}`~amplify.Result` オブジェクトには、以下の通りのものが入ります。1 つ目は {py:func}`~amplify.solve` から返されるオブジェクトです。2 つ目は、{py:attr}`~amplify.Result.split` プロパティで得られる `i` 回目の実行結果です。 ```{list-table} :header-rows: 1 :width: 100% :widths: 1 4 4 * - プロパティ名 - {py:func}`~amplify.solve` 関数が返す {py:class}`~amplify.Result` - {py:attr}`~amplify.Result.split` 適用後の {py:class}`~amplify.Result` * - {py:attr}`~amplify.Result.best` - {py:attr}`~amplify.Result.num_solves` 回の実行のうち最も良い解 - `i` 回目の実行のうち最も良い解 * - {py:attr}`~amplify.Result.solutions` - {py:attr}`~amplify.Result.num_solves` 回の実行で得た全ての解 - `i` 回目の実行で得た全ての解 * - {py:attr}`~amplify.Result.intermediate` - 通常の {py:func}`~amplify.solve` 実行と同様 - 通常の {py:func}`~amplify.solve` 実行と同様 * - {py:attr}`~amplify.Result.embedding` - 通常の {py:func}`~amplify.solve` 実行と同様 - 通常の {py:func}`~amplify.solve` 実行と同様 * - {py:attr}`~amplify.Result.client_result` - 最初の実行で得られたもの - `i` 回目の実行で得られたもの * - {py:attr}`~amplify.Result.execution_time` - {py:attr}`~amplify.Result.num_solves` 回の実行の合計値 - `i` 回目の実行で得られたもの * - {py:attr}`~amplify.Result.response_time` - {py:attr}`~amplify.Result.num_solves` 回の実行の合計値 - `i` 回目の実行で得られたもの * - {py:attr}`~amplify.Result.total_time` - {py:func}`~amplify.solve` を始めてから終わるまでの時間 - `i` 回目の実行のために費やされた時間 ```