# Complete Optimization Problem and Solution: concert_singer

## 1. Problem Context and Goals

### Context  
The business problem revolves around optimizing the allocation of singers to concerts held in various stadiums. The primary objective is to maximize the total audience capacity across all concerts while ensuring that singers are not overbooked and that each concert does not exceed the stadium's capacity. 

Key operational decisions involve determining whether a specific singer is assigned to a concert in a particular stadium. This decision is represented by a binary variable, where a value of 1 indicates an assignment and 0 indicates no assignment. The total audience capacity is calculated by summing the capacities of all stadiums where concerts are assigned to singers.

Business configuration includes a scalar parameter that limits the maximum number of concerts a singer can participate in, ensuring that singers are not overbooked. Additionally, the business logic formula for calculating the total audience capacity is based on the sum of the capacities of all stadiums where concerts are assigned to singers.

### Goals  
The optimization goal is to maximize the total audience capacity across all concerts. This is achieved by strategically assigning singers to concerts in stadiums, ensuring that the sum of the capacities of all assigned concerts is as large as possible. Success is measured by the total audience capacity achieved, which directly depends on the capacities of the stadiums and the assignments of singers to concerts.

## 2. Constraints    

The optimization problem is subject to the following constraints:

1. **Singer Assignment Limit**: Each singer can participate in a maximum number of concerts, as defined by the business configuration parameter. This ensures that singers are not overbooked and can manage their schedules effectively.

2. **Stadium Capacity Limit**: Each concert in a stadium cannot exceed the stadium's capacity. This ensures that the number of singers assigned to a concert does not surpass the stadium's ability to accommodate them.

These constraints are designed to ensure that the assignments are feasible and respect both singer availability and stadium limitations.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 1 Database Schema
-- Objective: Schema changes include creating a singer_concert_assignment table for decision variables, adding singer_limit to business_configuration_logic, and ensuring stadium capacity is correctly mapped. Configuration logic updates include scalar parameters for singer limits and formulas for audience capacity calculations.

CREATE TABLE stadium (
  Capacity INTEGER
);

CREATE TABLE singer_concert_assignment (
  assignment BOOLEAN
);
```

### Data Dictionary  
- **stadium**: This table contains details about the stadiums, including their capacities.
  - **Capacity**: The maximum number of singers a stadium can accommodate. This value is used as a coefficient in the objective function to calculate the total audience capacity.

- **singer_concert_assignment**: This table records the assignments of singers to concerts in stadiums.
  - **assignment**: A binary variable indicating whether a singer is assigned to a concert in a stadium. This variable is used as a decision variable in the optimization model.

### Current Stored Values  
```sql
-- Iteration 1 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on realistic stadium capacities, singer availability, and business constraints to ensure a meaningful and solvable optimization problem.

-- Realistic data for stadium
INSERT INTO stadium (Capacity) VALUES (10000);
INSERT INTO stadium (Capacity) VALUES (20000);
INSERT INTO stadium (Capacity) VALUES (30000);

-- Realistic data for singer_concert_assignment
INSERT INTO singer_concert_assignment (assignment) VALUES (0);
INSERT INTO singer_concert_assignment (assignment) VALUES (1);
INSERT INTO singer_concert_assignment (assignment) VALUES (0);
INSERT INTO singer_concert_assignment (assignment) VALUES (1);
INSERT INTO singer_concert_assignment (assignment) VALUES (0);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- Let \( x_{ij} \) be a binary decision variable where:
  - \( x_{ij} = 1 \) if singer \( i \) is assigned to concert in stadium \( j \),
  - \( x_{ij} = 0 \) otherwise.

#### Objective Function
Maximize the total audience capacity:
\[
\text{Maximize } Z = \sum_{i} \sum_{j} \text{stadium.Capacity}_j \times x_{ij}
\]
- **Coefficient Source**: `stadium.Capacity` from the `stadium` table.

#### Constraints
1. **Singer Assignment Limit**: Each singer \( i \) can participate in at most \( L \) concerts:
\[
\sum_{j} x_{ij} \leq L \quad \forall i
\]
- **Coefficient Source**: \( L \) is a scalar parameter from the business configuration logic.

2. **Stadium Capacity Limit**: Each concert in stadium \( j \) cannot exceed the stadium's capacity:
\[
\sum_{i} x_{ij} \leq \text{stadium.Capacity}_j \quad \forall j
\]
- **Coefficient Source**: `stadium.Capacity` from the `stadium` table.

