# ソルバーの直列実行 性能の評価を行いたい場合、ソルバーを何回か繰り返し実行したい場合があります。また、ソルバーによっては、長時間のタイムアウトを指定して 1 回実行するよりも短時間のタイムアウトで何回か繰り返し実行したほうが良い解を見つける可能性が高くなることもあります。Amplify SDK は、そのようなニーズのために、同じ組合せ最適化問題を同じソルバーで複数回連続して実行する機能を提供しています。 ```{seealso} 定式化やソルバーの性能調査などのために複数回を実行した統計を取る場合には [](parallel.md) の方が適していることもります。そちらも参照してください。 ``` ## 複数実行の例 まず、通常の {py:func}`~amplify.solve` 関数の実行と同様に、モデルとソルバークライアントを作成します。 ```{testcode} from amplify import VariableGenerator, one_hot, FixstarsClient, 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 = FixstarsClient() # client.token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" client.parameters.timeout = timedelta(milliseconds=1000) ``` {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:class}`~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` キーワード引数として指定した値と同一ですが、ソルバー実行のうち何回かが何らかの理由により失敗した場合、`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` が指定された場合に {py:func}`~amplify.solve` から返される {py:class}`~amplify.Result` オブジェクト、およびそれに {py:attr}`~amplify.Result.split` プロパティを使用して得られる `i` 回目の実行結果を表す {py:class}`~amplify.Result` オブジェクトの各プロパティについては、以下の通りのものが入ります。 ```{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` 回目の実行のために費やされた時間 ```