# Complete Optimization Problem and Solution: program_share

## 1. Problem Context and Goals

### Context  
A media company is focused on optimizing the allocation of its programs across various broadcasting channels. The primary decision involves determining which programs should be broadcasted on which channels. This decision is represented by a binary variable, where each combination of channel and program is either selected for broadcasting or not. The objective is to maximize the total share percentage of all broadcasts, which is calculated by summing the share percentages of the selected programs on their respective channels.

The operational parameters include constraints on the maximum share capacity that each channel can handle, ensuring that no channel exceeds its operational limits. Additionally, the availability of programs for broadcasting is considered, with all programs assumed to be available for scheduling. These constraints are crucial for maintaining a linear optimization model, where each decision variable directly influences the objective without involving nonlinear relationships.

The business configuration includes:
- A maximum share capacity for each channel, which serves as a constraint to ensure channels do not exceed their operational limits.
- An indicator of program availability, ensuring that only available programs are considered for broadcasting.

### Goals  
The primary goal of this optimization problem is to maximize the total share percentage of programs broadcasted across all channels. This involves selecting the optimal combination of programs and channels to achieve the highest possible total share. The success of this optimization is measured by the total share percentage, which is calculated by summing the share percentages of the selected programs on their respective channels. The objective is clearly defined in linear terms, focusing on maximizing the sum of the share percentages associated with the decision variables.

## 2. Constraints    

The optimization problem is subject to the following constraints:

- Each channel has a maximum share capacity that it can handle. The total share percentage of programs broadcasted on a channel must not exceed this capacity. This ensures that the channel operates within its limits and maintains quality broadcasting standards.

- Only available programs can be considered for broadcasting. This constraint ensures that the decision variables only include programs that are ready and available for scheduling, aligning with the operational realities of the media company.

These constraints are expressed in linear terms, ensuring that the optimization problem remains within the realm of linear programming.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 1 Database Schema
-- Objective: Schema changes include creating a new table for decision variables, modifying existing tables to ensure all necessary data for constraints are available, and updating configuration logic for scalar parameters and formulas.

CREATE TABLE broadcast_share (
  channel_id INTEGER,
  program_id INTEGER,
  Share_in_percent FLOAT
);

CREATE TABLE decision_variables (
  channel_id INTEGER,
  program_id INTEGER,
  x BOOLEAN
);
```

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

- **broadcast_share**: This table stores the share percentages of programs on various channels. It plays a critical role in the optimization model by providing the coefficients for the objective function. Each entry links a channel and a program with a specific share percentage, which is used to calculate the total share.

  - **channel_id**: Represents the unique identifier for each channel. It is used to link with decision variables and ensure that the share percentages are correctly attributed to the respective channels.
  
  - **program_id**: Represents the unique identifier for each program. It is used to link with decision variables, ensuring that the share percentages are correctly attributed to the respective programs.
  
  - **Share_in_percent**: Indicates the share percentage of a program on a channel. This value serves as a coefficient in the objective function, directly influencing the total share calculation.

- **decision_variables**: This table indicates whether a program is broadcasted on a channel. It contains the binary decision variables that form the core of the optimization model.

  - **channel_id**: Represents the unique identifier for each channel, forming part of the decision variable index.
  
  - **program_id**: Represents the unique identifier for each program, forming part of the decision variable index.
  
  - **x**: A binary variable indicating whether a program is broadcasted on a channel. It is the primary decision variable in the optimization model, determining the selection of programs for broadcasting.

### Current Stored Values  
```sql
-- Iteration 1 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on typical media company operations, ensuring that channel capacities and program availabilities align with realistic broadcasting scenarios.

-- Realistic data for broadcast_share
INSERT INTO broadcast_share (channel_id, program_id, Share_in_percent) VALUES (1, 101, 12.5);
INSERT INTO broadcast_share (channel_id, program_id, Share_in_percent) VALUES (1, 102, 18.0);
INSERT INTO broadcast_share (channel_id, program_id, Share_in_percent) VALUES (2, 103, 15.0);
INSERT INTO broadcast_share (channel_id, program_id, Share_in_percent) VALUES (2, 101, 10.0);
INSERT INTO broadcast_share (channel_id, program_id, Share_in_percent) VALUES (3, 102, 20.0);

