# Complete Optimization Problem and Solution: entertainment_awards

## 1. Problem Context and Goals

### Context  
The film festival aims to maximize audience engagement by selecting a subset of artworks to nominate for each festival. The decision to nominate an artwork is binary, meaning each artwork can either be nominated or not for a specific festival. The engagement score for each artwork at a festival reflects its potential to attract and engage the audience. The festival must also adhere to two key operational constraints:  
1. **Maximum Nominations per Festival**: Each festival has a predefined limit on the number of artworks that can be nominated.  
2. **Minimum Diversity Requirement**: The nominated artworks must collectively meet a minimum diversity score, ensuring a balanced representation of different artwork types.  

The business configuration includes a scalar parameter, the minimum required diversity score, which is set to 0.75. This ensures that the selection of artworks maintains a moderate level of diversity while maximizing audience engagement.  

### Goals  
The primary goal of this optimization problem is to maximize the total audience engagement across all festivals. This is achieved by selecting the optimal set of artworks to nominate, considering their engagement scores and adhering to the operational constraints. Success is measured by the sum of engagement scores for the nominated artworks, ensuring that the festival’s audience engagement is as high as possible while respecting the nomination limits and diversity requirements.  

## 2. Constraints  

The optimization problem is subject to the following constraints:  
1. **Maximum Nominations per Festival**: For each festival, the total number of nominated artworks cannot exceed the predefined maximum number of nominations allowed for that festival.  
2. **Minimum Diversity Requirement**: For each festival, the combined diversity score of the nominated artworks must meet or exceed the minimum required diversity score of 0.75. This ensures a balanced selection of artwork types.  

These constraints are designed to ensure that the festival’s operational limits and diversity goals are met while maximizing audience engagement.  

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 3 Database Schema
-- Objective: Schema changes include adding a minimum diversity score constraint to the business configuration logic and updating the data dictionary to reflect this addition. No new tables were created or deleted as the existing schema adequately supports the optimization requirements.

CREATE TABLE engagement_scores (
  Artwork_ID INTEGER,
  Festival_ID INTEGER,
  score FLOAT
);

CREATE TABLE festival_nominations (
  Festival_ID INTEGER,
  max_nominations INTEGER
);

CREATE TABLE artwork_types (
  Type STRING,
  diversity_score FLOAT
);

CREATE TABLE nomination_decisions (
  Artwork_ID INTEGER,
  Festival_ID INTEGER,
  decision BOOLEAN
);
```

### Data Dictionary  
- **engagement_scores**: Contains the engagement scores for artworks at specific festivals.  
  - *Artwork_ID*: Unique identifier for the artwork.  
  - *Festival_ID*: Unique identifier for the festival.  
  - *score*: Engagement score for the artwork at the festival, used as the coefficient in the optimization objective.  

- **festival_nominations**: Defines the maximum number of nominations allowed per festival.  
  - *Festival_ID*: Unique identifier for the festival.  
  - *max_nominations*: Maximum number of nominations allowed for the festival, used as a constraint bound.  

- **artwork_types**: Lists the types of artworks and their associated diversity scores.  
  - *Type*: Type of artwork (e.g., Film, Short, Documentary).  
  - *diversity_score*: Diversity score for the artwork type, used in the diversity constraint.  

- **nomination_decisions**: Represents the binary decision variable indicating whether an artwork is nominated at a festival.  
  - *Artwork_ID*: Unique identifier for the artwork.  
  - *Festival_ID*: Unique identifier for the festival.  
  - *decision*: Binary decision (True/False) indicating whether the artwork is nominated.  

### Current Stored Values  
```sql
-- Iteration 3 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on realistic film festival scenarios, ensuring diversity in artwork types and engagement scores that reflect audience preferences. Constraints were respected to ensure the optimization problem remains solvable.

-- Realistic data for engagement_scores
INSERT INTO engagement_scores (Artwork_ID, Festival_ID, score) VALUES (1, 1, 0.85);
INSERT INTO engagement_scores (Artwork_ID, Festival_ID, score) VALUES (2, 1, 0.75);
INSERT INTO engagement_scores (Artwork_ID, Festival_ID, score) VALUES (3, 1, 0.65);

-- Realistic data for festival_nominations
INSERT INTO festival_nominations (Festival_ID, max_nominations) VALUES (1, 5);
INSERT INTO festival_nominations (Festival_ID, max_nominations) VALUES (2, 6);
INSERT INTO festival_nominations (Festival_ID, max_nominations) VALUES (3, 4);

