{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# ommx.v1.Solution\n", "\n", "OMMX has several structures that represent the solution of mathematical models.\n", "\n", "| Data Structure | Description |\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) | Holds the solution value for the decision variable ID. The simplest representation of a solution. |\n", "| [`ommx.v1.Solution`](https://jij-inc.github.io/ommx/python/ommx/autoapi/ommx/v1/index.html#ommx.v1.Solution) | A representation of the solution intended to be human-readable. In addition to the values of the decision variables and the evaluation values of the constraints, it also holds metadata for the decision variables and constraints added to the [`ommx.v1.Instance`](https://jij-inc.github.io/ommx/python/ommx/autoapi/ommx/v1/index.html#ommx.v1.Instance). |\n", "\n", "Most solvers are software designed to solve mathematical models, so they return minimal information equivalent to `ommx.v1.State`, but OMMX mainly handles `ommx.v1.Solution`, which allows users to easily check the optimization results.\n", "\n", "`ommx.v1.Solution` is generated by passing `ommx.v1.State` or equivalent `dict[int, float]` to the `ommx.v1.Instance.evaluate` method. Let's consider the simple optimization problem we saw in the previous section again:\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", "It is clear that this has a feasible solution $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": [ "The generated `ommx.v1.Solution` inherits most of the information from the `ommx.v1.Instance`. Let's first look at the decision variables." ] }, { "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": [ "In addition to the required attributes—ID, `kind`, `lower`, and `upper`-it also inherits metadata such as `name`. Additionally, the `value` stores which was assigned in `evaluate`. Similarly, the evaluation value is added to the constraints as `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": [ "The `objective` property contains the value of the objective function, and the `feasible` property contains whether the constraints are satisfied." ] }, { "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": [ "Since $xy = 0$ when $x = 1, y = 0$, all constraints are satisfied, so `feasible` is `True`. The value of the objective function is $x + y = 1$.\n", "\n", "What happens in the case of an infeasible solution, $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` indicates that it is an infeasible solution." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.10.6" } }, "nbformat": 4, "nbformat_minor": 4 }