# Complete Optimization Problem and Solution: game_injury

## 1. Problem Context and Goals

### Context  
A sports league is focused on reducing the total number of injuries across all scheduled games while ensuring that stadiums operate within their capacity limits and maintain a minimum average attendance. The league must make decisions on which games to schedule, represented by binary decision variables, and manage the usage percentage of each stadium, represented by continuous decision variables. The injury risk for each game is calculated based on historical data, specifically the ratio of historical injuries to the total number of games played. This risk value is used as a coefficient in the optimization objective.  

The league operates under specific business configurations:  
- **Maximum capacity percentage for each stadium**: Each stadium has a predefined maximum capacity percentage, ensuring that usage does not exceed safe operational limits.  
- **Minimum average attendance required for each stadium**: To meet financial and operational goals, each stadium must maintain a minimum average attendance level.  

These configurations ensure that the optimization problem remains linear, avoiding complex nonlinear relationships such as variable products or divisions. The operational parameters and business logic are designed to align with a linear optimization formulation, focusing on minimizing injury risk while adhering to capacity and attendance constraints.

### Goals  
The primary goal of the optimization is to minimize the total injury risk across all scheduled games. This is achieved by summing the injury risk for each game multiplied by the decision to schedule that game. Success is measured by achieving the lowest possible total injury risk while ensuring that all stadiums operate within their capacity limits and meet the minimum average attendance requirements. The optimization process uses realistic business data and configurations to ensure that the results are both mathematically consistent and operationally feasible.

## 2. Constraints  

The optimization problem is subject to the following constraints:  
1. **Stadium Capacity Constraint**: The total usage of each stadium, calculated as the sum of scheduled games multiplied by the stadium's capacity percentage, must not exceed the stadium's maximum capacity percentage. This ensures that stadiums operate within safe and manageable limits.  
2. **Minimum Attendance Constraint**: The total attendance across all scheduled games for each stadium must meet or exceed the minimum average attendance requirement. This ensures that financial and operational goals are met while maintaining fan engagement.  

These constraints are designed to align with linear mathematical forms, avoiding any nonlinear relationships such as variable products or divisions. They ensure that the optimization problem remains tractable and aligned with the league's operational requirements.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 1 Database Schema
-- Objective: Schema changes include creating tables for injury risk, game scheduling, and stadium usage. Configuration logic updates include scalar parameters for stadium capacity and minimum average attendance, and formulas for injury risk calculation.

CREATE TABLE injury_risk (
  game_id INTEGER,
  risk_value FLOAT
);

CREATE TABLE game_scheduling (
  game_id INTEGER,
  is_scheduled BOOLEAN
);

CREATE TABLE stadium_usage (
  stadium_id INTEGER,
  usage_percentage FLOAT
);

CREATE TABLE stadium (
  stadium_id INTEGER,
  capacity_percentage FLOAT,
  average_attendance INTEGER
);
```

### Data Dictionary  
- **Injury Risk Table**:  
  - **Purpose**: Stores the risk of injury for each game, calculated based on historical data.  
  - **Optimization Role**: Provides coefficients for the objective function, representing the injury risk for each game.  

- **Game Scheduling Table**:  
  - **Purpose**: Stores decisions on whether each game is scheduled.  
  - **Optimization Role**: Represents binary decision variables in the optimization problem.  

- **Stadium Usage Table**:  
  - **Purpose**: Tracks the percentage of capacity used for each stadium.  
  - **Optimization Role**: Represents continuous decision variables in the optimization problem.  

- **Stadium Table**:  
  - **Purpose**: Stores capacity and attendance data for each stadium.  
  - **Optimization Role**: Provides constraint bounds for stadium capacity and minimum average attendance.  

### Current Stored Values  
```sql
-- Iteration 1 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on realistic sports league scenarios, considering historical injury rates, stadium capacities, and attendance trends. Data was generated to ensure a balance between minimizing injury risk and meeting attendance and capacity constraints.

-- Realistic data for injury_risk
INSERT INTO injury_risk (game_id, risk_value) VALUES (1, 0.15);
INSERT INTO injury_risk (game_id, risk_value) VALUES (2, 0.1);
INSERT INTO injury_risk (game_id, risk_value) VALUES (3, 0.2);

-- Realistic data for game_scheduling
INSERT INTO game_scheduling (game_id, is_scheduled) VALUES (1, True);
INSERT INTO game_scheduling (game_id, is_scheduled) VALUES (2, False);
INSERT INTO game_scheduling (game_id, is_scheduled) VALUES (3, True);

