ommx.v1.Solution

ommx.v1.Solution#

OMMX has several structures that represent the solution of mathematical models.

Data Structure

Description

ommx.v1.State

Holds the solution value for the decision variable ID. The simplest representation of a solution.

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.

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.

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:

\[\begin{split} \begin{align} \max \quad & x + y \\ \text{subject to} \quad & x y = 0 \\ & x, y \in \{0, 1\} \end{align} \end{split}\]

It is clear that this has a feasible solution \(x = 1, y = 0\).

from ommx.v1 import Instance, DecisionVariable

# Create a simple instance
x = DecisionVariable.binary(1, name='x')
y = DecisionVariable.binary(2, name='y')

instance = Instance.from_components(
    decision_variables=[x, y],
    objective=x + y,
    constraints=[x * y == 0],
    sense=Instance.MAXIMIZE
)

# Create a solution
solution = instance.evaluate({1: 1, 2: 0})  # x=1, y=0

The generated ommx.v1.Solution inherits most of the information from the ommx.v1.Instance. Let’s first look at the decision variables.

solution.decision_variables
kind lower upper name subscripts description substituted_value value
id
1 binary 0.0 1.0 x [] <NA> <NA> 1.0
2 binary 0.0 1.0 y [] <NA> <NA> 0.0

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.

solution.constraints
equality value used_ids name subscripts description dual_variable removed_reason
id
0 =0 0.0 {1, 2} <NA> [] <NA> <NA> <NA>

The objective property contains the value of the objective function, and the feasible property contains whether the constraints are satisfied.

print(f"{solution.objective=}, {solution.feasible=}")
solution.objective=1.0, solution.feasible=True

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\).

What happens in the case of an infeasible solution, \(x = 1, y = 1\)?

solution11 = instance.evaluate({1: 1, 2: 1})  # x=1, y=1
print(f"{solution11.objective=}, {solution11.feasible=}")
solution11.objective=2.0, solution11.feasible=False

feasible = False indicates that it is an infeasible solution.