## 4. Mathematical Optimization Formulation

#### Decision Variables
- Let \( x_1 \) be the budget allocated to movie 1.
- Let \( x_2 \) be the budget allocated to movie 2.
- Let \( x_3 \) be the budget allocated to movie 3.

#### Objective Function
Maximize the expected increase in movie ratings:
\[ \text{Maximize } 0.15x_1 + 0.25x_2 + 0.1x_3 \]

Data Source Verification:
- Coefficients 0.15, 0.25, and 0.1 are from `promotion_sensitivity.sensitivity_value` for movies 1, 2, and 3, respectively.

#### Constraints
1. Total budget constraint:
   \[ x_1 + x_2 + x_3 \leq 65000 \]
   - Data Source Verification: The total promotional budget is the sum of current allocations from `budget_allocation.amount`.

2. Minimum budget constraints for each movie:
   \[ x_1 \geq 20000 \]
   \[ x_2 \geq 30000 \]
   \[ x_3 \geq 15000 \]
   - Data Source Verification: Minimum budget allocations are from `budget_allocation.amount` for movies 1, 2, and 3, respectively.

3. Non-negativity constraints:
   \[ x_1 \geq 0, \quad x_2 \geq 0, \quad x_3 \geq 0 \]

This linear programming model is ready to be solved using appropriate optimization software, ensuring that the promotional budget is allocated to maximize the expected increase in movie ratings while adhering to the specified constraints.

## 5. Gurobipy Implementation

```python
# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_movie_promotion():
    """Optimize promotional budget allocation for movies to maximize ratings increase."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("movie_promotion")
    
    # Data: Sensitivity values and minimum budget requirements
    sensitivity = [0.15, 0.25, 0.1]
    min_budget = [20000, 30000, 15000]
    total_budget = 65000
    n_movies = len(sensitivity)
    
    # CRITICAL: Validate array lengths before loops
    assert len(sensitivity) == len(min_budget) == n_movies, "Array length mismatch"
    
    # 2. VARIABLES
    # Variable dictionaries for budget allocation
    budget_allocation = {i: model.addVar(vtype=GRB.CONTINUOUS, name=f"x_{i+1}", lb=0) 
                         for i in range(n_movies)}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the expected increase in movie ratings
    model.setObjective(gp.quicksum(sensitivity[i] * budget_allocation[i] for i in range(n_movies)), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Total budget constraint
    model.addConstr(gp.quicksum(budget_allocation[i] for i in range(n_movies)) <= total_budget, name="total_budget")
    
    # Minimum budget constraints for each movie
    for i in range(n_movies):
        model.addConstr(budget_allocation[i] >= min_budget[i], name=f"min_budget_{i+1}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in range(n_movies):
            if budget_allocation[i].x > 1e-6:
                print(f"Budget allocation for movie {i+1}: {budget_allocation[i].x:.2f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
optimize_movie_promotion()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 12000.0
**Execution Time**: 0.28 seconds
**Reliability**: high
**Analysis**: Gurobipy found an optimal solution quickly with the expected optimal value. The decision variables meet all constraints.

## 6. DOCplex Implementation

```python
# Complete DOCPLEX implementation

from docplex.mp.model import Model

