ommx.v1.Instance#

ommx.v1.Instance is a data structure for describing the optimization problem itself (mathematical model). It consists of the following components:

For example, let’s consider a simple optimization problem:

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

The corresponding ommx.v1.Instance is as follows.

from ommx.v1 import Instance, DecisionVariable

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
)

Each of these components has a corresponding property. The objective function is converted into the form of ommx.v1.Function, as explained in the previous section.

instance.objective
Function(x1 + x2)

sense is set to Instance.MAXIMIZE for maximization problems or Instance.MINIMIZE for minimization problems.

instance.sense == Instance.MAXIMIZE
True

Decision Variables#

Decision variables and constraints can be obtained in the form of pandas.DataFrame.

instance.decision_variables_df
kind lower upper name subscripts description substituted_value
id
1 Binary 0.0 1.0 x [] <NA> <NA>
2 Binary 0.0 1.0 y [] <NA> <NA>

First, kind, lower, and upper are essential information for the mathematical model.

  • kind specifies the type of decision variable, which can be Binary, Integer, Continuous, SemiInteger, or SemiContinuous.

  • lower and upper are the lower and upper bounds of the decision variable. For Binary variables, this range is \([0, 1]\).

Additionally, OMMX is designed to handle metadata that may be needed when integrating mathematical optimization into practical data analysis. While this metadata does not affect the mathematical model itself, it is useful for data analysis and visualization.

  • name is a human-readable name for the decision variable. In OMMX, decision variables are always identified by ID, so this name may be duplicated. It is intended to be used in combination with subscripts, which is described later.

  • description is a more detailed explanation of the decision variable.

  • When dealing with many mathematical optimization problems, decision variables are often handled as multidimensional arrays. For example, it is common to consider constraints with subscripts like \(x_i + y_i \leq 1, \forall i \in [1, N]\). In this case, x and y are the names of the decision variables, so they are stored in name, and the part corresponding to \(i\) is stored in subscripts. subscripts is a list of integers, but if the subscript cannot be represented as an integer, there is a parameters property that allows storage in the form of dict[str, str].

If you need a list of ommx.v1.DecisionVariable directly, you can use the decision_variables property.

for v in instance.decision_variables:
    print(f"{v.id=}, {v.name=}")
v.id=1, v.name='x'
v.id=2, v.name='y'

To obtain ommx.v1.DecisionVariable from the ID of the decision variable, you can use the get_decision_variable_by_id method.

x1 = instance.get_decision_variable_by_id(1)
print(f"{x1.id=}, {x1.name=}")
x1.id=1, x1.name='x'

Constraints#

Next, let’s look at the constraints.

instance.constraints
[Constraint(x1*x2 == 0)]

In OMMX, constraints are also managed by ID. This ID is independent of the decision variable ID. When you create a constraint like x * y == 0, a sequential number is automatically assigned. To manually set the ID, you can use the set_id method.

c = (x * y == 0).set_id(100)
print(f"{c.id=}")
c.id=100

The essential information for constraints is id and equality. equality indicates whether the constraint is an equality constraint (Constraint.EQUAL_TO_ZERO) or an inequality constraint (Constraint.LESS_THAN_OR_EQUAL_TO_ZERO). Note that constraints of the type \(f(x) \geq 0\) are treated as \(-f(x) \leq 0\).

Constraints can also store metadata similar to decision variables. You can use name, description, subscripts, and parameters. These can be set using the add_name, add_description, add_subscripts, and add_parameters methods.

c = (x * y == 0).set_id(100).add_name("prod-zero")
print(f"{c.id=}, {c.name=}")
c.id=100, c.name='prod-zero'

You can also use the constraints property to directly obtain a list of ommx.v1.Constraint. To obtain ommx.v1.Constraint by its the constraint ID, use the get_constraint_by_id method.

for c in instance.constraints:
    print(c)
Constraint(x1*x2 == 0)

Symbolic substitution#

Instance.substitute replaces decision variables with function expressions in the objective and active constraints. This is useful for transformations such as binary encodings, where an integer variable is removed and represented by newly introduced binary variables.

This operation is an algebraic rewrite. It does not automatically translate the substituted variable’s kind, lower, or upper into constraints on the replacement expression. For example, if x1 is binary and you substitute x1 with x2 + x3, OMMX does not add the constraints 0 <= x2 + x3 and x2 + x3 <= 1. If x1 is integer, OMMX also does not add a constraint that the replacement expression must be integral.

The substituted variable is recorded as a dependent variable, so its value can be reconstructed when evaluating a solution. Its bound and kind are checked by Solution.feasible, but they are not passed to solvers as constraints on the replacement expression. Removed constraints are not rewritten immediately; if restored later, dependent and fixed variables are substituted at restore time. In other words, substitute does not by itself guarantee an equivalent optimization model.

This is intentional. Some transformations, such as relaxing a constraint, deliberately change the model. Other transformations, such as log encoding or a custom binary encoding, are valid because the encoding itself is constructed to preserve the original variable’s domain.

If a general substitution must preserve the model, add the necessary constraints explicitly. A common conservative pattern is to keep the original variable and include a linking equality such as x1 - (x2 + x3) == 0 in the transformed model’s constraints. If you eliminate x1 with substitute, add any required bound constraints on the replacement expression yourself.