-- Realistic data for decision_variables
INSERT INTO decision_variables (channel_id, program_id, x) VALUES (1, 101, 1);
INSERT INTO decision_variables (channel_id, program_id, x) VALUES (1, 102, 1);
INSERT INTO decision_variables (channel_id, program_id, x) VALUES (2, 103, 1);
INSERT INTO decision_variables (channel_id, program_id, x) VALUES (2, 101, 0);
INSERT INTO decision_variables (channel_id, program_id, x) VALUES (3, 102, 1);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- Let \( x_{c,p} \) be a binary decision variable for each channel \( c \) and program \( p \), where:
  - \( x_{c,p} = 1 \) if program \( p \) is broadcasted on channel \( c \)
  - \( x_{c,p} = 0 \) otherwise

#### Objective Function
Maximize the total share percentage of programs broadcasted across all channels:

\[
\text{Maximize } \sum_{(c,p) \in \text{broadcast\_share}} \text{Share\_in\_percent}_{c,p} \times x_{c,p}
\]

Data Source Verification:
- Coefficients \(\text{Share\_in\_percent}_{c,p}\) are sourced from `broadcast_share.Share_in_percent`.

#### Constraints

1. **Channel Share Capacity Constraints:**
   - Each channel \( c \) has a maximum share capacity \( \text{Max\_Share\_Capacity}_c \). The total share percentage of programs broadcasted on a channel must not exceed this capacity.
   
   \[
   \sum_{p} \text{Share\_in\_percent}_{c,p} \times x_{c,p} \leq \text{Max\_Share\_Capacity}_c \quad \forall c
   \]

   Data Source Verification:
   - Coefficients \(\text{Share\_in\_percent}_{c,p}\) are sourced from `broadcast_share.Share_in_percent`.
   - \(\text{Max\_Share\_Capacity}_c\) is a business configuration parameter (not explicitly provided in the data).

2. **Program Availability Constraints:**
   - Only available programs can be considered for broadcasting. This is inherently ensured by the decision variable setup, assuming all programs listed in `broadcast_share` are available.

3. **Binary Decision Variables:**
   - \( x_{c,p} \in \{0, 1\} \quad \forall (c,p) \)

This formulation provides a complete and immediately solvable linear mathematical model, ensuring that the optimization problem adheres to the constraints and objectives outlined in the business problem description.

## 5. Gurobipy Implementation

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

import gurobipy as gp
from gurobipy import GRB