#### Data Source Verification
- **stadium.Capacity**: From the `stadium` table.
- **L**: From the business configuration logic (scalar parameter for singer limits).

This formulation provides a complete, immediately solvable LINEAR mathematical model with all numerical coefficients derived from the provided data.

## 5. Gurobipy Implementation

```python
#!/usr/bin/env python3
"""
Gurobipy implementation for concert singer optimization problem
"""

import gurobipy as gp
from gurobipy import GRB

def optimize_concert_singer():
    # 1. MODEL & DATA SETUP
    model = gp.Model("concert_singer_optimization")
    
    # Data from the problem
    stadium_capacities = [10000, 20000, 30000]
    singer_assignments = [0, 1, 0, 1, 0]
    L = 2  # Maximum number of concerts a singer can participate in
    
    # Number of singers and stadiums
    n_singers = len(singer_assignments)
    n_stadiums = len(stadium_capacities)
    
    # CRITICAL: Validate array lengths before loops
    assert len(stadium_capacities) == n_stadiums, "Stadium capacities array length mismatch"
    assert len(singer_assignments) == n_singers, "Singer assignments array length mismatch"
    
    # 2. VARIABLES
    # Decision variables: x_ij (binary)
    x = model.addVars(n_singers, n_stadiums, vtype=GRB.BINARY, name="x")
    
    # 3. OBJECTIVE FUNCTION
    # Maximize total audience capacity
    model.setObjective(
        gp.quicksum(stadium_capacities[j] * x[i, j] for i in range(n_singers) for j in range(n_stadiums)),
        GRB.MAXIMIZE
    )
    
    # 4. CONSTRAINTS
    
    # Singer Assignment Limit: Each singer can participate in at most L concerts
    for i in range(n_singers):
        model.addConstr(
            gp.quicksum(x[i, j] for j in range(n_stadiums)) <= L,
            name=f"singer_limit_{i}"
        )
    
    # Stadium Capacity Limit: Each concert in stadium j cannot exceed the stadium's capacity
    for j in range(n_stadiums):
        model.addConstr(
            gp.quicksum(x[i, j] for i in range(n_singers)) <= stadium_capacities[j],
            name=f"stadium_capacity_{j}"
        )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in range(n_singers):
            for j in range(n_stadiums):
                if x[i, j].x > 1e-6:
                    print(f"Singer {i} assigned to stadium {j}: {x[i, j].x:.0f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

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

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 250000.0
**Execution Time**: 0.19 seconds
**Reliability**: high
**Analysis**: Gurobipy found an optimal solution with the highest objective value and the fastest execution time, indicating high efficiency and reliability.

## 6. DOCplex Implementation

```python
#!/usr/bin/env python3
"""
DOCplex implementation for concert_singer optimization problem
"""

from docplex.mp.model import Model

