# Complete Optimization Problem and Solution: music_1

## 1. Problem Context and Goals

### Context  
The music streaming service is focused on optimizing its storage allocation to enhance the overall user experience by maximizing the total rating of songs available on the platform. Each song is evaluated based on its rating, file size, and resolution quality. The decision to store a song is represented by a binary choice, where storing a song is indicated by a value of 1, and not storing it is indicated by a value of 0. The platform operates under specific constraints: a limited total storage capacity and a requirement for a minimum total resolution quality across all stored songs. These constraints ensure that the platform maintains a balance between quantity and quality, adhering to industry standards and operational limitations. The business configuration includes parameters such as the total storage capacity available for storing songs and the minimum total resolution quality required for stored songs, which are critical in guiding the optimization process.

### Goals  
The primary goal of the optimization is to maximize the total rating of the songs stored on the platform. This involves selecting a combination of songs that collectively offer the highest possible rating while respecting the constraints of storage capacity and resolution quality. The success of this optimization is measured by the total rating achieved, which is directly influenced by the ratings of the individual songs selected for storage. The objective is to ensure that the platform offers a high-quality music catalog that meets user expectations and operational requirements.

## 2. Constraints    

The optimization process is governed by two main constraints. First, the total file size of the stored songs must not exceed the available storage capacity. This ensures that the platform operates within its physical storage limits. Second, the combined resolution quality of the stored songs must meet or exceed a specified minimum threshold. This constraint guarantees that the platform maintains a certain level of audio quality, which is essential for user satisfaction. These constraints are linear in nature, focusing on the sum of individual song attributes to guide the decision-making process.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 1 Database Schema
-- Objective: Schema changes include adding tables for missing data requirements and updating configuration logic for scalar parameters and formulas.

CREATE TABLE song (
  f_id INTEGER,
  rating FLOAT,
  file_size INTEGER
);

CREATE TABLE song_file_data (
  song_id INTEGER,
  resolution INTEGER
);
```

### Data Dictionary  
The data structure is designed to support the optimization process by providing essential information about each song. The "song" table contains metadata for each song, including a unique identifier, the song's rating, and its file size. This information is crucial for determining the song's contribution to the overall rating and its impact on storage capacity. The "song_file_data" table links each song to its resolution quality, which is used to ensure that the platform meets the minimum resolution quality constraint. Each table and column is aligned with the optimization roles, ensuring that the data supports the linear formulation of the problem.

### Current Stored Values  
```sql
-- Iteration 1 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on typical file sizes and resolutions for music streaming services, ensuring a mix of high and low ratings to reflect a realistic catalog.

-- Realistic data for song
INSERT INTO song (f_id, rating, file_size) VALUES (1, 4.5, 5000);
INSERT INTO song (f_id, rating, file_size) VALUES (2, 3.8, 3000);
INSERT INTO song (f_id, rating, file_size) VALUES (3, 5.0, 7000);

-- Realistic data for song_file_data
INSERT INTO song_file_data (song_id, resolution) VALUES (1, 1080);
INSERT INTO song_file_data (song_id, resolution) VALUES (2, 720);
INSERT INTO song_file_data (song_id, resolution) VALUES (3, 480);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- Let \( x_i \) be a binary decision variable for each song \( i \), where \( x_i = 1 \) if song \( i \) is stored, and \( x_i = 0 \) otherwise.

#### Objective Function
Maximize the total rating of the stored songs:
\[ \text{Maximize } \sum_{i} \text{rating}_i \times x_i \]

Using the provided data:
\[ \text{Maximize } 4.5x_1 + 3.8x_2 + 5.0x_3 \]

#### Constraints
1. **Storage Capacity Constraint**: The total file size of the stored songs must not exceed the available storage capacity.
   \[ \sum_{i} \text{file\_size}_i \times x_i \leq \text{Total Storage Capacity} \]

   Using the provided data and assuming a total storage capacity of 10,000 (as an example):
   \[ 5000x_1 + 3000x_2 + 7000x_3 \leq 10000 \]

2. **Resolution Quality Constraint**: The combined resolution quality of the stored songs must meet or exceed a specified minimum threshold.
   \[ \sum_{i} \text{resolution}_i \times x_i \geq \text{Minimum Resolution Quality} \]

   Using the provided data and assuming a minimum resolution quality of 1500 (as an example):
   \[ 1080x_1 + 720x_2 + 480x_3 \geq 1500 \]

Data Source Verification:
- Coefficients for the objective function (ratings) are sourced from `song.rating`.
- Coefficients for the storage capacity constraint (file sizes) are sourced from `song.file_size`.
- Coefficients for the resolution quality constraint (resolutions) are sourced from `song_file_data.resolution`.
- The constants for the constraints (total storage capacity and minimum resolution quality) are assumed for this formulation and should be replaced with actual values from business configuration parameters.

## 5. Gurobipy Implementation

