Solution Module API

The solution module provides classes for working with optimization solutions, including variable value access, sensitivity analysis, and solution mapping.

Overview

The solution module implements type-safe solution handling with automatic mapping from solver indices to user data:

        graph LR
    A[Solver] --> B[LXSolution]
    B --> C[Variable Values]
    B --> D[Mapped Values]
    B --> E[Sensitivity Data]
    B --> F[Goal Deviations]
    G[LXSolutionMapper] --> D
    H[Data Models] --> G

    style B fill:#e1f5ff
    style C fill:#fff4e1
    style D fill:#ffe1e1
    style E fill:#e1ffe1
    style F fill:#f0e1ff
    

Components

Solution Container

lumix.solution.solution.LXSolution

Type-safe solution with automatic mapping.

The LXSolution class provides comprehensive access to:

  • Variable values (direct and mapped)

  • Solution metadata (status, objective, solve time)

  • Sensitivity analysis data (shadow prices, reduced costs)

  • Goal programming information (deviations, satisfaction)

Solution Mapper

lumix.solution.mapping.LXSolutionMapper

Maps solution values back to ORM model instances.

The LXSolutionMapper class provides utilities for:

  • Mapping solution values to model instances

  • Handling single-indexed variables

  • Processing multi-indexed variables

  • Working with cartesian product results

Detailed API Reference

LXSolution

Solution classes for LumiX.

class lumix.solution.solution.LXSolution(objective_value, status, solve_time, variables=<factory>, mapped=<factory>, shadow_prices=<factory>, reduced_costs=<factory>, gap=None, iterations=None, nodes=None, goal_deviations=<factory>)[source]

Bases: Generic[TModel]

Type-safe solution with automatic mapping.

Provides access to:

  • Variable values (by name or LXVariable object)

  • Mapped values (variables mapped by index keys)

  • Shadow prices (dual values for constraints)

  • Reduced costs (for sensitivity analysis)

Examples

Basic usage:

solution = optimizer.solve(model)

# Access by variable name
prod_value = solution.variables["production"]

# Access by LXVariable object
prod_value = solution.get_variable(production)

# Access multi-indexed variables
duty_value = solution.variables["duty"][(driver_id, date)]

# Access mapped values (indexed by keys)
for key, value in solution.get_mapped(duty).items():
    if value > 0.5:
        print(f"Variable {key} = {value}")
Parameters:
objective_value: float
status: str
solve_time: float
variables: Dict[str, Union[float, Dict[Any, float]]]
mapped: Dict[str, Dict[Any, float]]
shadow_prices: Dict[str, float]
reduced_costs: Dict[str, float]
gap: Optional[float] = None
iterations: Optional[int] = None
nodes: Optional[int] = None
goal_deviations: Dict[str, Dict[str, Union[float, Dict[Any, float]]]]
get_variable(var)[source]

Get variable value with full type inference.

Parameters:

var (LXVariable[TypeVar(TModel), TypeVar(TValue, int, float)]) – LXVariable to get value for

Return type:

Union[TypeVar(TValue, int, float), Dict[Any, TypeVar(TValue, int, float)]]

Returns:

Variable value (scalar or dict for indexed variables)

get_mapped(var)[source]

Get values mapped by index keys.

Returns the same structure as variables, indexed by the keys extracted via the variable’s index_func (e.g., product.id).

Note

This returns index keys, not model instances, to avoid hashability issues with non-frozen dataclasses.

Parameters:

var (LXVariable[TypeVar(TModel), TypeVar(TValue, int, float)]) – LXVariable to get mapped values for

Return type:

Dict[Any, TypeVar(TValue, int, float)]

Returns:

Dictionary mapping index keys to values

Examples

For production indexed by product.id:

for product_id, qty in solution.get_mapped(production).items():
    print(f"Product {product_id}: {qty} units")
get_shadow_price(constraint_name)[source]

Get shadow price (dual value) for constraint.

Parameters:

constraint_name (str) – Constraint name

Return type:

Optional[float]

Returns:

Shadow price if available

