# Complete Optimization Problem and Solution: game_injury

## 1. Problem Context and Goals

### Context  
The sports league is focused on optimizing the scheduling of its games to achieve the highest possible attendance while minimizing the risk of player injuries. The league's decision-making process involves determining the number of home games to be played at each stadium and deciding which games to schedule, ensuring that these decisions align with the league's operational parameters. The primary decision variables include the number of home games scheduled at each stadium, represented as an integer, and the scheduling of games, represented as a binary decision. The league aims to maximize total attendance by considering the average attendance expected at each stadium and minimizing the injury risk associated with each game. The business configuration includes parameters such as the total number of home games that can be scheduled, which serves as a constraint bound, and the maximum allowable injury risk for scheduling, ensuring player safety while maintaining competitive play.

### Goals  
The primary goal of the optimization is to maximize the total attendance across all scheduled games. This involves leveraging the average attendance figures for each stadium and minimizing the injury risk associated with each game. The success of this optimization is measured by the total attendance achieved, which is calculated by considering the average attendance at each stadium and the injury risk for each game. The optimization goal is clearly defined in natural language, focusing on maximizing attendance while adhering to the constraints set by the league's operational parameters.

## 2. Constraints    

The optimization problem is subject to several constraints that ensure the feasibility and practicality of the scheduling decisions:

- The total number of home games scheduled across all stadiums must not exceed the league's predefined limit, ensuring a balanced distribution of games.
- The cumulative injury risk associated with the scheduled games must remain within the maximum allowable limit, prioritizing player safety.
- The number of home games scheduled at each stadium must not exceed the stadium's capacity, as determined by a percentage of the total home games allowed, ensuring that each stadium operates within its capacity limits.

These constraints are articulated in business terms that naturally lead to linear mathematical forms, avoiding any nonlinear relationships.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 1 Database Schema
-- Objective: Schema changes include adding missing tables for injury risk and game schedule, and updating configuration logic for scalar parameters and formulas.

CREATE TABLE stadium (
  Average_Attendance INTEGER,
  Capacity_Percentage FLOAT,
  Home_Games INTEGER
);

CREATE TABLE injury_risk (
  Risk FLOAT
);

CREATE TABLE game_schedule (
  Scheduled BOOLEAN
);
```

### Data Dictionary  
The data dictionary provides a comprehensive mapping of the tables and columns to their business purposes and roles in the optimization process:

- **Stadium Table**: This table contains information about each stadium, including the average attendance expected, the maximum capacity utilization allowed, and the number of home games scheduled. The average attendance is used in the objective function to calculate total attendance, while the capacity percentage is used to limit the number of home games.

- **Injury Risk Table**: This table stores the risk of injury associated with each game. The injury risk is a critical factor in the objective function, as it is used to minimize injuries while scheduling games.

- **Game Schedule Table**: This table indicates whether a game is scheduled. The scheduled column is a binary variable that serves as a decision variable for game scheduling.

### Current Stored Values  
```sql
-- Iteration 1 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on typical stadium capacities, average attendance figures, and realistic injury risks in sports leagues. The approach ensures that the optimization problem is both meaningful and solvable by balancing attendance and injury risk.

-- Realistic data for stadium
INSERT INTO stadium (Average_Attendance, Capacity_Percentage, Home_Games) VALUES (8000, 0.85, 6);
INSERT INTO stadium (Average_Attendance, Capacity_Percentage, Home_Games) VALUES (12000, 0.9, 8);
INSERT INTO stadium (Average_Attendance, Capacity_Percentage, Home_Games) VALUES (15000, 0.95, 6);

-- Realistic data for injury_risk
INSERT INTO injury_risk (Risk) VALUES (0.02);
INSERT INTO injury_risk (Risk) VALUES (0.03);
INSERT INTO injury_risk (Risk) VALUES (0.04);
INSERT INTO injury_risk (Risk) VALUES (0.01);
INSERT INTO injury_risk (Risk) VALUES (0.05);

-- Realistic data for game_schedule
INSERT INTO game_schedule (Scheduled) VALUES (True);
INSERT INTO game_schedule (Scheduled) VALUES (False);
INSERT INTO game_schedule (Scheduled) VALUES (True);
INSERT INTO game_schedule (Scheduled) VALUES (False);
INSERT INTO game_schedule (Scheduled) VALUES (True);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- \( x_i \): Binary variable indicating whether game \( i \) is scheduled (1 if scheduled, 0 otherwise).
- \( y_j \): Integer variable representing the number of home games scheduled at stadium \( j \).

#### Objective Function
Maximize total attendance while minimizing injury risk:
\[ \text{Maximize } \sum_{j} \text{Average_Attendance}_j \times y_j - \sum_{i} \text{Risk}_i \times x_i \]

Data Source Verification:
- \(\text{Average_Attendance}_j\) from `stadium.Average_Attendance`
- \(\text{Risk}_i\) from `injury_risk.Risk`

#### Constraints
1. Total number of home games scheduled across all stadiums must not exceed the league's predefined limit:
   \[ \sum_{j} y_j \leq \text{Total_Home_Games_Limit} \]

