## 4. Mathematical Optimization Formulation

#### Decision Variables
- Let \( x_{ij} \) be a binary decision variable where \( x_{ij} = 1 \) if concert \( i \) is held at stadium \( j \), and \( x_{ij} = 0 \) otherwise.

#### Objective Function
Maximize the total expected attendance across all concerts and stadiums:

\[
\text{Maximize } \sum_{(i,j) \in \text{concert\_stadium\_mapping}} \text{attendance\_coefficients.coefficient}_{ij} \times x_{ij}
\]

Data Source Verification:
- Coefficients for the objective function are sourced from the `attendance_coefficients.coefficient` column.

#### Constraints
1. **Stadium Capacity Constraints**: Ensure that the expected attendance for each concert at a stadium does not exceed the stadium's capacity.

\[
\sum_{i} \text{attendance\_coefficients.coefficient}_{ij} \times x_{ij} \leq \text{stadium\_capacity.capacity}_j \quad \forall j
\]

Data Source Verification:
- Capacities are sourced from the `stadium_capacity.capacity` column.

2. **Concert Venue Assignment Constraints**: Ensure that each concert is held at least once at one of the available stadiums.

\[
\sum_{j} x_{ij} \geq 1 \quad \forall i
\]

Data Source Verification:
- The requirement that each concert must be held at least once is derived from the business problem description.

This formulation ensures that the optimization model is linear, adhering to the constraints and objectives outlined in the business problem. The decision variables, objective function, and constraints are all expressed in linear terms, making the model suitable for solving using linear programming techniques.

## 5. Gurobipy Implementation

```python
# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_concert_singer():
    """Optimize concert-stadium pairings to maximize attendance"""

    # 1. MODEL & DATA SETUP
    model = gp.Model("concert_singer")

    # Data from the problem context
    concert_stadium_mapping = [(1, 101), (2, 102), (3, 103)]
    attendance_coefficients = {
        (1, 101): 120.0,
        (1, 102): 180.0,
        (2, 102): 250.0,
        (2, 103): 300.0,
        (3, 103): 400.0
    }
    stadium_capacity = {
        101: 5000,
        102: 10000,
        103: 15000
    }

    # Validate data lengths
    assert len(concert_stadium_mapping) > 0, "Concert-stadium mapping is empty"
    assert len(attendance_coefficients) > 0, "Attendance coefficients are empty"
    assert len(stadium_capacity) > 0, "Stadium capacity data is empty"

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

    # 3. OBJECTIVE FUNCTION
    model.setObjective(
        gp.quicksum(attendance_coefficients.get((i, j), 150) * x[i, j] for i, j in concert_stadium_mapping),
        GRB.MAXIMIZE
    )

    # 4. CONSTRAINTS

    # Stadium Capacity Constraints
    for j in stadium_capacity:
        model.addConstr(
            gp.quicksum(attendance_coefficients.get((i, j), 150) * x[i, j] for i, _ in concert_stadium_mapping if j == _)
            <= stadium_capacity[j],
            name=f"capacity_{j}"
        )

    # Concert Venue Assignment Constraints
    for i, _ in set(concert_stadium_mapping):
        model.addConstr(
            gp.quicksum(x[i, j] for _, j in concert_stadium_mapping if i == _) >= 1,
            name=f"venue_assignment_{i}"
        )

    # 5. SOLVING & RESULTS
    model.optimize()

    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i, j in concert_stadium_mapping:
            if x[i, j].x > 1e-6:
                print(f"Concert {i} at Stadium {j} is selected with expected attendance: {attendance_coefficients.get((i, j), 150)}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")

    return model

# Run the optimization
optimize_concert_singer()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 770.0
**Execution Time**: 0.82 seconds
**Reliability**: high
**Analysis**: Gurobipy successfully found an optimal solution with a high reliability due to its robust performance and quick execution time.

## 6. DOCplex Implementation

```python
# Complete DOCPLEX implementation

from docplex.mp.model import Model