-- Realistic data for artwork_types
INSERT INTO artwork_types (Type, diversity_score) VALUES ('Film', 0.9);
INSERT INTO artwork_types (Type, diversity_score) VALUES ('Short', 0.8);
INSERT INTO artwork_types (Type, diversity_score) VALUES ('Documentary', 0.7);

-- Realistic data for nomination_decisions
INSERT INTO nomination_decisions (Artwork_ID, Festival_ID, decision) VALUES (1, 1, True);
INSERT INTO nomination_decisions (Artwork_ID, Festival_ID, decision) VALUES (2, 1, False);
INSERT INTO nomination_decisions (Artwork_ID, Festival_ID, decision) VALUES (3, 1, True);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- \( x_{i,j} \): Binary decision variable indicating whether artwork \( i \) is nominated for festival \( j \).  
  - \( x_{i,j} \in \{0, 1\} \) for all \( i \in \text{Artwork\_ID}, j \in \text{Festival\_ID} \).

#### Objective Function
Maximize the total audience engagement across all festivals:  
\[
\text{Maximize} \quad \sum_{i} \sum_{j} \text{engagement\_scores.score}_{i,j} \times x_{i,j}
\]

#### Constraints
1. **Maximum Nominations per Festival**:  
   For each festival \( j \), the total number of nominated artworks cannot exceed the maximum allowed nominations:  
   \[
   \sum_{i} x_{i,j} \leq \text{festival\_nominations.max\_nominations}_j \quad \forall j \in \text{Festival\_ID}
   \]

2. **Minimum Diversity Requirement**:  
   For each festival \( j \), the combined diversity score of the nominated artworks must meet or exceed the minimum required diversity score of 0.75:  
   \[
   \sum_{i} \text{artwork\_types.diversity\_score}_i \times x_{i,j} \geq 0.75 \quad \forall j \in \text{Festival\_ID}
   \]

#### Data Source Verification
- **Objective Function Coefficients**:  
  - \( \text{engagement\_scores.score}_{i,j} \): Engagement score for artwork \( i \) at festival \( j \) (from `engagement_scores.score`).

- **Constraint Coefficients**:  
  - \( \text{festival\_nominations.max\_nominations}_j \): Maximum number of nominations allowed for festival \( j \) (from `festival_nominations.max_nominations`).  
  - \( \text{artwork\_types.diversity\_score}_i \): Diversity score for the type of artwork \( i \) (from `artwork_types.diversity_score`).

This formulation provides a complete, immediately solvable linear programming model for the given problem.

## 5. Gurobipy Implementation

```python
# Complete GUROBIPY implementation - Retry Attempt 4

import gurobipy as gp
from gurobipy import GRB

def entertainment_awards_optimization():
    # 1. MODEL & DATA SETUP
    model = gp.Model("entertainment_awards")

    # Sample data based on the provided schema and realistic values
    engagement_scores = {
        (1, 1): 0.85,
        (2, 1): 0.75,
        (3, 1): 0.65
    }
    
    festival_nominations = {
        1: 5,
        2: 6,
        3: 4
    }
    
    artwork_types = {
        1: 0.9,  # Assuming Artwork_ID 1 is 'Film'
        2: 0.8,  # Assuming Artwork_ID 2 is 'Short'
        3: 0.7   # Assuming Artwork_ID 3 is 'Documentary'
    }

    # Extract unique Artwork_ID and Festival_ID
    Artwork_ID = list(set(i for i, j in engagement_scores.keys()))
    Festival_ID = list(set(j for i, j in engagement_scores.keys()))

    # Validate array lengths before loops
    assert len(festival_nominations) == len(Festival_ID), "Festival nominations data mismatch"
    assert all((i, j) in engagement_scores for i in Artwork_ID for j in Festival_ID), "Engagement scores data mismatch"

    # 2. VARIABLES
    x = model.addVars(Artwork_ID, Festival_ID, vtype=GRB.BINARY, name="x")

    # 3. OBJECTIVE FUNCTION
    model.setObjective(
        gp.quicksum(engagement_scores[i, j] * x[i, j] for i in Artwork_ID for j in Festival_ID),
        GRB.MAXIMIZE
    )

    # 4. CONSTRAINTS
    # Maximum Nominations per Festival
    for j in Festival_ID:
        model.addConstr(
            gp.quicksum(x[i, j] for i in Artwork_ID) <= festival_nominations[j],
            name=f"max_nominations_{j}"
        )

    # Minimum Diversity Requirement
    for j in Festival_ID:
        model.addConstr(
            gp.quicksum(artwork_types[i] * x[i, j] for i in Artwork_ID) >= 0.75,
            name=f"min_diversity_{j}"
        )

    # 5. SOLVING & RESULTS
    model.optimize()

    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in Artwork_ID:
            for j in Festival_ID:
                if x[i, j].x > 1e-6:
                    print(f"Artwork {i} nominated for Festival {j}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")

    return model

# Execute the optimization
entertainment_awards_optimization()
```