```python
# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_music_storage():
    """Optimize music storage to maximize total rating under constraints."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("music_1")
    
    # Data from the problem statement
    ratings = [4.5, 3.8, 5.0]
    file_sizes = [5000, 3000, 7000]
    resolutions = [1080, 720, 480]
    
    # Assumed parameters
    total_storage_capacity = 10000
    minimum_resolution_quality = 1500
    
    n_songs = len(ratings)
    
    # CRITICAL: Validate array lengths before loops
    assert len(file_sizes) == len(resolutions) == n_songs, "Array length mismatch"
    
    # 2. VARIABLES
    # Binary decision variables for each song
    x = {i: model.addVar(vtype=GRB.BINARY, name=f"x_{i}") for i in range(n_songs)}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total rating of the stored songs
    model.setObjective(gp.quicksum(ratings[i] * x[i] for i in range(n_songs)), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Storage Capacity Constraint
    model.addConstr(gp.quicksum(file_sizes[i] * x[i] for i in range(n_songs)) <= total_storage_capacity, name="storage_capacity")
    
    # Resolution Quality Constraint
    model.addConstr(gp.quicksum(resolutions[i] * x[i] for i in range(n_songs)) >= minimum_resolution_quality, name="resolution_quality")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in range(n_songs):
            if x[i].x > 1e-6:
                print(f"x[{i}] = {x[i].x:.3f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
optimize_music_storage()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 8.3
**Execution Time**: 0.17 seconds
**Reliability**: high
**Analysis**: Gurobipy found an optimal solution quickly with the highest reliability due to its robust performance and industry reputation.

## 6. DOCplex Implementation

```python
# Complete DOCPLEX implementation

from docplex.mp.model import Model

def optimize_music_storage():
    """Optimize music storage to maximize total rating under constraints."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="music_optimization")
    
    # Data from the problem context
    ratings = [4.5, 3.8, 5.0]
    file_sizes = [5000, 3000, 7000]
    resolutions = [1080, 720, 480]
    
    # Assumed constraints
    total_storage_capacity = 10000
    minimum_resolution_quality = 1500
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(ratings) == len(file_sizes) == len(resolutions), "Array length mismatch"
    safe_range = range(min(len(ratings), len(file_sizes), len(resolutions)))  # Safe indexing
    
    # 2. VARIABLES
    x = {i: mdl.binary_var(name=f"x_{i}") for i in safe_range}
    
    # 3. OBJECTIVE FUNCTION
    objective = mdl.sum(ratings[i] * x[i] for i in safe_range)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Storage Capacity Constraint
    storage_constraint = mdl.sum(file_sizes[i] * x[i] for i in safe_range)
    mdl.add_constraint(storage_constraint <= total_storage_capacity, ctname="storage_capacity")
    
    # Resolution Quality Constraint
    resolution_constraint = mdl.sum(resolutions[i] * x[i] for i in safe_range)
    mdl.add_constraint(resolution_constraint >= minimum_resolution_quality, ctname="resolution_quality")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in safe_range:
            value = solution.get_value(x[i])
            if value > 1e-6:
                print(f"x[{i}] = {value:.3f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Run the optimization
optimize_music_storage()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 8.3
**Execution Time**: 1.45 seconds
**Reliability**: high
**Analysis**: DOCplex also found an optimal solution, albeit with a longer execution time compared to Gurobipy, but still reliable.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

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

def music_optimization():
    """Optimize song storage for a music streaming service"""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # Song data
    ratings = [4.5, 3.8, 5.0]
    file_sizes = [5000, 3000, 7000]
    resolutions = [1080, 720, 480]
    
    # Business constraints
    total_storage_capacity = 10000
    minimum_resolution_quality = 1500
    
    # Validate array lengths
    n_songs = len(ratings)
    assert len(file_sizes) == len(resolutions) == n_songs, "Array length mismatch"
    
    # 3. SETS
    model.I = pyo.RangeSet(1, n_songs)  # 1-based indexing
    
    # 4. PARAMETERS
    model.rating = pyo.Param(model.I, initialize={i+1: ratings[i] for i in range(n_songs)})
    model.file_size = pyo.Param(model.I, initialize={i+1: file_sizes[i] for i in range(n_songs)})
    model.resolution = pyo.Param(model.I, initialize={i+1: resolutions[i] for i in range(n_songs)})
    
    # 5. VARIABLES
    model.x = pyo.Var(model.I, within=pyo.Binary)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.rating[i] * model.x[i] for i in model.I)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    def storage_capacity_rule(model):
        return sum(model.file_size[i] * model.x[i] for i in model.I) <= total_storage_capacity
    model.storage_capacity_constraint = pyo.Constraint(rule=storage_capacity_rule)
    
    def resolution_quality_rule(model):
        return sum(model.resolution[i] * model.x[i] for i in model.I) >= minimum_resolution_quality
    model.resolution_quality_constraint = pyo.Constraint(rule=resolution_quality_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.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
music_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 8.3
**Execution Time**: 1.32 seconds
**Reliability**: high
**Analysis**: Pyomo achieved the optimal solution with a slightly faster execution time than DOCplex, indicating good reliability.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 8.30 | 0.17s | N/A | N/A |
| Docplex | OPTIMAL | 8.30 | 1.45s | N/A | N/A |
| Pyomo | OPTIMAL | 8.30 | 1.32s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 8.3
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy is preferred due to its fastest execution time and high reliability, making it suitable for real-time applications.

### Optimal Decision Variables
- **x_1** = 1.000
  - *Business Meaning*: Store song 2, which complements song 1 to maximize the rating.
- **x_2** = 1.000
  - *Business Meaning*: Do not store song 3, as it exceeds storage capacity when combined with others.
- **x_3** = 0.000
  - *Business Meaning*: Resource allocation for x_3

### Business Interpretation
**Overall Strategy**: The optimal solution suggests storing songs 1 and 2 to maximize the total rating while adhering to constraints.
**Objective Value Meaning**: An optimal objective value of 8.3 indicates the maximum achievable rating given the constraints.
**Resource Allocation Summary**: Allocate storage to songs 1 and 2, ensuring the best use of available resources.
**Implementation Recommendations**: Implement a system to dynamically select songs based on ratings and constraints, using Gurobipy for optimization.