get_reduced_cost(var_name)[source]

Get reduced cost for variable.

Parameters:

var_name (str) – Variable name

Return type:

Optional[float]

Returns:

Reduced cost if available

get_goal_deviations(goal_name)[source]

Get deviation values for a goal constraint.

Returns both positive and negative deviations for the specified goal.

Parameters:

goal_name (str) – Name of the goal constraint

Return type:

Optional[Dict[str, Union[float, Dict[Any, float]]]]

Returns:

Dictionary with keys ‘pos’ and ‘neg’ containing deviation values, or None if goal not found

Example

>>> deviations = solution.get_goal_deviations("production_target")
>>> pos_dev = deviations["pos"]  # Over-production
>>> neg_dev = deviations["neg"]  # Under-production
is_goal_satisfied(goal_name, tolerance=1e-06)[source]

Check if a goal is satisfied within tolerance.

A goal is satisfied if both positive and negative deviations are within the specified tolerance.

Parameters:
  • goal_name (str) – Name of the goal constraint

  • tolerance (float) – Tolerance for deviation (default: 1e-6)

Return type:

Optional[bool]

Returns:

True if goal is satisfied, False if not, None if goal not found

Example

>>> if solution.is_goal_satisfied("demand_goal", tolerance=0.01):
...     print("Demand goal achieved!")
get_total_deviation(goal_name)[source]

Get total absolute deviation for a goal.

Sum of absolute values of all positive and negative deviations.

Parameters:

goal_name (str) – Name of the goal constraint

Return type:

Optional[float]

Returns:

Total deviation, or None if goal not found

Example

>>> total_dev = solution.get_total_deviation("production_target")
>>> print(f"Total deviation: {total_dev}")
is_optimal()[source]

Check if solution is optimal.

Return type:

bool

is_feasible()[source]

Check if solution is feasible.

Return type:

bool

visualize(model=None)[source]

Create interactive visualization for this solution.

Requires the visualization extra: pip install lumix-opt[viz]

Parameters:

model (Optional[LXModel[TypeVar(TModel)]]) – Optional optimization model (for constraint info)

Return type:

LXSolutionVisualizer[TypeVar(TModel)]

Returns:

LXSolutionVisualizer instance

Examples

Basic usage:

solution.visualize().show()

With model for constraint details:

solution.visualize(model).show()

Export to HTML:

solution.visualize(model).to_html("solution.html")
summary()[source]

Get solution summary.

Return type:

str

Returns:

String summary

__init__(objective_value, status, solve_time, variables=<factory>, mapped=<factory>, shadow_prices=<factory>, reduced_costs=<factory>, gap=None, iterations=None, nodes=None, goal_deviations=<factory>)
Parameters:
Return type:

None

Key Attributes:

  • objective_value: Final objective function value

  • status: Solution status string (optimal, feasible, infeasible, etc.)

  • solve_time: Time taken to solve in seconds

  • variables: Dictionary mapping variable names to values (solver indices)

  • mapped: Dictionary mapping variable names to values (data keys)

  • shadow_prices: Shadow prices (dual values) for constraints

  • reduced_costs: Reduced costs for variables

  • gap: Optimality gap (for MIP)

  • iterations: Number of solver iterations

  • nodes: Number of branch-and-bound nodes

  • goal_deviations: Deviation values for goal programming

Key Methods:

  • get_variable(var): Get variable value with type inference

  • get_mapped(var): Get values mapped by index keys

  • get_shadow_price(constraint_name): Get shadow price for constraint

  • get_reduced_cost(var_name): Get reduced cost for variable

  • get_goal_deviations(goal_name): Get goal deviation values

  • is_goal_satisfied(goal_name, tolerance): Check if goal is achieved

  • get_total_deviation(goal_name): Get total absolute deviation

  • is_optimal(): Check if solution is optimal

  • is_feasible(): Check if solution is feasible

  • summary(): Get formatted solution summary

LXSolutionMapper

Solution mapping utilities for LumiX.

class lumix.solution.mapping.LXSolutionMapper[source]

