Sensitivity Analysis Example

Overview

This example demonstrates LumiX’s sensitivity analysis capabilities for understanding optimal solutions through shadow prices, reduced costs, and bottleneck identification.

Sensitivity analysis reveals which constraints are limiting profitability, which resources are most valuable, and how sensitive the solution is to parameter changes - essential for investment decisions and resource prioritization.

Problem Description

After solving a production planning problem, a manufacturing company wants to understand:

Shadow Prices: What is the marginal value of each resource?

Reduced Costs: What is the opportunity cost of each variable?

Bottlenecks: Which constraints are limiting profitability?

Investment Priorities: Where should we invest to maximize ROI?

Mathematical Foundation

Primal Problem:

\[\begin{split}\text{Maximize} \quad c^T x \\ \text{subject to} \quad Ax \leq b, \quad x \geq 0\end{split}\]

Dual Problem:

\[\begin{split}\text{Minimize} \quad b^T y \\ \text{subject to} \quad A^T y \geq c, \quad y \geq 0\end{split}\]

Shadow Prices (\(y\)): Dual variables representing marginal value of relaxing constraints.

Reduced Costs: Opportunity cost of forcing a variable to change from its optimal value.

Key Features

Shadow Price Analysis

Analyze marginal value of constraints:

    This function performs a complete sensitivity analysis workflow that reveals
    the economic insights hidden in the optimal solution:
        1. Build and solve the production model with sensitivity enabled
        2. Analyze shadow prices (dual values) for all constraints
        3. Identify binding constraints that limit profitability
        4. Find bottlenecks with high marginal values
        5. Compute reduced costs for decision variables
        6. Rank constraints by sensitivity impact
        7. Generate comprehensive sensitivity reports

    The analysis provides actionable insights for resource investment decisions,

Interpretation:

  • Shadow price > 0: Constraint is binding (at capacity)

  • Shadow price = 0: Constraint has slack (not binding)

  • Shadow price = $2.50: Each additional unit increases profit by $2.50

Bottleneck Identification

Find constraints limiting profitability:

    Interpretation: Each additional labor hour increases profit by 2.50
    ...

Notes:
    Sensitivity analysis requires:
        - The optimizer must support dual values (.enable_sensitivity())
        - The optimal solution must be available (not infeasible/unbounded)
        - Only works with LP models (not MIP)

    Key concepts demonstrated:
        - Shadow prices: marginal value of relaxing a constraint by one unit

Bottlenecks are binding constraints with significant shadow prices.

Most Sensitive Constraints