def optimize_program_share():
    """Optimize program share allocation across channels using Gurobi."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("program_share")
    
    # Data: Share percentages and maximum share capacities
    share_data = {
        (1, 101): 12.5,
        (1, 102): 18.0,
        (2, 103): 15.0,
        (2, 101): 10.0,
        (3, 102): 20.0
    }
    
    max_share_capacity = {
        1: 30.0,
        2: 25.0,
        3: 20.0
    }
    
    # Extract unique channels and programs
    channels = set(c for c, p in share_data.keys())
    programs = set(p for c, p in share_data.keys())
    
    # CRITICAL: Validate data consistency
    assert all((c, p) in share_data for c in channels for p in programs if (c, p) in share_data), "Data inconsistency detected"
    
    # 2. VARIABLES
    x = model.addVars(share_data.keys(), vtype=GRB.BINARY, name="x")
    
    # 3. OBJECTIVE FUNCTION
    model.setObjective(gp.quicksum(share_data[c, p] * x[c, p] for c, p in share_data.keys()), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS
    # Channel Share Capacity Constraints
    for c in channels:
        model.addConstr(
            gp.quicksum(share_data.get((c, p), 0) * x[c, p] for p in programs if (c, p) in share_data) <= max_share_capacity[c],
            name=f"capacity_{c}"
        )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for c, p in share_data.keys():
            if x[c, p].x > 1e-6:
                print(f"x[{c},{p}] = {x[c, p].x:.3f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")

# Run the optimization
optimize_program_share()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 63.0
**Execution Time**: 0.15 seconds
**Reliability**: high
**Retry Attempt**: 1
**Analysis**: Gurobipy found an optimal solution quickly with the shortest execution time, indicating efficient performance and reliability.

## 6. DOCplex Implementation

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

from docplex.mp.model import Model

def optimize_program_share():
    # 1. MODEL & DATA SETUP
    mdl = Model(name="program_share")
    
    # Data from the problem context
    channels = [1, 2, 3]
    programs = [101, 102, 103]
    share_in_percent = {
        (1, 101): 12.5,
        (1, 102): 18.0,
        (2, 103): 15.0,
        (2, 101): 10.0,
        (3, 102): 20.0
    }
    max_share_capacity = {
        1: 30.0,
        2: 25.0,
        3: 20.0
    }
    
    # Validate data consistency
    assert all((c, p) in share_in_percent for c in channels for p in programs if (c, p) in share_in_percent), "Data inconsistency detected"
    
    # Safe range for indexing
    safe_range = [(c, p) for c in channels for p in programs if (c, p) in share_in_percent]
    
    # 2. VARIABLES
    x = {(c, p): mdl.binary_var(name=f"x_{c}_{p}") for c, p in safe_range}
    
    # 3. OBJECTIVE FUNCTION
    objective = mdl.sum(share_in_percent[c, p] * x[c, p] for c, p in safe_range)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    # Channel Share Capacity Constraints
    for c in channels:
        mdl.add_constraint(
            mdl.sum(share_in_percent[c, p] * x[c, p] for p in programs if (c, p) in share_in_percent) <= max_share_capacity[c],
            ctname=f"capacity_{c}"
        )
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for c, p in safe_range:
            if solution.get_value(x[c, p]) > 0.5:  # Binary variable threshold
                print(f"Program {p} is broadcasted on Channel {c}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")

# Execute the optimization
optimize_program_share()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 63.0
**Execution Time**: 1.30 seconds
**Reliability**: high
**Retry Attempt**: 1
**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 - Retry Attempt 1

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

def program_share_optimization():
    """Optimize program allocation across channels to maximize share percentage."""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # Example data based on the problem description
    channels = [1, 2, 3]
    programs = [101, 102, 103]
    share_data = {
        (1, 101): 12.5,
        (1, 102): 18.0,
        (2, 103): 15.0,
        (2, 101): 10.0,
        (3, 102): 20.0
    }
    max_share_capacity = {
        1: 30.0,
        2: 25.0,
        3: 20.0
    }
    
    # 3. SETS
    model.C = pyo.Set(initialize=channels)
    model.P = pyo.Set(initialize=programs)
    model.CP = pyo.Set(initialize=share_data.keys(), dimen=2)
    
    # 4. PARAMETERS
    model.Share_in_percent = pyo.Param(model.CP, initialize=share_data)
    model.Max_Share_Capacity = pyo.Param(model.C, initialize=max_share_capacity)
    
    # 5. VARIABLES
    model.x = pyo.Var(model.CP, within=pyo.Binary)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.Share_in_percent[c, p] * model.x[c, p] for c, p in model.CP)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    def channel_capacity_rule(model, c):
        return sum(model.Share_in_percent[c, p] * model.x[c, p] for p in model.P if (c, p) in model.CP) <= model.Max_Share_Capacity[c]
    model.channel_capacity_constraint = pyo.Constraint(model.C, rule=channel_capacity_rule)
    
    # 8. SOLVING WITH GUROBI
    solver = SolverFactory('gurobi')
    
    # Solve the model
    results = solver.solve(model, tee=True)
    
    # 9. RESULT PROCESSING
    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 c, p in model.CP:
            x_val = pyo.value(model.x[c, p])
            if x_val > 1e-6:  # Only print non-zero values
                print(f"x[{c}, {p}] = {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
program_share_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 63.0
**Execution Time**: 1.37 seconds
**Reliability**: high
**Retry Attempt**: 1
**Analysis**: Pyomo achieved the optimal solution with a slightly longer execution time than DOCplex, but the results are consistent and reliable.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 63.00 | 0.15s | N/A | 1 |
| Docplex | OPTIMAL | 63.00 | 1.30s | N/A | 1 |
| Pyomo | OPTIMAL | 63.00 | 1.37s | N/A | 1 |

### Solver Consistency Analysis
**Result**: All solvers produced consistent results ✓
**Consistent Solvers**: gurobipy, docplex, pyomo
**Majority Vote Optimal Value**: 63.0
**Solver Retry Summary**: gurobipy: 1 attempts, docplex: 1 attempts, pyomo: 1 attempts

### Final Recommendation
**Recommended Optimal Value**: 63.0
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy is preferred due to its faster execution time while maintaining the same optimal value as the other solvers, indicating efficiency and reliability.

### Business Interpretation
**Overall Strategy**: The optimal broadcasting strategy maximizes the share percentage across all channels, ensuring the best possible audience reach.
**Objective Value Meaning**: The optimal objective value of 63.0 indicates the maximum achievable share percentage across all channels, reflecting the effectiveness of the broadcasting strategy.
**Resource Allocation Summary**: Resources should be allocated to programs that maximize share percentages while adhering to channel capacity constraints.
**Implementation Recommendations**: Implement the broadcasting strategy by selecting programs that align with the decision variables, ensuring compliance with channel capacity constraints and maximizing audience reach.