-- Realistic data for stadium_usage
INSERT INTO stadium_usage (stadium_id, usage_percentage) VALUES (1, 0.75);
INSERT INTO stadium_usage (stadium_id, usage_percentage) VALUES (2, 0.8);
INSERT INTO stadium_usage (stadium_id, usage_percentage) VALUES (3, 0.7);

-- Realistic data for stadium
INSERT INTO stadium (stadium_id, capacity_percentage, average_attendance) VALUES (1, 0.85, 5500);
INSERT INTO stadium (stadium_id, capacity_percentage, average_attendance) VALUES (2, 0.9, 6000);
INSERT INTO stadium (stadium_id, capacity_percentage, average_attendance) VALUES (3, 0.8, 5000);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- \( x_i \): Binary decision variable indicating whether game \( i \) is scheduled (1 if scheduled, 0 otherwise).  
  **Source**: `game_scheduling.is_scheduled`  
- \( y_j \): Continuous decision variable representing the usage percentage of stadium \( j \).  
  **Source**: `stadium_usage.usage_percentage`  

#### Objective Function
Minimize the total injury risk across all scheduled games:  
\[
\text{Minimize} \quad \sum_{i} \text{risk\_value}_i \times x_i
\]  
**Coefficient Source**: `injury_risk.risk_value`  

#### Constraints
1. **Stadium Capacity Constraint**: The total usage of each stadium \( j \) must not exceed its maximum capacity percentage.  
\[
\sum_{i \in \text{games at stadium } j} x_i \times \text{capacity\_percentage}_j \leq y_j \times \text{capacity\_percentage}_j \quad \forall j
\]  
**Coefficient Source**: `stadium.capacity_percentage`  

2. **Minimum Attendance Constraint**: The total attendance across all scheduled games for each stadium \( j \) must meet or exceed the minimum average attendance requirement.  
\[
\sum_{i \in \text{games at stadium } j} x_i \times \text{average\_attendance}_j \geq \text{average\_attendance}_j \quad \forall j
\]  
**Coefficient Source**: `stadium.average_attendance`  

3. **Binary and Continuous Variable Constraints**:  
\[
x_i \in \{0, 1\} \quad \forall i  
\]  
\[
0 \leq y_j \leq 1 \quad \forall j
\]  

#### Data Source Verification
- **Objective Function Coefficient**: `injury_risk.risk_value`  
- **Stadium Capacity Constraint Coefficient**: `stadium.capacity_percentage`  
- **Minimum Attendance Constraint Coefficient**: `stadium.average_attendance`  

This formulation provides a complete, immediately solvable linear mathematical model with all numerical coefficients derived from the provided data.

## 5. Gurobipy Implementation

```python
#!/usr/bin/env python3
"""
Gurobipy 12.0.2 Implementation for Game Injury Optimization Problem
"""

import gurobipy as gp
from gurobipy import GRB

def game_injury_optimization():
    """Optimization model to minimize total injury risk across scheduled games."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("game_injury_optimization")
    
    # Sample data from the problem description
    injury_risk = {1: 0.15, 2: 0.1, 3: 0.2}
    game_scheduling = {1: True, 2: False, 3: True}
    stadium_usage = {1: 0.75, 2: 0.8, 3: 0.7}
    stadium = {
        1: {'capacity_percentage': 0.85, 'average_attendance': 5500},
        2: {'capacity_percentage': 0.9, 'average_attendance': 6000},
        3: {'capacity_percentage': 0.8, 'average_attendance': 5000}
    }
    
    # CRITICAL: Validate array lengths before loops
    assert len(injury_risk) == len(game_scheduling), "Array length mismatch for injury_risk and game_scheduling"
    assert len(stadium_usage) == len(stadium), "Array length mismatch for stadium_usage and stadium"
    
    # 2. VARIABLES
    # Binary decision variables for game scheduling
    x = {i: model.addVar(vtype=GRB.BINARY, name=f"x_{i}") for i in injury_risk.keys()}
    
    # Continuous decision variables for stadium usage
    y = {j: model.addVar(vtype=GRB.CONTINUOUS, name=f"y_{j}", lb=0, ub=1) for j in stadium_usage.keys()}
    
    # 3. OBJECTIVE FUNCTION
    # Minimize total injury risk across all scheduled games
    model.setObjective(gp.quicksum(injury_risk[i] * x[i] for i in injury_risk.keys()), GRB.MINIMIZE)
    
    # 4. CONSTRAINTS
    # Stadium Capacity Constraint
    for j in stadium.keys():
        model.addConstr(
            gp.quicksum(x[i] * stadium[j]['capacity_percentage'] for i in injury_risk.keys()) <= y[j] * stadium[j]['capacity_percentage'],
            name=f"stadium_capacity_{j}"
        )
    
    # Minimum Attendance Constraint
    for j in stadium.keys():
        model.addConstr(
            gp.quicksum(x[i] * stadium[j]['average_attendance'] for i in injury_risk.keys()) >= stadium[j]['average_attendance'],
            name=f"min_attendance_{j}"
        )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in injury_risk.keys():
            if x[i].x > 1e-6:
                print(f"Game {i} is scheduled: {x[i].x:.0f}")
        for j in stadium_usage.keys():
            print(f"Stadium {j} usage percentage: {y[j].x:.2f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Execute the optimization model
if __name__ == "__main__":
    game_injury_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 0.1
**Execution Time**: 0.21 seconds
**Reliability**: high
**Analysis**: Gurobipy achieved the optimal solution with the lowest execution time, indicating high efficiency and reliability.

## 6. DOCplex Implementation

```python
#!/usr/bin/env python3
"""
DOCplex implementation for game injury optimization problem
"""