def concert_singer_optimization():
    """Concert Singer Optimization using DOCPLEX"""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="concert_singer")
    
    # Data from the problem statement
    concert_stadium_mapping = [(1, 101), (2, 102), (3, 103)]
    attendance_coefficients = {
        (1, 101): 120.0,
        (1, 102): 180.0,
        (2, 102): 250.0,
        (2, 103): 300.0,
        (3, 103): 400.0
    }
    stadium_capacity = {
        101: 5000,
        102: 10000,
        103: 15000
    }
    
    # Extract unique concerts and stadiums
    concerts = set(concert_id for concert_id, _ in concert_stadium_mapping)
    stadiums = set(stadium_id for _, stadium_id in concert_stadium_mapping)
    
    # CRITICAL: Validate data consistency
    assert all((concert_id, stadium_id) in attendance_coefficients for concert_id, stadium_id in concert_stadium_mapping), "Missing coefficients"
    assert all(stadium_id in stadium_capacity for _, stadium_id in concert_stadium_mapping), "Missing capacities"
    
    # 2. VARIABLES
    x = {(i, j): mdl.binary_var(name=f"x_{i}_{j}") for i, j in concert_stadium_mapping}
    
    # 3. OBJECTIVE FUNCTION
    objective = mdl.sum(attendance_coefficients[i, j] * x[i, j] for i, j in concert_stadium_mapping)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # Stadium Capacity Constraints
    for j in stadiums:
        mdl.add_constraint(
            mdl.sum(attendance_coefficients[i, j] * x[i, j] for i in concerts if (i, j) in concert_stadium_mapping) <= stadium_capacity[j],
            ctname=f"capacity_{j}"
        )
    
    # Concert Venue Assignment Constraints
    for i in concerts:
        mdl.add_constraint(
            mdl.sum(x[i, j] for j in stadiums if (i, j) in concert_stadium_mapping) >= 1,
            ctname=f"concert_{i}_venue"
        )
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i, j in concert_stadium_mapping:
            value = solution.get_value(x[i, j])
            if value > 1e-6:
                print(f"x[{i},{j}] = {value:.3f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Run the optimization
concert_singer_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 770.0
**Execution Time**: 7.81 seconds
**Reliability**: high
**Analysis**: DOCplex also found an optimal solution, matching Gurobipy's result, but took longer to execute. The reliability remains high due to consistent results.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

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

def concert_singer_optimization():
    """Concert Singer Optimization Model"""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # Data from the problem statement
    concert_stadium_mapping = [(1, 101), (2, 102), (3, 103)]
    attendance_coefficients = {
        (1, 101): 120.0,
        (1, 102): 180.0,
        (2, 102): 250.0,
        (2, 103): 300.0,
        (3, 103): 400.0
    }
    stadium_capacity = {
        101: 5000,
        102: 10000,
        103: 15000
    }
    
    # 3. SETS
    model.Concerts = pyo.Set(initialize=set(i for i, _ in concert_stadium_mapping))
    model.Stadiums = pyo.Set(initialize=set(j for _, j in concert_stadium_mapping))
    model.ConcertStadiumPairs = pyo.Set(initialize=concert_stadium_mapping)
    
    # 4. PARAMETERS
    model.AttendanceCoefficient = pyo.Param(model.ConcertStadiumPairs, initialize=attendance_coefficients)
    model.StadiumCapacity = pyo.Param(model.Stadiums, initialize=stadium_capacity)
    
    # 5. VARIABLES
    model.x = pyo.Var(model.ConcertStadiumPairs, within=pyo.Binary)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.AttendanceCoefficient[i, j] * model.x[i, j] for i, j in model.ConcertStadiumPairs)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    
    # Stadium Capacity Constraints
    def stadium_capacity_rule(model, j):
        return sum(model.AttendanceCoefficient[i, j] * model.x[i, j] for i in model.Concerts if (i, j) in model.ConcertStadiumPairs) <= model.StadiumCapacity[j]
    model.stadium_capacity_constraint = pyo.Constraint(model.Stadiums, rule=stadium_capacity_rule)
    
    # Concert Venue Assignment Constraints
    def concert_venue_rule(model, i):
        return sum(model.x[i, j] for j in model.Stadiums if (i, j) in model.ConcertStadiumPairs) >= 1
    model.concert_venue_constraint = pyo.Constraint(model.Concerts, rule=concert_venue_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, j in model.ConcertStadiumPairs:
            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

# Run the optimization
concert_singer_optimization()
```

### Execution Results
**Status**: ERROR
**Error**: Traceback (most recent call last):
  File "/tmp/tmpktoqcyos.py", line 90, in <module>
    concert_singer_optimization()
  File "/tmp/tmpktoqcyos.py", line 34, in concert_singer_optimization
    model.AttendanceCoefficient = pyo.Param(model.ConcertStadiumPairs, initialize=attendance_coefficients)
  File "/dccstor/nl2opt/miniforge3/envs/nl2opt_optim/lib/python3.10/site-packages/pyomo/core/base/block.py", line 571, in __setattr__
    self.add_component(name, val)
  File "/dccstor/nl2opt/miniforge3/envs/nl2opt_optim/lib/python3.10/site-packages/pyomo/core/base/block.py", line 1101, in add_component
    val.construct(data)
  File "/dccstor/nl2opt/miniforge3/envs/nl2opt_optim/lib/python3.10/site-packages/pyomo/core/base/param.py", line 850, in construct
    self._construct_from_rule_using_setitem()
  File "/dccstor/nl2opt/miniforge3/envs/nl2opt_optim/lib/python3.10/site-packages/pyomo/core/base/indexed_component.py", line 772, in _construct_from_rule_using_setitem
    self[index] = rule(block, index)
  File "/dccstor/nl2opt/miniforge3/envs/nl2opt_optim/lib/python3.10/site-packages/pyomo/core/base/indexed_component.py", line 692, in __setitem__
    index = self._validate_index(index)
  File "/dccstor/nl2opt/miniforge3/envs/nl2opt_optim/lib/python3.10/site-packages/pyomo/core/base/indexed_component.py", line 870, in _validate_index
    raise KeyError(
KeyError: "Index '(1, 102)' is not valid for indexed component 'AttendanceCoefficient'"

**Analysis**: Pyomo encountered an error related to index validation, indicating a potential issue with data input or model setup. This reduces its reliability for this problem.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 770.00 | 0.82s | N/A | N/A |
| Docplex | OPTIMAL | 770.00 | 7.81s | N/A | N/A |
| Pyomo | ERROR | N/A | 2.83s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 770.0
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy is preferred due to its faster execution time and high reliability, providing consistent results with DOCplex.

### Business Interpretation
**Overall Strategy**: The optimal solution maximizes expected attendance, ensuring concerts are held at stadiums with sufficient capacity.
**Objective Value Meaning**: The optimal objective value of 770.0 represents the maximum total expected attendance across all concerts and stadiums.
**Resource Allocation Summary**: Concerts should be allocated to stadiums where they can achieve the highest attendance without exceeding capacity.
**Implementation Recommendations**: Ensure data integrity for indices and coefficients, and use Gurobipy for future optimization tasks due to its efficiency and reliability.