{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# ommx.v1.Solution\n", "\n", "OMMXには数理モデルの解を表す構造体がいくつか存在します\n", "\n", "| データ構造 | 説明 |\n", "| --- | --- |\n", "| [`ommx.v1.State`](https://jij-inc.github.io/ommx/python/ommx/autoapi/ommx/v1/solution_pb2/index.html#ommx.v1.solution_pb2.State) | 決定変数のIDに対して解の値を保持したもの。最も単純な解の表現。 |\n", "| [`ommx.v1.Solution`](https://jij-inc.github.io/ommx/python/ommx/autoapi/ommx/v1/index.html#ommx.v1.Solution) | 人間が読む事を想定した解の表現。決定変数の値やそれによる制約条件の評価値に加えて、[`ommx.v1.Instance`](https://jij-inc.github.io/ommx/python/ommx/autoapi/ommx/v1/index.html#ommx.v1.Instance)に追加された決定変数や制約条件のメタデータも保持している。 |\n", "\n", "多くのソルバーは数理モデルを解く事を目的としたソフトウェアなので `ommx.v1.State` に相当する最小限の情報を返しますが、OMMXではユーザーが最適化の結果を容易に確認できる形である `ommx.v1.Solution` を中心として扱います。\n", "\n", "`ommx.v1.Solution` は `ommx.v1.Instance.evaluate` メソッドに `ommx.v1.State` あるいは相当する `dict[int, float]` を渡す事で生成されます。前節で見た簡単な最適化問題\n", "\n", "$$\n", "\\begin{align}\n", "\\max \\quad & x + y \\\\\n", "\\text{subject to} \\quad & x y = 0 \\\\\n", "& x, y \\in \\{0, 1\\}\n", "\\end{align}\n", "$$\n", "\n", "をここでも考えましょう。これは明らかに実行可能解 $x = 1, y = 0$ を持ちます。" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from ommx.v1 import Instance, DecisionVariable\n", "\n", "# Create a simple instance\n", "x = DecisionVariable.binary(1, name='x')\n", "y = DecisionVariable.binary(2, name='y')\n", "\n", "instance = Instance.from_components(\n", " decision_variables=[x, y],\n", " objective=x + y,\n", " constraints=[x * y == 0],\n", " sense=Instance.MAXIMIZE\n", ")\n", "\n", "# Create a solution\n", "solution = instance.evaluate({1: 1, 2: 0}) # x=1, y=0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "生成された `ommx.v1.Soluiton` は `ommx.v1.Instance` からほとんどの情報を引き継ぎます。まず決定変数を見てみましょう。" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
kindloweruppernamesubscriptsdescriptionsubstituted_valuevalue
id
1binary0.01.0x[]<NA><NA>1.0
2binary0.01.0y[]<NA><NA>0.0
\n", "
" ], "text/plain": [ " kind lower upper name subscripts description substituted_value value\n", "id \n", "1 binary 0.0 1.0 x [] 1.0\n", "2 binary 0.0 1.0 y [] 0.0" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "solution.decision_variables" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "必須であるIDと `kind`, `lower`, `upper` に加えて `name` などのメタデータも引き継ぎます。加えて `value` には `evaluate` で代入された値が追加されます。同様に制約条件にも `value` として評価値が追加されます。" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
equalityvalueused_idsnamesubscriptsdescriptiondual_variableremoved_reason
id
0=00.0{1, 2}<NA>[]<NA><NA><NA>
\n", "
" ], "text/plain": [ " equality value used_ids name subscripts description dual_variable \\\n", "id \n", "0 =0 0.0 {1, 2} [] \n", "\n", " removed_reason \n", "id \n", "0 " ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "solution.constraints" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`objective` プロパティには目的関数の値が、`feasible` プロパティには制約条件を満たしているかどうかが格納されます。" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "solution.objective=1.0, solution.feasible=True\n" ] } ], "source": [ "print(f\"{solution.objective=}, {solution.feasible=}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$x = 1, y = 0$ の時 $xy = 0$ なので制約条件は全て守られているので `feasible` は `True` になります。また目的関数の値は $x + y = 1$ になります。\n", "\n", "では実行可能解でないケース、$x = 1, y = 1$ の時はどうなるでしょうか?" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "solution11.objective=2.0, solution11.feasible=False\n" ] } ], "source": [ "solution11 = instance.evaluate({1: 1, 2: 1}) # x=1, y=1\n", "print(f\"{solution11.objective=}, {solution11.feasible=}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`feasible = False` となっており、実行可能解でない事が確認できます。" ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.4" } }, "nbformat": 4, "nbformat_minor": 2 }