Analysis Module API¶
The analysis module provides comprehensive tools for post-optimization analysis and decision support.
Overview¶
The analysis module implements three complementary analysis approaches:
graph TD
A[LXSolution] --> B[Sensitivity Analysis]
A --> C[Scenario Analysis]
A --> D[What-If Analysis]
E[LXModel] --> C
E --> D
F[LXOptimizer] --> C
F --> D
style A fill:#e1f5ff
style B fill:#fff4e1
style C fill:#ffe1e1
style D fill:#e1ffe1
style E fill:#f0e1ff
style F fill:#e8f4f8
Components¶
Scenario Analysis¶
Type-safe scenario definition for what-if analysis. |
|
Scenario analysis for optimization models. |
|
Represents a single modification to a model parameter. |
The LXScenarioAnalyzer class enables systematic comparison of
multiple what-if scenarios. Define scenarios with LXScenario,
run them in parallel, and compare results side-by-side.
Sensitivity Analysis¶
Sensitivity analysis for optimization models. |
|
Sensitivity analysis results for a variable. |
|
Sensitivity analysis results for a constraint. |
The LXSensitivityAnalyzer class analyzes shadow prices,
reduced costs, and binding constraints to understand how parameter changes affect the optimal solution.
What-If Analysis¶
Interactive what-if analysis for optimization models. |
|
Results of a what-if analysis. |
|
Represents a single what-if change to explore. |
The LXWhatIfAnalyzer class provides interactive exploration of
parameter changes with immediate feedback on objective value impact.
Detailed API Reference¶
Scenario Analysis¶
Scenario and What-If analysis for LumiX models.
- class lumix.analysis.scenario.LXScenario(name, modifications=<factory>, description='')[source]
Bases:
Generic[TModel]Type-safe scenario definition for what-if analysis.
A scenario represents a set of modifications to a base model that allow you to explore different business conditions or assumptions.
Examples
Create a high-capacity scenario:
high_capacity = ( LXScenario[Product]("high_capacity") .modify_constraint_rhs("capacity", multiply=1.5) .describe("Increase capacity by 50%") )
Create a low-cost scenario:
low_cost = ( LXScenario[Product]("low_cost") .modify_constraint_rhs("min_production", set_value=50.0) .modify_variable_bound("production", lower=10.0) .describe("Lower minimum production requirements") )
Create a combined scenario:
optimistic = ( LXScenario[Product]("optimistic") .modify_constraint_rhs("capacity", multiply=1.3) .modify_constraint_rhs("budget", add=10000.0) .describe("Optimistic market conditions") )
- Parameters:
name (str)
modifications (List[LXScenarioModification])
description (str)
-
name:
str
-
modifications:
List[LXScenarioModification]
-
description:
str= ''
- describe(description)[source]
Add description to scenario.
- Parameters:
description (
str) – Human-readable description- Return type:
Self- Returns:
Self for chaining
- modify_constraint_rhs(constraint_name, set_value=None, add=None, multiply=None, description='')[source]
Modify constraint right-hand side.
- Parameters:
- Return type:
Self- Returns:
Self for chaining
Examples
# Set capacity to 1000 scenario.modify_constraint_rhs(“capacity”, set_value=1000.0)
# Increase capacity by 200 scenario.modify_constraint_rhs(“capacity”, add=200.0)
# Increase capacity by 50% scenario.modify_constraint_rhs(“capacity”, multiply=1.5)
- modify_variable_bound(variable_name, lower=None, upper=None, description='')[source]
Modify variable bounds.
- Parameters:
- Return type:
Self- Returns:
Self for chaining
Examples
# Set lower bound scenario.modify_variable_bound(“production”, lower=100.0)
# Set both bounds scenario.modify_variable_bound(“inventory”, lower=50.0, upper=500.0)
- add_custom_modification(modification)[source]
Add custom modification.
- Parameters:
modification (
LXScenarioModification) – Custom modification to add- Return type:
Self- Returns:
Self for chaining
- __init__(name, modifications=<factory>, description='')
- Parameters:
name (str)
modifications (List[LXScenarioModification])
description (str)
- Return type:
None
- class lumix.analysis.scenario.LXScenarioModification(target_type, target_name, modification_type, value, description='')[source]
Bases:
objectRepresents a single modification to a model parameter.
Examples
Increase capacity by 20%:
LXScenarioModification( target_type="constraint", target_name="capacity", modification_type="rhs_multiply", value=1.2 )
Set minimum production to 100:
LXScenarioModification( target_type="constraint", target_name="min_production", modification_type="rhs_set", value=100.0 )
- Parameters:
-
target_type:
str
-
target_name:
str
-
modification_type:
str
-
description:
str= ''
- class lumix.analysis.scenario.LXScenarioAnalyzer(base_model, optimizer, include_baseline=True)[source]
Bases:
Generic[TModel]Scenario analysis for optimization models.
Allows running multiple what-if scenarios on a base model and comparing the results to understand how different assumptions affect outcomes.
Examples
Create analyzer and add scenarios:
analyzer = LXScenarioAnalyzer(base_model, optimizer) # Add scenarios analyzer.add_scenario( LXScenario[Product]("high_capacity") .modify_constraint_rhs("capacity", multiply=1.5) ) analyzer.add_scenario( LXScenario[Product]("low_capacity") .modify_constraint_rhs("capacity", multiply=0.8) ) # Run all scenarios results = analyzer.run_all_scenarios() # Compare results print(analyzer.compare_scenarios()) # Get specific result high_cap_solution = analyzer.get_result("high_capacity")
- Parameters:
base_model (LXModel[TModel])
optimizer (LXOptimizer[TModel])
include_baseline (bool)
- __init__(base_model, optimizer, include_baseline=True)[source]
Initialize scenario analyzer.
- Parameters:
base_model (
LXModel[TypeVar(TModel)]) – Base optimization modeloptimizer (
LXOptimizer[TypeVar(TModel)]) – Optimizer to use for solving scenariosinclude_baseline (
bool) – Whether to include baseline (unmodified) scenario
- add_scenario(scenario)[source]
Add scenario to analyze.
- Parameters:
scenario (
LXScenario[TypeVar(TModel)]) – Scenario to add- Return type:
Self- Returns:
Self for chaining
- add_scenarios(*scenarios)[source]
Add multiple scenarios.
- Parameters:
*scenarios (
LXScenario[TypeVar(TModel)]) – Scenarios to add- Return type:
Self- Returns:
Self for chaining
- run_scenario(scenario_name)[source]
Run single scenario.
- Parameters:
scenario_name (
str) – Name of scenario to run- Return type:
LXSolution[TypeVar(TModel)]- Returns:
Solution for the scenario
- Raises:
KeyError – If scenario not found
- run_all_scenarios(include_baseline=None)[source]
Run all scenarios.
- get_result(scenario_name)[source]
Get result for specific scenario.
- Parameters:
scenario_name (
str) – Name of scenario- Return type:
Optional[LXSolution[TypeVar(TModel)]]- Returns:
Solution if available, None otherwise
- compare_scenarios(scenario_names=None, include_baseline=True, sort_by_objective=True)[source]
Compare scenario results.
- get_best_scenario(maximize=True)[source]
Get name of best scenario by objective value.
- sensitivity_to_parameter(parameter_name, values, modification_type='rhs_multiply', target_type='constraint')[source]
Analyze sensitivity to a single parameter across multiple values.
- Parameters:
- Return type:
Dict[float,LXSolution[TypeVar(TModel)]]- Returns:
Dictionary mapping parameter values to solutions
Examples
Analyze sensitivity to capacity multiplier:
results = analyzer.sensitivity_to_parameter( "capacity", values=[0.8, 0.9, 1.0, 1.1, 1.2, 1.3], modification_type="rhs_multiply" ) for multiplier, solution in results.items(): print(f"Capacity × {multiplier}: ${solution.objective_value:,.2f}")
- visualize()[source]
Create interactive visualization for scenario comparison.
Requires the visualization extra: pip install lumix-opt[viz]
- Return type:
LXScenarioCompare[TypeVar(TModel)]- Returns:
LXScenarioCompare instance
Examples
Basic usage:
analyzer = LXScenarioAnalyzer(model, optimizer) analyzer.add_scenario(scenario1) analyzer.add_scenario(scenario2) analyzer.run_all_scenarios() analyzer.visualize().show()
Comparison chart:
analyzer.visualize().plot_comparison().show()
Export to HTML:
analyzer.visualize().to_html("scenarios.html")
Sensitivity Analysis¶
Sensitivity analysis for LumiX optimization models.
- class lumix.analysis.sensitivity.LXSensitivityAnalyzer(model, solution)[source]
Bases:
Generic[TModel]Sensitivity analysis for optimization models.
Analyzes how changes in parameters affect the optimal solution, including:
Shadow prices (dual values) for constraints
Reduced costs for variables
Binding constraints identification
Sensitivity ranges (when available from solver)
Examples
Create analyzer and analyze sensitivity:
analyzer = LXSensitivityAnalyzer(model, solution) # Analyze variable sensitivity var_sensitivity = analyzer.analyze_variable("production") print(f"Reduced cost: {var_sensitivity.reduced_cost}") # Analyze constraint sensitivity const_sensitivity = analyzer.analyze_constraint("capacity") print(f"Shadow price: {const_sensitivity.shadow_price}") # Get binding constraints binding = analyzer.get_binding_constraints() for name, sensitivity in binding.items(): print(f"{name}: shadow price = {sensitivity.shadow_price}") # Generate full report print(analyzer.generate_report()) # Get most sensitive parameters sensitive_constraints = analyzer.get_most_sensitive_constraints(top_n=5)
- Parameters:
model (LXModel[TModel])
solution (LXSolution[TModel])
- __init__(model, solution)[source]
Initialize sensitivity analyzer.
- Parameters:
solution (
LXSolution[TypeVar(TModel)]) – Solution to analyze
- analyze_variable(var_name)[source]
Analyze sensitivity of a variable.
- Parameters:
var_name (
str) – Variable name- Return type:
- Returns:
Variable sensitivity information
- Raises:
ValueError – If variable not found in solution
- analyze_constraint(constraint_name)[source]
Analyze sensitivity of a constraint.
- Parameters:
constraint_name (
str) – Constraint name- Return type:
- Returns:
Constraint sensitivity information
- analyze_all_variables()[source]
Analyze all variables in solution.
- Return type:
- Returns:
Dictionary mapping variable names to sensitivity information
- analyze_all_constraints()[source]
Analyze all constraints in model.
- Return type:
- Returns:
Dictionary mapping constraint names to sensitivity information
- get_binding_constraints(threshold=1e-06)[source]
Get all binding (tight) constraints.
A constraint is binding if its shadow price is non-zero.
- Parameters:
threshold (
float) – Threshold for considering shadow price as non-zero- Return type:
- Returns:
Dictionary of binding constraints
Examples
Get all binding constraints:
binding = analyzer.get_binding_constraints() for name, sens in binding.items(): print(f"{name} is binding with shadow price {sens.shadow_price}")
- get_non_basic_variables(threshold=1e-06)[source]
Get all non-basic variables (with non-zero reduced costs).
- Parameters:
threshold (
float) – Threshold for considering reduced cost as non-zero- Return type:
- Returns:
Dictionary of non-basic variables
- get_most_sensitive_constraints(top_n=10)[source]
Get constraints with highest shadow prices (most valuable to relax).
- Parameters:
top_n (
int) – Number of constraints to return- Return type:
- Returns:
List of (name, sensitivity) tuples sorted by shadow price magnitude
Examples
Get most sensitive constraints:
top_constraints = analyzer.get_most_sensitive_constraints(top_n=5) for name, sens in top_constraints: print(f"{name}: ${sens.shadow_price:.2f} per unit relaxation")
- get_most_sensitive_variables(top_n=10)[source]
Get variables with highest reduced costs.
- Parameters:
top_n (
int) – Number of variables to return- Return type:
- Returns:
List of (name, sensitivity) tuples sorted by reduced cost magnitude
- identify_bottlenecks(shadow_price_threshold=0.01)[source]
Identify bottleneck constraints (binding with high shadow prices).
- Parameters:
shadow_price_threshold (
float) – Minimum shadow price to consider- Return type:
- Returns:
List of bottleneck constraint names
Examples
Identify bottlenecks:
bottlenecks = analyzer.identify_bottlenecks() print(f"Found {len(bottlenecks)} bottlenecks:") for name in bottlenecks: print(f" - {name}")
- generate_report(include_variables=True, include_constraints=True, include_binding_only=False, top_n=None)[source]
Generate comprehensive sensitivity analysis report.
- Parameters:
- Return type:
- Returns:
Formatted sensitivity report
Examples
Full report:
print(analyzer.generate_report())
Only binding constraints:
print(analyzer.generate_report( include_variables=False, include_binding_only=True ))
Top 10 most sensitive:
print(analyzer.generate_report(top_n=10))
- generate_summary()[source]
Generate brief sensitivity summary.
- Return type:
- Returns:
Brief summary of key sensitivity metrics
- visualize()[source]
Create interactive visualization for sensitivity analysis.
Requires the visualization extra: pip install lumix-opt[viz]
- Return type:
LXSensitivityPlot[TypeVar(TModel)]- Returns:
LXSensitivityPlot instance
Examples
Basic usage:
analyzer = LXSensitivityAnalyzer(model, solution) analyzer.visualize().show()
Tornado chart:
analyzer.visualize().plot_tornado(top_n=15).show()
Export to HTML:
analyzer.visualize().to_html("sensitivity.html")
- class lumix.analysis.sensitivity.LXVariableSensitivity(name, value, reduced_cost=None, allowable_increase=None, allowable_decrease=None, is_basic=False, is_at_bound=False)[source]
Bases:
objectSensitivity analysis results for a variable.
- Parameters:
- name
Variable name
- value
Current value in solution
- reduced_cost
Reduced cost (opportunity cost)
- allowable_increase
Maximum increase before basis change (if available)
- allowable_decrease
Maximum decrease before basis change (if available)
- is_basic
Whether variable is basic in optimal solution
- is_at_bound
Whether variable is at its bound
-
name:
str
-
value:
float
-
is_basic:
bool= False
-
is_at_bound:
bool= False
- __init__(name, value, reduced_cost=None, allowable_increase=None, allowable_decrease=None, is_basic=False, is_at_bound=False)
- class lumix.analysis.sensitivity.LXConstraintSensitivity(name, shadow_price=None, slack=None, allowable_increase=None, allowable_decrease=None, is_binding=False, is_active=False)[source]
Bases:
objectSensitivity analysis results for a constraint.
- Parameters:
- name
Constraint name
- shadow_price
Shadow price (marginal value of relaxation)
- slack
Slack or surplus value
- allowable_increase
Maximum RHS increase before basis change (if available)
- allowable_decrease
Maximum RHS decrease before basis change (if available)
- is_binding
Whether constraint is binding (tight)
- is_active
Whether constraint is active at optimum
-
name:
str
-
is_binding:
bool= False
-
is_active:
bool= False
- __init__(name, shadow_price=None, slack=None, allowable_increase=None, allowable_decrease=None, is_binding=False, is_active=False)
What-If Analysis¶
Interactive What-If analysis for LumiX models.
- class lumix.analysis.whatif.LXWhatIfAnalyzer(model, optimizer, baseline_solution=None)[source]
Bases:
Generic[TModel]Interactive what-if analysis for optimization models.
Allows quick exploration of parameter changes and their impact on the optimal solution. Useful for understanding trade-offs and identifying opportunities for improvement.
Examples
Create analyzer and explore changes:
analyzer = LXWhatIfAnalyzer(model, optimizer) # Get baseline solution baseline = analyzer.get_baseline_solution() # What if we increase capacity? result = analyzer.increase_constraint_rhs("capacity", by=200.0) print(f"Increasing capacity by 200 would improve profit by ${result.delta_objective:,.2f}") # What if we relax minimum production? result = analyzer.relax_constraint("min_production", by_percent=0.5) print(f"Relaxing min production by 50% would change objective by {result.delta_percentage:.1f}%") # Compare multiple changes results = analyzer.compare_changes([ ("capacity", "increase", 100), ("capacity", "increase", 200), ("capacity", "increase", 300), ]) # Find bottlenecks bottlenecks = analyzer.find_bottlenecks(top_n=5) for name, improvement in bottlenecks: print(f"{name}: relaxing by 1 unit would improve objective by ${improvement:.2f}")
- Parameters:
model (LXModel[TModel])
optimizer (LXOptimizer[TModel])
baseline_solution (Optional[LXSolution[TModel]])
- __init__(model, optimizer, baseline_solution=None)[source]
Initialize what-if analyzer.
- Parameters:
optimizer (
LXOptimizer[TypeVar(TModel)]) – Optimizer to use for solvingbaseline_solution (
Optional[LXSolution[TypeVar(TModel)]]) – Pre-computed baseline solution (optional)
- get_baseline_solution(recompute=False)[source]
Get baseline solution (caches result).
- Parameters:
recompute (
bool) – Force recomputation of baseline- Return type:
LXSolution[TypeVar(TModel)]- Returns:
Baseline solution
- increase_constraint_rhs(constraint_name, by=None, by_percent=None, to=None)[source]
Analyze impact of increasing constraint RHS.
- Parameters:
- Return type:
LXWhatIfResult[TypeVar(TModel)]- Returns:
What-if analysis result
Examples
# Increase capacity by 100 units result = analyzer.increase_constraint_rhs(“capacity”, by=100)
# Increase capacity by 20% result = analyzer.increase_constraint_rhs(“capacity”, by_percent=0.2)
# Set capacity to 1500 result = analyzer.increase_constraint_rhs(“capacity”, to=1500)
- decrease_constraint_rhs(constraint_name, by=None, by_percent=None, to=None)[source]
Analyze impact of decreasing constraint RHS.
- relax_constraint(constraint_name, by=None, by_percent=None)[source]
Relax a constraint (increase RHS for LE, decrease for GE).
This is a convenience method that automatically determines the direction based on constraint type.
- Parameters:
- Return type:
LXWhatIfResult[TypeVar(TModel)]- Returns:
What-if analysis result
Examples
# Relax capacity constraint by 100 units result = analyzer.relax_constraint(“capacity”, by=100)
# Relax minimum production by 20% result = analyzer.relax_constraint(“min_production”, by_percent=0.2)
- tighten_constraint(constraint_name, by=None, by_percent=None)[source]
Tighten a constraint (decrease RHS for LE, increase for GE).
- modify_variable_bound(variable_name, lower=None, upper=None)[source]
Analyze impact of changing variable bounds.
- Parameters:
- Return type:
LXWhatIfResult[TypeVar(TModel)]- Returns:
What-if analysis result
Examples
# Increase minimum production to 100 result = analyzer.modify_variable_bound(“production”, lower=100)
# Set production range to [50, 500] result = analyzer.modify_variable_bound(“production”, lower=50, upper=500)
- compare_changes(changes, constraint_type='rhs')[source]
Compare multiple what-if changes.
- Parameters:
- Return type:
List[LXWhatIfResult[TypeVar(TModel)]]- Returns:
List of what-if results
Examples
Compare different capacity increases:
results = analyzer.compare_changes([ ("capacity", "increase", 100), ("capacity", "increase", 200), ("capacity", "increase", 300), ]) for result in results: print(f"{result.description}: Δ = ${result.delta_objective:,.2f}")
- find_bottlenecks(test_amount=1.0, top_n=10)[source]
Find bottleneck constraints by testing small relaxations.
Tests relaxing each constraint by a small amount and measures the impact on the objective function.
- Parameters:
- Return type:
- Returns:
List of (constraint_name, improvement) tuples sorted by improvement
Examples
Find top bottlenecks:
bottlenecks = analyzer.find_bottlenecks(test_amount=1.0, top_n=5) print("Top 5 bottlenecks:") for name, improvement in bottlenecks: print(f" {name}: +${improvement:.2f} per unit relaxation")
- sensitivity_range(constraint_name, min_value, max_value, num_points=10)[source]
Analyze objective sensitivity across a range of constraint RHS values.
- Parameters:
- Return type:
- Returns:
List of (rhs_value, objective_value) tuples
Examples
# Analyze sensitivity to capacity from 800 to 1200 sensitivity = analyzer.sensitivity_range(“capacity”, 800, 1200, num_points=20)
- for rhs, obj in sensitivity:
print(f”Capacity = {rhs:6.1f} → Objective = ${obj:,.2f}”)
- class lumix.analysis.whatif.LXWhatIfResult(description, original_objective, new_objective, delta_objective, delta_percentage, original_solution, new_solution, changes_applied=<factory>)[source]
Bases:
Generic[TModel]Results of a what-if analysis.
- Parameters:
description (str)
original_objective (float)
new_objective (float)
delta_objective (float)
delta_percentage (float)
original_solution (LXSolution[TModel])
new_solution (LXSolution[TModel])
changes_applied (List[LXWhatIfChange])
- description
Description of the change
- original_objective
Original objective value
- new_objective
New objective value
- delta_objective
Change in objective value
- delta_percentage
Percentage change in objective
- original_solution
Original solution
- new_solution
New solution with change applied
- changes_applied
List of changes applied
-
description:
str
-
original_objective:
float
-
new_objective:
float
-
delta_objective:
float
-
delta_percentage:
float
-
original_solution:
LXSolution[TypeVar(TModel)]
-
new_solution:
LXSolution[TypeVar(TModel)]
-
changes_applied:
List[LXWhatIfChange]
- __init__(description, original_objective, new_objective, delta_objective, delta_percentage, original_solution, new_solution, changes_applied=<factory>)
- Parameters:
description (str)
original_objective (float)
new_objective (float)
delta_objective (float)
delta_percentage (float)
original_solution (LXSolution[TModel])
new_solution (LXSolution[TModel])
changes_applied (List[LXWhatIfChange])
- Return type:
None
- class lumix.analysis.whatif.LXWhatIfChange(change_type, target_name, description, original_value=None, new_value=None, delta=None)[source]
Bases:
objectRepresents a single what-if change to explore.
Examples
Relax capacity constraint by 100 units:
LXWhatIfChange( change_type="constraint_rhs", target_name="capacity", description="Relax capacity by 100", new_value=1100.0 )
- Parameters:
-
change_type:
str
-
target_name:
str
-
description:
str