Rank constraints by sensitivity:

    invalid (though this example doesn't compute ranges).
"""

print("=" * 80)
print("SENSITIVITY ANALYSIS: Production Planning Solution Insights")
print("=" * 80)

# Build and solve model

Prioritize relaxing constraints with highest shadow prices.

Reduced Cost Analysis

Analyze variable opportunity costs:

    print("\n" + "-" * 80)
    print("OPTIMAL SOLUTION SUMMARY")
    print("-" * 80)
    print(solution.summary())

    # Generate full sensitivity report

Interpretation:

  • Reduced cost = 0: Variable in optimal basis

  • Reduced cost > 0: Increasing variable would decrease objective

Running the Example

Prerequisites:

pip install lumix
pip install ortools  # or cplex, gurobi

Run:

cd examples/09_sensitivity_analysis
python sensitivity_analysis.py

Expected Output:

====================================================================
SENSITIVITY ANALYSIS: Production Planning Solution Insights
====================================================================

OPTIMAL SOLUTION SUMMARY
--------------------------------------------------------------------
Status: optimal
Objective: $12,345.68
Solve time: 0.123s

====================================================================
CONSTRAINT SENSITIVITY ANALYSIS
====================================================================

capacity_Labor Hours:
  Shadow Price: $2.5000
  Binding: YES
  ➜ Interpretation: Each additional labor hour increases profit by $2.50

capacity_Machine Hours:
  Shadow Price: $1.2500
  Binding: YES
  ➜ Interpretation: Each additional machine hour increases profit by $1.25

capacity_Raw Materials:
  Shadow Price: $0.0000
  Binding: NO

--------------------------------------------------------------------
BINDING CONSTRAINTS (At Capacity)
--------------------------------------------------------------------
Found 2 binding constraints:
  • capacity_Labor Hours
    Shadow Price: $2.5000
  • capacity_Machine Hours
    Shadow Price: $1.2500

--------------------------------------------------------------------
TOP 5 MOST VALUABLE CONSTRAINTS TO RELAX
--------------------------------------------------------------------
  1. capacity_Labor Hours
     Shadow Price: $2.5000
     Expected ROI: $2.50 per unit relaxation

  2. capacity_Machine Hours
     Shadow Price: $1.2500
     Expected ROI: $1.25 per unit relaxation

Complete Code Walkthrough

Step 1: Solve Model with Sensitivity Enabled


    model.add_constraint(
        LXConstraint(f"capacity_{resource.name}")
        .expression(usage_expr)

Note: .enable_sensitivity() requests dual values from solver.

Step 2: Create Sensitivity Analyzer

    )

Step 3: Analyze Individual Constraints

"""Run comprehensive sensitivity analysis on the optimal solution.

This function performs a complete sensitivity analysis workflow that reveals
the economic insights hidden in the optimal solution:
    1. Build and solve the production model with sensitivity enabled
    2. Analyze shadow prices (dual values) for all constraints
    3. Identify binding constraints that limit profitability
    4. Find bottlenecks with high marginal values
    5. Compute reduced costs for decision variables
    6. Rank constraints by sensitivity impact
    7. Generate comprehensive sensitivity reports

The analysis provides actionable insights for resource investment decisions,

Step 4: Identify Bottlenecks

    ============================================================

    CONSTRAINT SENSITIVITY ANALYSIS
    Shadow Price: 2.50 per unit (Labor Hours)
    Interpretation: Each additional labor hour increases profit by 2.50
    ...

Notes:
    Sensitivity analysis requires:
        - The optimizer must support dual values (.enable_sensitivity())
        - The optimal solution must be available (not infeasible/unbounded)
        - Only works with LP models (not MIP)

    Key concepts demonstrated:
        - Shadow prices: marginal value of relaxing a constraint by one unit

Step 5: Generate Business Insights

    print(f"\nIdentified {len(bottlenecks)} bottlenecks:")
    print("\nThese constraints are limiting profitability:")
    for name in bottlenecks:
        sens = analyzer.analyze_constraint(name)
        print(f"\n  {name}:")
        print(f"    Shadow Price: ${sens.shadow_price:.4f}")
        print(f"    Impact: Relaxing by 1 unit → +${sens.shadow_price:.2f} profit")
else:
    print("\nNo significant bottlenecks identified")

# Top sensitive constraints
print("\n" + "-" * 80)
print("TOP 5 MOST VALUABLE CONSTRAINTS TO RELAX")
print("-" * 80)

top_constraints = analyzer.get_most_sensitive_constraints(top_n=5)
if top_constraints:

Learning Objectives

After completing this example, you should understand:

  1. Shadow Prices: Marginal value of relaxing constraints

  2. Reduced Costs: Opportunity cost of changing variables

  3. Binding Constraints: Identifying active constraints (bottlenecks)

  4. Duality Theory: Relationship between primal and dual problems

  5. Investment Analysis: Using sensitivity for ROI calculations

  6. Complementary Slackness: Why slack constraints have zero shadow price

Common Patterns

Pattern 1: Constraint Sensitivity Check

sens = analyzer.analyze_constraint("constraint_name")

if sens.is_binding:
    print(f"Bottleneck: ${sens.shadow_price:.2f} per unit")
else:
    print(f"Has slack: {sens.slack:.2f} units available")

Pattern 2: Investment Decision

labor_sens = analyzer.analyze_constraint("capacity_Labor")

# Decision logic
if labor_sens.shadow_price > hiring_cost_per_hour:
    print("✓ HIRE: Shadow price exceeds cost")
    roi = labor_sens.shadow_price - hiring_cost_per_hour
    print(f"  Expected profit: ${roi:.2f} per hour")
else:
    print("✗ DON'T HIRE: Cost exceeds value")

Pattern 3: Prioritize Improvements

# Get top constraints to relax
top_constraints = analyzer.get_most_sensitive_constraints(top_n=5)

for i, (name, sens) in enumerate(top_constraints, 1):
    print(f"{i}. {name}: ${sens.shadow_price:.2f} per unit")

Business Insights Generated

Resource Investment Priorities

Based on shadow prices, prioritize investment in:

1. Labor Hours
   Marginal Value: $2.50 per unit
   Status: BINDING (at capacity)
   ✓ HIGH PRIORITY: Strong ROI expected

2. Machine Hours
   Marginal Value: $1.25 per unit
   Status: BINDING (at capacity)
   → MODERATE PRIORITY: Positive ROI

3. Raw Materials
   Marginal Value: $0.00 per unit
   Status: NOT BINDING (excess capacity)
   ✗ LOW PRIORITY: No value from expansion

Hiring Recommendation

► HIRING RECOMMENDATION:
  - Labor capacity is constraining profit
  - Each additional labor hour adds $2.50 profit
  - Consider hiring if cost per hour < $2.50

Risk Assessment

⚠ HIGH SENSITIVITY:
  - 2 constraints are binding
  - Solution highly sensitive to parameter changes
  - Small capacity variations significantly impact profit
  - Recommendation: Build buffer capacity

Sensitivity Metrics Explained

Shadow Prices (Dual Values)

Definition: Marginal value of relaxing a constraint by one unit.

Example: Labor has shadow price = $2.50

  • Increasing labor by 1 hour → Profit increases by $2.50

  • Decreasing labor by 1 hour → Profit decreases by $2.50

Business Use:

  • Maximum price to pay for one more unit

  • Value of capacity expansion

  • Investment prioritization

Reduced Costs

Definition: Opportunity cost of forcing a variable to change.

Example: Production variable has reduced cost = $0.50

  • Forcing production up by 1 unit → Profit decreases by $0.50

  • Variable currently at lower bound

  • Not economical to increase

Business Use:

  • Identify products worth producing more

  • Understand why some products not produced

  • Product portfolio decisions

Complementary Slackness

At optimality:

  • If constraint has slack → Shadow price = 0

  • If shadow price > 0 → Constraint is binding (no slack)

  • If variable at bound → Reduced cost may be non-zero

  • If reduced cost = 0 → Variable in basis (not at bound)

Extending the Example

Try These Modifications

  1. Multi-Period: Analyze sensitivity over time

  2. Stochastic: Probabilistic sensitivity analysis

  3. Range Analysis: Valid ranges for shadow prices

  4. Parametric: How shadow prices change with RHS

  5. Interactive Dashboard: Real-time sensitivity visualization

Next Steps

After mastering this example:

  1. Example 08 (Scenario Analysis): Strategic planning scenarios

  2. Example 10 (What-If Analysis): Quick tactical decisions

  3. Advanced Duality Theory: Linear programming duality

See Also

Related Examples:

API Reference:

Files in This Example

  • sensitivity_analysis.py - Main sensitivity analysis demonstration

  • sample_data.py - Data models (Product, Resource) and sample data

  • README.md - Detailed documentation and usage guide