2. Cumulative injury risk associated with the scheduled games must remain within the maximum allowable limit:
   \[ \sum_{i} \text{Risk}_i \times x_i \leq \text{Max_Allowable_Injury_Risk} \]

3. Number of home games scheduled at each stadium must not exceed the stadium's capacity:
   \[ y_j \leq \text{Capacity_Percentage}_j \times \text{Total_Home_Games_Limit} \]

4. Each game can only be scheduled once:
   \[ x_i \in \{0, 1\} \]

5. Number of home games scheduled at each stadium must be non-negative and integer:
   \[ y_j \geq 0, \quad y_j \in \mathbb{Z}^+ \]

Data Source Verification:
- \(\text{Capacity_Percentage}_j\) from `stadium.Capacity_Percentage`
- \(\text{Total_Home_Games_Limit}\) and \(\text{Max_Allowable_Injury_Risk}\) are parameters from business configuration.

This formulation ensures that the optimization problem is both meaningful and solvable by balancing attendance and injury risk while adhering to the constraints set by the league's operational parameters.

## 5. Gurobipy Implementation

```python
# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_game_injury():
    """Optimize game scheduling to maximize attendance and minimize injury risk."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("game_injury")
    
    # Data from the database
    average_attendance = [8000, 12000, 15000]
    capacity_percentage = [0.85, 0.9, 0.95]
    home_games = [6, 8, 6]
    injury_risk = [0.02, 0.03, 0.04, 0.01, 0.05]
    scheduled = [True, False, True, False, True]
    
    # Parameters
    total_home_games_limit = 20
    max_allowable_injury_risk = 0.1
    
    # Validate array lengths
    n_stadiums = len(average_attendance)
    n_games = len(injury_risk)
    assert len(capacity_percentage) == n_stadiums, "Array length mismatch for stadiums"
    assert len(home_games) == n_stadiums, "Array length mismatch for home games"
    assert len(scheduled) == n_games, "Array length mismatch for games"
    
    # 2. VARIABLES
    x = {i: model.addVar(vtype=GRB.BINARY, name=f"x_{i}") for i in range(n_games)}
    y = {j: model.addVar(vtype=GRB.INTEGER, name=f"y_{j}", lb=0) for j in range(n_stadiums)}
    
    # 3. OBJECTIVE FUNCTION
    model.setObjective(
        gp.quicksum(average_attendance[j] * y[j] for j in range(n_stadiums)) -
        gp.quicksum(injury_risk[i] * x[i] for i in range(n_games)),
        GRB.MAXIMIZE
    )
    
    # 4. CONSTRAINTS
    # Total number of home games scheduled across all stadiums
    model.addConstr(gp.quicksum(y[j] for j in range(n_stadiums)) <= total_home_games_limit, name="total_home_games_limit")
    
    # Cumulative injury risk
    model.addConstr(gp.quicksum(injury_risk[i] * x[i] for i in range(n_games)) <= max_allowable_injury_risk, name="max_injury_risk")
    
    # Stadium capacity constraints
    for j in range(n_stadiums):
        model.addConstr(y[j] <= capacity_percentage[j] * total_home_games_limit, name=f"capacity_{j}")
    
    # Each game can only be scheduled once
    for i in range(n_games):
        model.addConstr(x[i] <= 1, name=f"schedule_once_{i}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for j in range(n_stadiums):
            if y[j].x > 1e-6:
                print(f"Home games at stadium {j}: {y[j].x:.0f}")
        for i in range(n_games):
            if x[i].x > 1e-6:
                print(f"Game {i} scheduled: {x[i].x:.0f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
optimize_game_injury()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 297000.0
**Execution Time**: 0.50 seconds
**Reliability**: high
**Analysis**: Gurobipy found an optimal solution quickly with a very short execution time, indicating efficient performance and reliability.

## 6. DOCplex Implementation

```python
# Complete DOCPLEX implementation

from docplex.mp.model import Model