### Execution Results
**Status**: ERROR
**Error**: Traceback (most recent call last):
  File "/tmp/tmpq05_37ks.py", line 78, in <module>
    entertainment_awards_optimization()
  File "/tmp/tmpq05_37ks.py", line 34, in entertainment_awards_optimization
    assert len(festival_nominations) == len(Festival_ID), "Festival nominations data mismatch"
AssertionError: Festival nominations data mismatch

**Analysis**: Gurobipy failed due to a data mismatch in the festival nominations data. The error indicates that the length of the festival nominations data does not match the length of the Festival_ID set. This suggests an issue with data preparation or input validation.

## 6. DOCplex Implementation

```python
# Complete DOCPLEX implementation - Retry Attempt 4

from docplex.mp.model import Model

def entertainment_awards_optimization():
    # 1. MODEL & DATA SETUP
    mdl = Model(name="entertainment_awards")
    
    # Sample data based on the provided schema and realistic values
    engagement_scores = {
        (1, 1): 0.85,
        (2, 1): 0.75,
        (3, 1): 0.65
    }
    
    festival_nominations = {
        1: 5,
        2: 6,
        3: 4
    }
    
    artwork_types = {
        1: 0.9,  # Assuming Artwork_ID 1 is of type 'Film'
        2: 0.8,  # Assuming Artwork_ID 2 is of type 'Short'
        3: 0.7   # Assuming Artwork_ID 3 is of type 'Documentary'
    }
    
    # Extract unique Artwork_ID and Festival_ID
    Artwork_ID = list(set(artwork_id for (artwork_id, _) in engagement_scores.keys()))
    Festival_ID = list(festival_nominations.keys())
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(festival_nominations) == len(Festival_ID), "Festival nominations data mismatch"
    assert all((i, j) in engagement_scores for i in Artwork_ID for j in Festival_ID), "Engagement scores data mismatch"
    
    # 2. VARIABLES
    x = {(i, j): mdl.binary_var(name=f"x_{i}_{j}") for i in Artwork_ID for j in Festival_ID}
    
    # 3. OBJECTIVE FUNCTION
    objective = mdl.sum(engagement_scores[(i, j)] * x[(i, j)] for i in Artwork_ID for j in Festival_ID)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # Maximum Nominations per Festival
    for j in Festival_ID:
        mdl.add_constraint(mdl.sum(x[(i, j)] for i in Artwork_ID) <= festival_nominations[j], ctname=f"max_nominations_{j}")
    
    # Minimum Diversity Requirement
    for j in Festival_ID:
        mdl.add_constraint(mdl.sum(artwork_types[i] * x[(i, j)] for i in Artwork_ID) >= 0.75, ctname=f"min_diversity_{j}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for (i, j) in x:
            if solution.get_value(x[(i, j)]) > 0.5:
                print(f"Artwork {i} nominated for Festival {j}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Execute the optimization
entertainment_awards_optimization()
```

### Execution Results
**Status**: ERROR
**Error**: Traceback (most recent call last):
  File "/tmp/tmp7hhbd70u.py", line 68, in <module>
    entertainment_awards_optimization()
  File "/tmp/tmp7hhbd70u.py", line 34, in entertainment_awards_optimization
    assert all((i, j) in engagement_scores for i in Artwork_ID for j in Festival_ID), "Engagement scores data mismatch"
AssertionError: Engagement scores data mismatch

**Analysis**: DOCplex encountered an error due to a mismatch in the engagement scores data. The error indicates that not all combinations of Artwork_ID and Festival_ID are present in the engagement_scores data. This points to incomplete or incorrect data preparation.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation - Retry Attempt 4

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