Bases: Generic[TModel]

Maps solution values back to ORM model instances.

Handles: - Single-indexed variables - Multi-indexed variables - Join-based variables

map_variable_to_models(var, solution_values, model_instances)[source]

Map variable values to model instances.

Parameters:
  • var (LXVariable[TypeVar(TModel), Any]) – LXVariable definition

  • solution_values (Dict[Any, float]) – Solution values (index -> value)

  • model_instances (List[TypeVar(TModel)]) – Model instances used in optimization

Return type:

Dict[TypeVar(TModel), float]

Returns:

Dictionary mapping model instances to values

map_multi_indexed_variable(var, solution_values)[source]

Map multi-indexed variable values to model instance tuples.

Parameters:
  • var (LXVariable) – LXVariable definition

  • solution_values (Dict[tuple, float]) – Solution values ((key1, key2, …) -> value)

Return type:

Dict[tuple, float]

Returns:

Dictionary mapping model instance tuples to values

Key Methods:

  • map_variable_to_models(var, solution_values, model_instances): Map solution values to model instances for single-indexed variables

  • map_multi_indexed_variable(var, solution_values): Map solution values to model instance tuples for multi-indexed variables

Usage Examples

Basic Solution Access

from lumix import LXOptimizer, LXModel, LXVariable

# Solve model
optimizer = LXOptimizer().use_solver("gurobi")
solution = optimizer.solve(model)

# Check status
if solution.is_optimal():
    print(f"Optimal objective: {solution.objective_value:.2f}")

# Access variable values
for key, value in solution.get_mapped(production).items():
    print(f"Product {key}: {value} units")

Sensitivity Analysis

# Get shadow price
shadow_price = solution.get_shadow_price("capacity_constraint")
if shadow_price:
    print(f"Value of additional capacity: ${shadow_price:.2f}")

# Get reduced cost
reduced_cost = solution.get_reduced_cost("production[product_A]")
if reduced_cost:
    print(f"Cost reduction needed: ${reduced_cost:.2f}")

Goal Programming

# Check goal satisfaction
if solution.is_goal_satisfied("demand_target"):
    print("Demand goal achieved!")
else:
    deviations = solution.get_goal_deviations("demand_target")
    print(f"Positive deviation: {deviations['pos']}")
    print(f"Negative deviation: {deviations['neg']}")

# Get total deviation
total_dev = solution.get_total_deviation("demand_target")
print(f"Total deviation: {total_dev:.2f}")

Solution Mapping

from lumix.solution import LXSolutionMapper

# Create mapper
mapper = LXSolutionMapper[Product]()

# Map to model instances
instance_values = mapper.map_variable_to_models(
    var=production,
    solution_values=solution.mapped["production"],
    model_instances=products
)

# Process by instance
for product, quantity in instance_values.items():
    print(f"{product.name}: {quantity} units")

Multi-Indexed Variables

# Map multi-indexed solution
instance_map = mapper.map_multi_indexed_variable(
    var=assignment,
    solution_values=solution.mapped["assignment"]
)

# Result: {(Driver(id=1), Date(date="2024-01-01")): 1, ...}
for (driver, date), assigned in instance_map.items():
    if assigned > 0.5:
        print(f"{driver.name} assigned on {date.date}")

Solution Summary

# Get formatted summary
print(solution.summary())

Output:

Status: optimal
Objective: 12345.678900
Solve time: 0.123s
Non-zero variables: 42/100
Gap: 0.00%
Iterations: 125

Goal Constraints: 3
Goals Satisfied: 2/3

Type Hints

The solution module is fully type-annotated:

from typing import Dict, Any, Union, Optional, Tuple
from lumix.solution import LXSolution, LXSolutionMapper
from lumix.core import LXVariable

# Type-safe solution access
solution: LXSolution[Product]
value: Union[float, Dict[Any, float]] = solution.get_variable(production)

# Type-safe mapping
mapper: LXSolutionMapper[Product]
instance_map: Dict[Product, float] = mapper.map_variable_to_models(...)

See Also