def game_injury_optimization():
    """Optimize game scheduling to maximize attendance and minimize injury risk."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="game_injury")
    
    # Data from the problem context
    average_attendance = [8000, 12000, 15000]
    capacity_percentage = [0.85, 0.9, 0.95]
    home_games = [6, 8, 6]
    injury_risks = [0.02, 0.03, 0.04, 0.01, 0.05]
    total_home_games_limit = 20
    max_allowable_injury_risk = 0.1
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(average_attendance) == len(capacity_percentage) == len(home_games), "Array length mismatch"
    safe_range_stadiums = range(len(average_attendance))
    safe_range_games = range(len(injury_risks))
    
    # 2. VARIABLES
    x = {i: mdl.binary_var(name=f"x_{i}") for i in safe_range_games}
    y = {j: mdl.integer_var(name=f"y_{j}", lb=0) for j in safe_range_stadiums}
    
    # 3. OBJECTIVE FUNCTION
    objective = mdl.sum(average_attendance[j] * y[j] for j in safe_range_stadiums) - mdl.sum(injury_risks[i] * x[i] for i in safe_range_games)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    # Total number of home games constraint
    mdl.add_constraint(mdl.sum(y[j] for j in safe_range_stadiums) <= total_home_games_limit, ctname="total_home_games_limit")
    
    # Cumulative injury risk constraint
    mdl.add_constraint(mdl.sum(injury_risks[i] * x[i] for i in safe_range_games) <= max_allowable_injury_risk, ctname="max_injury_risk")
    
    # Stadium capacity constraints
    for j in safe_range_stadiums:
        mdl.add_constraint(y[j] <= capacity_percentage[j] * total_home_games_limit, ctname=f"capacity_{j}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for j in safe_range_stadiums:
            print(f"Home games at stadium {j}: {solution.get_value(y[j])}")
        for i in safe_range_games:
            print(f"Game {i} scheduled: {solution.get_value(x[i])}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Run the optimization
game_injury_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 297000.0
**Execution Time**: 5.81 seconds
**Reliability**: high
**Analysis**: DOCplex also found an optimal solution, but with a longer execution time compared to Gurobipy, suggesting it is reliable but less efficient.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

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

def game_injury_optimization():
    """Optimization for scheduling games to maximize attendance and minimize injury risk."""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # Assuming these are the given data from the database
    average_attendance = [8000, 12000, 15000]
    capacity_percentage = [0.85, 0.9, 0.95]
    home_games = [6, 8, 6]
    injury_risks = [0.02, 0.03, 0.04, 0.01, 0.05]
    total_home_games_limit = 20
    max_allowable_injury_risk = 0.1
    
    # Validate array lengths
    n_stadiums = len(average_attendance)
    n_games = len(injury_risks)
    assert len(capacity_percentage) == n_stadiums, "Array length mismatch for stadium data"
    assert len(home_games) == n_stadiums, "Array length mismatch for home games data"
    
    # 3. SETS
    model.J = pyo.RangeSet(1, n_stadiums)  # Stadiums
    model.I = pyo.RangeSet(1, n_games)     # Games
    
    # 4. PARAMETERS
    model.Average_Attendance = pyo.Param(model.J, initialize={j+1: average_attendance[j] for j in range(n_stadiums)})
    model.Capacity_Percentage = pyo.Param(model.J, initialize={j+1: capacity_percentage[j] for j in range(n_stadiums)})
    model.Risk = pyo.Param(model.I, initialize={i+1: injury_risks[i] for i in range(n_games)})
    
    # 5. VARIABLES
    model.x = pyo.Var(model.I, within=pyo.Binary)  # Game scheduling
    model.y = pyo.Var(model.J, within=pyo.NonNegativeIntegers)  # Home games at each stadium
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.Average_Attendance[j] * model.y[j] for j in model.J) - sum(model.Risk[i] * model.x[i] for i in model.I)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    # Total number of home games constraint
    def total_home_games_rule(model):
        return sum(model.y[j] for j in model.J) <= total_home_games_limit
    model.total_home_games_constraint = pyo.Constraint(rule=total_home_games_rule)
    
    # Cumulative injury risk constraint
    def injury_risk_rule(model):
        return sum(model.Risk[i] * model.x[i] for i in model.I) <= max_allowable_injury_risk
    model.injury_risk_constraint = pyo.Constraint(rule=injury_risk_rule)
    
    # Stadium capacity constraint
    def capacity_rule(model, j):
        return model.y[j] <= model.Capacity_Percentage[j] * total_home_games_limit
    model.capacity_constraint = pyo.Constraint(model.J, rule=capacity_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 j in model.J:
            y_val = pyo.value(model.y[j])
            print(f"y[{j}] = {int(y_val)}")
        
        for i in model.I:
            x_val = pyo.value(model.x[i])
            if x_val > 1e-6:  # Only print non-zero values
                print(f"x[{i}] = {int(x_val)}")
        
    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

# Run the optimization
game_injury_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 297000.0
**Execution Time**: 2.15 seconds
**Reliability**: high
**Analysis**: Pyomo achieved an optimal solution with a moderate execution time, indicating a balance between efficiency and reliability.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 297000.00 | 0.50s | N/A | N/A |
| Docplex | OPTIMAL | 297000.00 | 5.81s | N/A | N/A |
| Pyomo | OPTIMAL | 297000.00 | 2.15s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 297000.0
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy is preferred due to its high reliability and the fastest execution time, making it the most efficient choice for this problem.

### Business Interpretation
**Overall Strategy**: The optimal solution maximizes attendance while minimizing injury risk, adhering to all constraints.
**Objective Value Meaning**: The optimal objective value of 297000.0 represents the maximum achievable attendance minus the weighted injury risk, reflecting the best trade-off between these factors.
**Resource Allocation Summary**: Resources should be allocated to schedule games that maximize attendance while keeping injury risk within acceptable limits.
**Implementation Recommendations**: Implement the schedule as determined by the optimal solution, ensuring compliance with all constraints and monitoring injury risk closely.