Solution

Solution#

class Solution#

Idiomatic wrapper of ommx.v1.Solution protobuf message.

This also contains annotations not contained in protobuf message, and will be stored in OMMX artifact.

__copy__() Solution#
__deepcopy__(_memo: Any) Solution#
add_user_annotation(key: str, value: str, annotation_namespace: str = 'org.ommx.user.') None#
add_user_annotations(annotations: Mapping[str, str], annotation_namespace: str = 'org.ommx.user.') None#
extract_all_decision_variables() dict#

Extract all decision variables grouped by name.

Returns a mapping from variable name to a mapping from subscripts to values. This is useful for extracting all variables at once in a structured format. Variables without names are not included in the result.

Raises ValueError if a decision variable with parameters is found, or if the same name and subscript combination is found multiple times.

Examples#

>>> from ommx.v1 import Instance, DecisionVariable
>>> x = [DecisionVariable.binary(i, name="x", subscripts=[i]) for i in range(3)]
>>> y = [DecisionVariable.binary(i+3, name="y", subscripts=[i]) for i in range(2)]
>>> instance = Instance.from_components(
...     decision_variables=x + y,
...     objective=sum(x) + sum(y),
...     constraints=[],
...     sense=Instance.MAXIMIZE,
... )
>>> solution = instance.evaluate({i: 1 for i in range(5)})
>>> all_vars = solution.extract_all_decision_variables()
>>> all_vars["x"]
{(0,): 1.0, (1,): 1.0, (2,): 1.0}
>>> all_vars["y"]
{(0,): 1.0, (1,): 1.0}
extract_all_named_functions() dict#

Extract all named functions grouped by name (returns a Python dict)

extract_constraints(name: str) dict#

Extract the values of constraints based on the name with subscripts key.

Raises ValueError if the constraint with parameters is found, or if the same subscript is found.

Examples#

>>> from ommx.v1 import Instance, DecisionVariable
>>> x = [DecisionVariable.binary(i) for i in range(3)]
>>> c0 = (x[0] + x[1] == 1).add_name("c").add_subscripts([0])
>>> c1 = (x[1] + x[2] == 1).add_name("c").add_subscripts([1])
>>> instance = Instance.from_components(
...     decision_variables=x,
...     objective=sum(x),
...     constraints=[c0, c1],
...     sense=Instance.MAXIMIZE,
... )
>>> solution = instance.evaluate({0: 1, 1: 0, 2: 1})
>>> solution.extract_constraints("c")
{(0,): 0.0, (1,): 0.0}
extract_decision_variables(name: str) dict#

Extract the values of decision variables based on the name with subscripts key.

Raises ValueError if a decision variable with parameters is found, or if the same subscript is found.

Examples#

>>> from ommx.v1 import Instance, DecisionVariable
>>> x = [DecisionVariable.binary(i, name="x", subscripts=[i]) for i in range(3)]
>>> instance = Instance.from_components(
...     decision_variables=x,
...     objective=sum(x),
...     constraints=[sum(x) == 1],
...     sense=Instance.MAXIMIZE,
... )
>>> solution = instance.evaluate({i: 1 for i in range(3)})
>>> solution.extract_decision_variables("x")
{(0,): 1.0, (1,): 1.0, (2,): 1.0}
extract_named_functions(name: str) dict#

Extract named functions by name with subscripts as key (returns a Python dict)

from_bytes(bytes: bytes) Solution#
get_constraint_by_id(constraint_id: int) EvaluatedConstraint#

Get a specific evaluated constraint by ID

get_constraint_value(constraint_id: int) float#

Get the evaluated value of a specific constraint by ID

get_decision_variable_by_id(variable_id: int) EvaluatedDecisionVariable#

Get a specific evaluated decision variable by ID

get_dual_variable(constraint_id: int) Optional[float]#

Get the dual variable value for a specific constraint by ID

get_named_function_by_id(named_function_id: int) EvaluatedNamedFunction#

Get a specific evaluated named function by ID

get_user_annotation(key: str, annotation_namespace: str = 'org.ommx.user.') str#
get_user_annotations(annotation_namespace: str = 'org.ommx.user.') dict[str, str]#
set_dual_variable(constraint_id: int, value: Optional[float]) None#

Set the dual variable value for a specific constraint by ID

to_bytes() bytes#
total_violation_l1() float#

Calculate total constraint violation using L1 norm (sum of absolute violations)

Returns the sum of violations across all constraints (including removed constraints):

  • For equality constraints: \(\sum |f(x)|\)

  • For inequality constraints: \(\sum \max(0, f(x))\)

total_violation_l2() float#

Calculate total constraint violation using L2 norm squared (sum of squared violations)

Returns the sum of squared violations across all constraints (including removed constraints):

  • For equality constraints: \(\sum (f(x))^2\)

  • For inequality constraints: \(\sum (\max(0, f(x)))^2\)

LP_RELAXED: Relaxation#

Class constant for LP-relaxed solutions

NOT_OPTIMAL: Optimality#

Class constant for non-optimal solutions