def entertainment_awards_optimization():
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # Engagement scores data
    engagement_scores = {
        (1, 1): 0.85,
        (2, 1): 0.75,
        (3, 1): 0.65
    }
    
    # Festival nominations data
    festival_nominations = {
        1: 5,
        2: 6,
        3: 4
    }
    
    # Artwork types data
    artwork_types = {
        1: 0.9,  # Film
        2: 0.8,  # Short
        3: 0.7   # Documentary
    }
    
    # Validate array lengths before indexing
    Artwork_ID = [1, 2, 3]
    Festival_ID = [1, 2, 3]
    
    assert len(festival_nominations) == len(Festival_ID), "Festival nominations data mismatch"
    assert all((i, j) in engagement_scores for i in Artwork_ID for j in Festival_ID), "Engagement scores data mismatch"
    assert all(i in artwork_types for i in Artwork_ID), "Artwork types data mismatch"
    
    # 3. SETS
    model.Artwork_ID = pyo.Set(initialize=Artwork_ID)
    model.Festival_ID = pyo.Set(initialize=Festival_ID)
    
    # 4. PARAMETERS
    model.engagement_scores = pyo.Param(model.Artwork_ID, model.Festival_ID, initialize=engagement_scores)
    model.max_nominations = pyo.Param(model.Festival_ID, initialize=festival_nominations)
    model.diversity_scores = pyo.Param(model.Artwork_ID, initialize=artwork_types)
    
    # 5. VARIABLES
    model.x = pyo.Var(model.Artwork_ID, model.Festival_ID, within=pyo.Binary)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.engagement_scores[i, j] * model.x[i, j] for i in model.Artwork_ID for j in model.Festival_ID)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    # Maximum Nominations per Festival
    def max_nominations_rule(model, j):
        return sum(model.x[i, j] for i in model.Artwork_ID) <= model.max_nominations[j]
    model.max_nominations_constraint = pyo.Constraint(model.Festival_ID, rule=max_nominations_rule)
    
    # Minimum Diversity Requirement
    def min_diversity_rule(model, j):
        return sum(model.diversity_scores[i] * model.x[i, j] for i in model.Artwork_ID) >= 0.75
    model.min_diversity_constraint = pyo.Constraint(model.Festival_ID, rule=min_diversity_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.Artwork_ID:
            for j in model.Festival_ID:
                x_val = pyo.value(model.x[i, j])
                if x_val > 1e-6:  # Only print non-zero values
                    print(f"x[{i}, {j}] = {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

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

### Execution Results
**Status**: ERROR
**Error**: Traceback (most recent call last):
  File "/tmp/tmp5vncwgt1.py", line 103, in <module>
    entertainment_awards_optimization()
  File "/tmp/tmp5vncwgt1.py", line 37, in entertainment_awards_optimization
    assert all((i, j) in engagement_scores for i in Artwork_ID for j in Festival_ID), "Engagement scores data mismatch"
AssertionError: Engagement scores data mismatch

**Analysis**: Pyomo also failed due to the same engagement scores data mismatch issue as DOCplex. This further confirms that the data preparation process is flawed, leading to missing or inconsistent data.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | ERROR | N/A | 0.17s | N/A | 4 |
| Docplex | ERROR | N/A | 1.28s | N/A | 4 |
| Pyomo | ERROR | N/A | 0.91s | N/A | 4 |

### Solver Consistency Analysis
**Result**: Solvers produced inconsistent results
**Inconsistent Solvers**: gurobipy, docplex, pyomo
**Potential Issues**:
- Data mismatch in festival nominations data
- Incomplete or missing engagement scores data
- Incorrect data preparation or input validation
**Solver Retry Summary**: gurobipy: 4 attempts, docplex: 4 attempts, pyomo: 4 attempts

### Final Recommendation
**Confidence Level**: LOW
**Preferred Solver(s)**: none
**Reasoning**: All solvers encountered errors due to data mismatches, indicating that the issue lies in the data preparation rather than the solvers themselves. No solver can be recommended until the data issues are resolved.

### Business Interpretation
**Overall Strategy**: The optimization process failed due to data inconsistencies, making it impossible to determine the optimal nominations for maximizing audience engagement.
**Objective Value Meaning**: The optimal objective value represents the maximum total audience engagement achievable across all festivals. However, this value could not be determined due to data errors.
**Resource Allocation Summary**: Resource allocation recommendations cannot be provided until the data issues are resolved and the optimization process is successfully completed.
**Implementation Recommendations**: 1. Verify and correct the festival nominations data to ensure it matches the Festival_ID set. 2. Ensure that engagement scores are available for all combinations of Artwork_ID and Festival_ID. 3. Re-run the optimization process with corrected data to obtain reliable results.