from docplex.mp.model import Model

def game_injury_optimization():
    """Optimize game scheduling to minimize injury risk while meeting stadium constraints"""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="game_injury_optimization")
    
    # Data from the problem context
    game_ids = [1, 2, 3]
    stadium_ids = [1, 2, 3]
    
    injury_risk = {1: 0.15, 2: 0.1, 3: 0.2}
    stadium_capacity = {1: 0.85, 2: 0.9, 3: 0.8}
    stadium_attendance = {1: 5500, 2: 6000, 3: 5000}
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(game_ids) == len(injury_risk), "Game data length mismatch"
    assert len(stadium_ids) == len(stadium_capacity) == len(stadium_attendance), "Stadium data length mismatch"
    
    # Safe indexing ranges
    game_range = range(len(game_ids))
    stadium_range = range(len(stadium_ids))
    
    # 2. VARIABLES
    # Binary decision variable for game scheduling
    x = {i: mdl.binary_var(name=f"x_{i}") for i in game_range}
    
    # Continuous decision variable for stadium usage percentage
    y = {j: mdl.continuous_var(name=f"y_{j}", lb=0, ub=1) for j in stadium_range}
    
    # 3. OBJECTIVE FUNCTION
    # Minimize total injury risk across all scheduled games
    objective = mdl.sum(injury_risk[game_ids[i]] * x[i] for i in game_range)
    mdl.minimize(objective)
    
    # 4. CONSTRAINTS
    
    # Stadium Capacity Constraint
    for j in stadium_range:
        mdl.add_constraint(
            mdl.sum(x[i] for i in game_range) * stadium_capacity[stadium_ids[j]] <= y[j] * stadium_capacity[stadium_ids[j]],
            ctname=f"stadium_capacity_{j}"
        )
    
    # Minimum Attendance Constraint
    for j in stadium_range:
        mdl.add_constraint(
            mdl.sum(x[i] for i in game_range) * stadium_attendance[stadium_ids[j]] >= stadium_attendance[stadium_ids[j]],
            ctname=f"min_attendance_{j}"
        )
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in game_range:
            value = solution.get_value(x[i])
            if value > 1e-6:
                print(f"Game {game_ids[i]} is scheduled: {value:.3f}")
        for j in stadium_range:
            value = solution.get_value(y[j])
            print(f"Stadium {stadium_ids[j]} usage percentage: {value:.3f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Execute the optimization
if __name__ == "__main__":
    game_injury_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 0.1
**Execution Time**: 1.16 seconds
**Reliability**: medium
**Analysis**: DOCplex also found the optimal solution but took significantly longer than Gurobipy, suggesting lower efficiency.

## 7. Pyomo Implementation

```python
#!/usr/bin/env python3
"""
Pyomo 6.9.2 Implementation for Game Injury Optimization Problem
"""

import pyomo.environ as pyo
from pyomo.opt import SolverFactory

def game_injury_optimization():
    """Optimization model to minimize total injury risk while meeting stadium capacity and attendance constraints."""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # Example data (replace with actual data loading logic)
    games = [1, 2, 3]
    stadiums = [1, 2, 3]
    
    injury_risk = {1: 0.15, 2: 0.1, 3: 0.2}
    stadium_capacity = {1: 0.85, 2: 0.9, 3: 0.8}
    average_attendance = {1: 5500, 2: 6000, 3: 5000}
    
    # Validate array lengths
    assert len(games) == len(injury_risk), "Mismatch in games and injury risk data"
    assert len(stadiums) == len(stadium_capacity) == len(average_attendance), "Mismatch in stadium data"
    
    # 3. SETS
    model.G = pyo.Set(initialize=games)  # Set of games
    model.S = pyo.Set(initialize=stadiums)  # Set of stadiums
    
    # 4. PARAMETERS
    model.risk_value = pyo.Param(model.G, initialize=injury_risk)
    model.capacity_percentage = pyo.Param(model.S, initialize=stadium_capacity)
    model.average_attendance = pyo.Param(model.S, initialize=average_attendance)
    
    # 5. VARIABLES
    model.x = pyo.Var(model.G, within=pyo.Binary)  # Binary decision variable for game scheduling
    model.y = pyo.Var(model.S, within=pyo.NonNegativeReals, bounds=(0, 1))  # Continuous decision variable for stadium usage
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.risk_value[i] * model.x[i] for i in model.G)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.minimize)
    
    # 7. CONSTRAINTS
    
    # Stadium Capacity Constraint
    def capacity_rule(model, j):
        return sum(model.x[i] for i in model.G) * model.capacity_percentage[j] <= model.y[j] * model.capacity_percentage[j]
    model.capacity_constraint = pyo.Constraint(model.S, rule=capacity_rule)
    
    # Minimum Attendance Constraint
    def attendance_rule(model, j):
        return sum(model.x[i] for i in model.G) * model.average_attendance[j] >= model.average_attendance[j]
    model.attendance_constraint = pyo.Constraint(model.S, rule=attendance_rule)
    
    # 8. SOLVING WITH GUROBI
    solver = SolverFactory('gurobi')
    
    # Optional: Set solver options
    solver.options['TimeLimit'] = 300  # 5 minutes
    solver.options['MIPGap'] = 0.01    # 1% gap
    
    # Solve the model
    results = solver.solve(model, tee=True)  # tee=True shows solver output
    
    # 9. RESULT PROCESSING
    # Check solver status
    if results.solver.termination_condition == pyo.TerminationCondition.optimal:
        print("Optimal solution found!")
        print(f"Optimal value: {pyo.value(model.objective)}")
        
        # Extract variable values
        print("\nVariable values:")
        for i in model.G:
            x_val = pyo.value(model.x[i])
            if x_val > 1e-6:  # Only print non-zero values
                print(f"x[{i}] = {int(x_val)}")
        
        for j in model.S:
            y_val = pyo.value(model.y[j])
            print(f"y[{j}] = {y_val:.3f}")
        
    elif results.solver.termination_condition == pyo.TerminationCondition.infeasible:
        print("Problem is infeasible")
    elif results.solver.termination_condition == pyo.TerminationCondition.unbounded:
        print("Problem is unbounded")
    else:
        print(f"Solver terminated with condition: {results.solver.termination_condition}")
    
    return model

# Execute the optimization
if __name__ == "__main__":
    game_injury_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 0.1
**Execution Time**: 1.02 seconds
**Reliability**: medium
**Analysis**: Pyomo achieved the optimal solution but had a longer execution time compared to Gurobipy, indicating moderate efficiency.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 0.10 | 0.21s | N/A | N/A |
| Docplex | OPTIMAL | 0.10 | 1.16s | N/A | N/A |
| Pyomo | OPTIMAL | 0.10 | 1.02s | N/A | N/A |

### Solver Consistency Analysis
**Result**: All solvers produced consistent results ✓
**Consistent Solvers**: gurobipy, docplex, pyomo
**Majority Vote Optimal Value**: 0.1

### Final Recommendation
**Recommended Optimal Value**: 0.1
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy is recommended due to its high reliability and significantly faster execution time compared to DOCplex and Pyomo.

### Business Interpretation
**Overall Strategy**: The optimal solution minimizes the total injury risk across all scheduled games, ensuring safer game scheduling.
**Objective Value Meaning**: The optimal objective value of 0.1 represents the minimized total injury risk across all scheduled games.
**Resource Allocation Summary**: Resources should be allocated to ensure that the total usage of each stadium does not exceed its maximum capacity percentage and that the minimum attendance requirements are met.
**Implementation Recommendations**: Implement the optimal game schedule as determined by the solver, ensuring compliance with stadium capacity and attendance constraints to minimize injury risk.