OPTIMAL: Optimality#

Class constant for optimal solutions

property annotations: dict[str, str]#

Returns a copy of the annotations dictionary.

Mutating the returned dict will not update the object. Use add_user_annotation() or assign to annotations to modify annotations.

property constraint_ids: set[int]#

Read-only property.

property constraints: dict[int, EvaluatedConstraint]#

Read-only property.

Get evaluated constraints as a dict keyed by constraint ID

property constraints_df: DataFrame#

Read-only property.

DataFrame of evaluated constraints

Columns: id (index), equality, value, used_ids, name, subscripts, description, dual_variable

property decision_variable_ids: set[int]#

Read-only property.

property decision_variable_names: set[str]#

Read-only property.

Get all unique decision variable names in this solution.

Returns a set of all unique variable names. Variables without names are not included.

Examples#

>>> from ommx.v1 import Instance, DecisionVariable
>>> x = [DecisionVariable.binary(i, name="x", subscripts=[i]) for i in range(3)]
>>> y = [DecisionVariable.binary(i+3, name="y", subscripts=[i]) for i in range(2)]
>>> instance = Instance.from_components(
...     decision_variables=x + y,
...     objective=sum(x) + sum(y),
...     constraints=[],
...     sense=Instance.MAXIMIZE,
... )
>>> solution = instance.evaluate({i: 1 for i in range(5)})
>>> sorted(solution.decision_variable_names)
['x', 'y']
property decision_variables: list[EvaluatedDecisionVariable]#

Read-only property.

Get evaluated decision variables as a list sorted by ID

property decision_variables_df: DataFrame#

Read-only property.

DataFrame of evaluated decision variables

Columns: id (index), kind, lower, upper, name, subscripts, description, substituted_value, value

property end: Optional[datetime]#
property feasible: bool#

Read-only property.

Feasibility of the solution in terms of all constraints, including removed constraints.

This is an alias for feasible_unrelaxed.

Compatibility: The meaning of this property has changed from Python SDK 1.7.0. Previously, this property represents the feasibility of the remaining constraints only, i.e. excluding relaxed constraints. From Python SDK 1.7.0, this property represents the feasibility of all constraints, including relaxed constraints.

property feasible_relaxed: bool#

Read-only property.

Feasibility of the solution in terms of remaining constraints, not including relaxed (removed) constraints.

property feasible_unrelaxed: bool#

Read-only property.

Feasibility of the solution in terms of all constraints, including relaxed (removed) constraints.

property indicator_constraints_df: DataFrame#

Read-only property.

DataFrame of evaluated indicator constraints

Columns: id (index), indicator_variable_id, equality, value, indicator_active, used_ids, name, subscripts, description

property indicator_removed_reasons_df: DataFrame#

Read-only property.

DataFrame of removed indicator constraint reasons.

Columns: id (index), removed_reason, removed_reason.{key}

Can be joined with indicator_constraints_df using the id index.

property instance: Optional[str]#
property instance_digest: Optional[str]#
property named_function_ids: set[int]#

Read-only property.

property named_function_names: set[str]#

Read-only property.

Get all unique named function names in this solution

property named_functions: list[EvaluatedNamedFunction]#

Read-only property.

Get evaluated named functions as a list sorted by ID

property named_functions_df: DataFrame#

Read-only property.

DataFrame of evaluated named functions

Columns: id (index), value, used_ids, name, subscripts, description, parameters.{key}

property objective: float#

Read-only property.

Get the objective function value

property optimality: Optimality#

Get the optimality status

property parameters: Optional[Any]#
property parameters_annotation: Optional[Any]#
property relaxation: Relaxation#

Get the relaxation status

property removed_reasons_df: DataFrame#

Read-only property.

DataFrame of removed constraint reasons.

Columns: id (index), removed_reason, removed_reason.{key}

Can be joined with constraints_df on the id index.

Examples#

>>> from ommx.v1 import Instance, DecisionVariable
>>> x = [DecisionVariable.binary(i) for i in range(3)]
>>> instance = Instance.from_components(
...     decision_variables=x,
...     objective=sum(x),
...     constraints=[
...         (x[0] + x[1] == 1).set_id(10),
...         (x[1] + x[2] == 1).set_id(20),
...     ],
...     sense=Instance.MAXIMIZE,
... )
>>> instance.relax_constraint(10, "test_reason")
>>> solution = instance.evaluate({0: 1, 1: 0, 2: 1})

removed_reasons_df contains only removed constraints:

>>> solution.removed_reasons_df
    removed_reason
id
10    test_reason

Join with constraints_df to get full information:

>>> df = solution.constraints_df.join(solution.removed_reasons_df)
>>> df[["value", "removed_reason"]]
    value removed_reason
id
10    0.0   test_reason
20    0.0           NaN
property sense: Sense#

Read-only property.

Get the optimization sense (minimize or maximize)

property solver: Optional[Any]#
property solver_annotation: Optional[Any]#
property start: Optional[datetime]#
property state: State#

Read-only property.

Get the solution state containing variable values