def optimize_concert_singer():
    """Optimize singer assignments to concerts in stadiums to maximize audience capacity."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="concert_singer")
    
    # Data from the database
    stadium_capacities = [10000, 20000, 30000]
    singer_assignments = [0, 1, 0, 1, 0]
    
    # Business configuration parameter
    L = 2  # Maximum number of concerts a singer can participate in
    
    # Validate array lengths
    n_stadiums = len(stadium_capacities)
    n_assignments = len(singer_assignments)
    assert n_stadiums > 0 and n_assignments > 0, "Empty data arrays"
    
    # Safe range for indexing
    safe_range = range(min(n_stadiums, n_assignments))
    
    # 2. VARIABLES
    # Decision variables: x_ij = 1 if singer i is assigned to concert in stadium j, 0 otherwise
    x = {(i, j): mdl.binary_var(name=f"x_{i}_{j}") for i in safe_range for j in safe_range}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize total audience capacity
    objective = mdl.sum(stadium_capacities[j] * x[(i, j)] for i in safe_range for j in safe_range)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # Singer Assignment Limit: Each singer can participate in at most L concerts
    for i in safe_range:
        mdl.add_constraint(mdl.sum(x[(i, j)] for j in safe_range) <= L, ctname=f"singer_limit_{i}")
    
    # Stadium Capacity Limit: Each concert in stadium j cannot exceed the stadium's capacity
    for j in safe_range:
        mdl.add_constraint(mdl.sum(x[(i, j)] for i in safe_range) <= stadium_capacities[j], ctname=f"stadium_capacity_{j}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in safe_range:
            for j in safe_range:
                value = solution.get_value(x[(i, j)])
                if value > 1e-6:
                    print(f"Singer {i} assigned to stadium {j}: {value:.0f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

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

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 150000.0
**Execution Time**: 1.41 seconds
**Reliability**: medium
**Analysis**: DOCplex found an optimal solution but with a significantly lower objective value compared to Gurobipy and Pyomo, suggesting potential issues with solver configuration or model interpretation.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

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

def concert_singer_optimization():
    """Optimize singer assignments to concerts in stadiums to maximize audience capacity."""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # Example data from the problem context
    stadium_capacities = [10000, 20000, 30000]  # Capacities of stadiums
    singer_assignments = [0, 1, 0, 1, 0]  # Initial assignments (binary)
    singer_limit = 2  # Maximum number of concerts a singer can participate in
    
    # CRITICAL: Validate array lengths before indexing
    n_stadiums = len(stadium_capacities)
    n_assignments = len(singer_assignments)
    assert n_stadiums > 0, "No stadiums provided"
    assert n_assignments > 0, "No singer assignments provided"
    
    # 3. SETS (Pyomo way to define indices)
    model.I = pyo.RangeSet(1, n_assignments)  # Singers (1-based indexing)
    model.J = pyo.RangeSet(1, n_stadiums)  # Stadiums (1-based indexing)
    
    # 4. PARAMETERS (data containers)
    model.stadium_capacity = pyo.Param(model.J, initialize={j: stadium_capacities[j-1] for j in model.J})
    model.singer_limit = pyo.Param(initialize=singer_limit)
    
    # 5. VARIABLES
    # Binary decision variable: x[i,j] = 1 if singer i is assigned to stadium j, 0 otherwise
    model.x = pyo.Var(model.I, model.J, within=pyo.Binary)
    
    # 6. OBJECTIVE FUNCTION
    # Maximize the total audience capacity
    def obj_rule(model):
        return sum(model.stadium_capacity[j] * model.x[i, j] for i in model.I for j in model.J)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    
    # Constraint 1: Singer Assignment Limit
    def singer_limit_rule(model, i):
        return sum(model.x[i, j] for j in model.J) <= model.singer_limit
    model.singer_limit_constraint = pyo.Constraint(model.I, rule=singer_limit_rule)
    
    # Constraint 2: Stadium Capacity Limit
    def stadium_capacity_rule(model, j):
        return sum(model.x[i, j] for i in model.I) <= model.stadium_capacity[j]
    model.stadium_capacity_constraint = pyo.Constraint(model.J, rule=stadium_capacity_rule)
    
    # 8. SOLVING WITH GUROBI (your available solver)
    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:
            for j in model.J:
                if pyo.value(model.x[i, j]) > 0.5:  # Only print assignments
                    print(f"Singer {i} assigned to Stadium {j}")
        
    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
if __name__ == "__main__":
    concert_singer_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 250000.0
**Execution Time**: 0.99 seconds
**Reliability**: high
**Analysis**: Pyomo found an optimal solution with the same objective value as Gurobipy but with a longer execution time, indicating reliability but lower efficiency.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 250000.00 | 0.19s | N/A | N/A |
| Docplex | OPTIMAL | 150000.00 | 1.41s | N/A | N/A |
| Pyomo | OPTIMAL | 250000.00 | 0.99s | N/A | N/A |

### Solver Consistency Analysis
**Result**: Solvers produced inconsistent results
**Consistent Solvers**: gurobipy, pyomo
**Inconsistent Solvers**: docplex
**Potential Issues**:
- Possible misconfiguration in DOCplex solver
- Different interpretations of the model constraints or objective function in DOCplex
**Majority Vote Optimal Value**: 250000.0

### Final Recommendation
**Recommended Optimal Value**: 250000.0
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy is recommended due to its high reliability, optimal solution, and fastest execution time. Pyomo also provides a consistent solution but is less efficient.

### Business Interpretation
**Overall Strategy**: The optimal solution maximizes the total audience capacity across all concerts, ensuring the best possible utilization of stadium resources.
**Objective Value Meaning**: The optimal objective value of 250,000 represents the maximum total audience capacity achievable under the given constraints.
**Resource Allocation Summary**: Resources should be allocated to ensure that each singer is assigned to concerts in a way that maximizes the total audience capacity without exceeding individual singer limits or stadium capacities.
**Implementation Recommendations**: Implement the optimal assignments of singers to concerts as determined by the solver. Ensure that the constraints on singer participation limits and stadium capacities are strictly adhered to during implementation.