def optimize_movie_promotion():
    """Optimize promotional budget allocation for movies to maximize rating improvement."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="movie_promotion")
    
    # Data: Sensitivity values and minimum budget requirements
    sensitivity_values = [0.15, 0.25, 0.1]
    min_budgets = [20000, 30000, 15000]
    total_budget = 65000
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(sensitivity_values) == len(min_budgets), "Array length mismatch"
    safe_range = range(len(sensitivity_values))  # Safe indexing
    
    # 2. VARIABLES
    # Variable dictionaries for budget allocation
    budget_allocation = {i: mdl.continuous_var(name=f"budget_{i}", lb=0) for i in safe_range}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the expected increase in movie ratings
    objective = mdl.sum(sensitivity_values[i] * budget_allocation[i] for i in safe_range)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Total budget constraint
    total_allocation = mdl.sum(budget_allocation[i] for i in safe_range)
    mdl.add_constraint(total_allocation <= total_budget, ctname="total_budget_limit")
    
    # Minimum budget constraints for each movie
    for i in safe_range:
        mdl.add_constraint(budget_allocation[i] >= min_budgets[i], ctname=f"min_budget_{i}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in safe_range:
            value = solution.get_value(budget_allocation[i])
            print(f"Budget allocation for movie {i+1}: {value:.2f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Run the optimization
optimize_movie_promotion()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 12000.0
**Execution Time**: 2.52 seconds
**Reliability**: high
**Analysis**: DOCplex also found an optimal solution with the same optimal value and decision variables as Gurobipy, albeit with a longer execution time.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

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

def movie_promotion_optimization():
    """Optimize promotional budget allocation for movies"""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    n_movies = 3
    sensitivity_values = [0.15, 0.25, 0.1]
    min_budgets = [20000, 30000, 15000]
    total_budget = 65000
    
    # CRITICAL: Validate array lengths before indexing
    assert len(sensitivity_values) == len(min_budgets) == n_movies, "Array length mismatch"
    
    # 3. SETS
    model.M = pyo.RangeSet(1, n_movies)  # 1-based indexing for movies
    
    # 4. PARAMETERS
    model.sensitivity = pyo.Param(model.M, initialize={i+1: sensitivity_values[i] for i in range(n_movies)})
    model.min_budget = pyo.Param(model.M, initialize={i+1: min_budgets[i] for i in range(n_movies)})
    
    # 5. VARIABLES
    model.budget_allocation = pyo.Var(model.M, within=pyo.NonNegativeReals)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.sensitivity[m] * model.budget_allocation[m] for m in model.M)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    
    # Total budget constraint
    def total_budget_rule(model):
        return sum(model.budget_allocation[m] for m in model.M) <= total_budget
    model.total_budget_constraint = pyo.Constraint(rule=total_budget_rule)
    
    # Minimum budget constraints for each movie
    def min_budget_rule(model, m):
        return model.budget_allocation[m] >= model.min_budget[m]
    model.min_budget_constraints = pyo.Constraint(model.M, rule=min_budget_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):.3f}")
        
        # Extract variable values
        print("\nBudget Allocation:")
        for m in model.M:
            allocation_val = pyo.value(model.budget_allocation[m])
            print(f"Movie {m}: {allocation_val:.2f}")
        
    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
movie_promotion_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 12000.0
**Execution Time**: 1.64 seconds
**Reliability**: high
**Analysis**: Pyomo achieved the same optimal solution and decision variables, confirming the consistency across solvers.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 12000.00 | 0.28s | N/A | N/A |
| Docplex | OPTIMAL | 12000.00 | 2.52s | N/A | N/A |
| Pyomo | OPTIMAL | 12000.00 | 1.64s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 12000.0
**Confidence Level**: HIGH
**Preferred Solver(s)**: multiple
**Reasoning**: All solvers provided consistent results, confirming the reliability of the solution. Gurobipy is preferred for its speed, but any solver can be used.

### Optimal Decision Variables
- **x_1** = 20000.000
  - *Business Meaning*: Budget allocated to movie 2, which is set at the minimum required level of $30,000.
- **x_2** = 30000.000
  - *Business Meaning*: Budget allocated to movie 3, which is set at the minimum required level of $15,000.
- **x_3** = 15000.000
  - *Business Meaning*: Resource allocation for x_3

### Business Interpretation
**Overall Strategy**: The optimal allocation of the promotional budget maximizes the expected increase in movie ratings while adhering to budget constraints.
**Objective Value Meaning**: The optimal objective value of 12000 represents the maximum expected increase in movie ratings achievable with the given budget.
**Resource Allocation Summary**: Allocate $20,000 to movie 1, $30,000 to movie 2, and $15,000 to movie 3 to achieve the optimal increase in ratings.
**Implementation Recommendations**: Ensure the budget allocations are strictly adhered to as per the solution. Monitor the impact on movie ratings to validate the model's effectiveness.