## 4. Mathematical Optimization Formulation

#### Decision Variables
- Let \( x_i \) be the number of matches hosted in city \( i \), where \( i \in \{1, 2, 3\} \).  
  \( x_i \) is a non-negative integer variable (MIP).

#### Objective Function
Maximize the total benefit:  
\[
\text{Maximize } Z = \sum_{i=1}^{3} \left( 0.4 \cdot \text{population}_i + 0.3 \cdot \text{gdp}_i + 0.3 \cdot \text{avg\_temperature}_i \right) \cdot x_i
\]

#### Constraints
1. **Total Matches Constraint**:  
\[
\sum_{i=1}^{3} x_i \leq 10
\]
2. **Budget Constraint**:  
\[
\sum_{i=1}^{3} \text{hosting\_cost}_i \cdot x_i \leq 1,000,000
\]
3. **Maximum Matches Per City Constraint**:  
\[
x_i \leq 2 \quad \forall i \in \{1, 2, 3\}
\]

#### Data Source Verification
- **Objective Function Coefficients**:  
  - \( 0.4 \): `objective_coefficients.weight_population`  
  - \( 0.3 \): `objective_coefficients.weight_gdp`  
  - \( 0.3 \): `objective_coefficients.weight_temperature`  
  - `population_i`, `gdp_i`, `avg_temperature_i`: `city_data.population`, `city_data.gdp`, `city_data.avg_temperature`  

- **Constraints**:  
  - **Total Matches Constraint**: Constant 10 is derived from the problem description.  
  - **Budget Constraint**: `hosting_cost_i`: `city_data.hosting_cost`; Constant 1,000,000: `match_constraints.total_budget`  
  - **Maximum Matches Per City Constraint**: Constant 2: `match_constraints.max_matches_per_city`  

#### Numerical Model
Using the provided data:  
- **Objective Function**:  
\[
\text{Maximize } Z = (0.4 \cdot 800,000 + 0.3 \cdot 75,000 + 0.3 \cdot 22) \cdot x_1 + (0.4 \cdot 1,200,000 + 0.3 \cdot 110,000 + 0.3 \cdot 26) \cdot x_2 + (0.4 \cdot 600,000 + 0.3 \cdot 60,000 + 0.3 \cdot 20) \cdot x_3
\]
Simplified:  
\[
\text{Maximize } Z = 320,000 \cdot x_1 + 480,000 \cdot x_2 + 240,000 \cdot x_3
\]

- **Constraints**:  
1. \( x_1 + x_2 + x_3 \leq 10 \)  
2. \( 120,000 \cdot x_1 + 180,000 \cdot x_2 + 100,000 \cdot x_3 \leq 1,000,000 \)  
3. \( x_1 \leq 2 \), \( x_2 \leq 2 \), \( x_3 \leq 2 \)  

This is a complete, immediately solvable Mixed-Integer Linear Programming (MIP) model.

## 5. Gurobipy Implementation

```python
#!/usr/bin/env python3
"""
Gurobipy 12.0.2 Implementation for Host City Selection Optimization
"""

import gurobipy as gp
from gurobipy import GRB

def host_city_optimization():
    """Optimize the selection of host cities for matches"""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("host_city_selection")
    
    # City data
    cities = [1, 2, 3]
    population = {1: 800000, 2: 1200000, 3: 600000}
    gdp = {1: 75000.0, 2: 110000.0, 3: 60000.0}
    avg_temperature = {1: 22.0, 2: 26.0, 3: 20.0}
    hosting_cost = {1: 120000.0, 2: 180000.0, 3: 100000.0}
    
    # Objective coefficients
    weight_population = 0.4
    weight_gdp = 0.3
    weight_temperature = 0.3
    
    # Constraints
    max_matches_per_city = 2
    total_budget = 1000000.0
    total_matches_limit = 10
    
    # CRITICAL: Validate array lengths before loops
    assert len(cities) == len(population) == len(gdp) == len(avg_temperature) == len(hosting_cost), "Array length mismatch"
    
    # 2. VARIABLES
    x = {i: model.addVar(vtype=GRB.INTEGER, name=f"x_{i}", lb=0, ub=max_matches_per_city) for i in cities}
    
    # 3. OBJECTIVE FUNCTION
    model.setObjective(
        gp.quicksum(
            (weight_population * population[i] + weight_gdp * gdp[i] + weight_temperature * avg_temperature[i]) * x[i]
            for i in cities
        ),
        GRB.MAXIMIZE
    )
    
    # 4. CONSTRAINTS
    
    # Total matches constraint
    model.addConstr(
        gp.quicksum(x[i] for i in cities) <= total_matches_limit,
        name="total_matches_constraint"
    )
    
    # Budget constraint
    model.addConstr(
        gp.quicksum(hosting_cost[i] * x[i] for i in cities) <= total_budget,
        name="budget_constraint"
    )
    
    # Maximum matches per city constraint
    for i in cities:
        model.addConstr(
            x[i] <= max_matches_per_city,
            name=f"max_matches_per_city_{i}"
        )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in cities:
            if x[i].x > 1e-6:
                print(f"Matches in city {i}: {x[i].x:.0f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

if __name__ == "__main__":
    host_city_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 2227040.8
**Execution Time**: 0.19 seconds
**Reliability**: high
**Analysis**: Gurobipy found the optimal solution quickly with a low execution time of 0.187 seconds. The solver is highly reliable for this type of MIP problem.

## 6. DOCplex Implementation

```python
#!/usr/bin/env python3
"""
DOCplex Implementation for Host City Selection Optimization
"""

from docplex.mp.model import Model

def optimize_host_cities():
    """Optimize the selection of host cities for matches."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="host_city_selection")
    
    # Data from the problem description
    city_ids = [1, 2, 3]
    populations = [800000, 1200000, 600000]
    gdps = [75000.0, 110000.0, 60000.0]
    avg_temperatures = [22.0, 26.0, 20.0]
    hosting_costs = [120000.0, 180000.0, 100000.0]
    
    # Weights for the objective function
    weight_population = 0.4
    weight_gdp = 0.3
    weight_temperature = 0.3
    
    # Constraints
    max_matches_per_city = 2
    total_budget = 1000000.0
    total_matches_limit = 10
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(city_ids) == len(populations) == len(gdps) == len(avg_temperatures) == len(hosting_costs), "Array length mismatch"
    safe_range = range(min(len(city_ids), len(populations), len(gdps), len(avg_temperatures), len(hosting_costs)))  # Safe indexing
    
    # 2. VARIABLES
    # Decision variables: number of matches hosted in each city
    x = {i: mdl.integer_var(name=f"x_{city_ids[i]}", lb=0, ub=max_matches_per_city) for i in safe_range}
    
    # 3. OBJECTIVE FUNCTION
    # Calculate the benefit for each city
    benefits = [
        (weight_population * populations[i] + weight_gdp * gdps[i] + weight_temperature * avg_temperatures[i]) 
        for i in safe_range
    ]
    
    # Maximize the total benefit
    objective = mdl.sum(benefits[i] * x[i] for i in safe_range)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # Total Matches Constraint
    total_matches = mdl.sum(x[i] for i in safe_range)
    mdl.add_constraint(total_matches <= total_matches_limit, ctname="total_matches_limit")
    
    # Budget Constraint
    total_cost = mdl.sum(hosting_costs[i] * x[i] for i in safe_range)
    mdl.add_constraint(total_cost <= total_budget, ctname="total_budget_limit")
    
    # Maximum Matches Per City Constraint (already enforced by variable bounds)
    
    # 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"Matches in city {city_ids[i]}: {value}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

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

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 2227040.8
**Execution Time**: 0.97 seconds
**Reliability**: high
**Analysis**: DOCplex also found the optimal solution but took slightly longer (0.965 seconds) compared to Gurobipy. It is still a reliable solver for this problem.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

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

def host_city_optimization():
    """Optimization model for selecting host cities for matches."""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # City data
    city_ids = [1, 2, 3]
    populations = [800000, 1200000, 600000]
    gdps = [75000.0, 110000.0, 60000.0]
    avg_temperatures = [22.0, 26.0, 20.0]
    hosting_costs = [120000.0, 180000.0, 100000.0]
    
    # Objective weights
    weight_population = 0.4
    weight_gdp = 0.3
    weight_temperature = 0.3
    
    # Constraints
    max_matches_per_city = 2
    total_budget = 1000000.0
    total_matches = 10
    
    # CRITICAL: Validate array lengths before indexing
    assert len(city_ids) == len(populations) == len(gdps) == len(avg_temperatures) == len(hosting_costs), "Array length mismatch"
    
    # 3. SETS
    model.I = pyo.Set(initialize=city_ids)  # Set of cities
    
    # 4. PARAMETERS
    model.population = pyo.Param(model.I, initialize={i: p for i, p in zip(city_ids, populations)})
    model.gdp = pyo.Param(model.I, initialize={i: g for i, g in zip(city_ids, gdps)})
    model.avg_temperature = pyo.Param(model.I, initialize={i: t for i, t in zip(city_ids, avg_temperatures)})
    model.hosting_cost = pyo.Param(model.I, initialize={i: c for i, c in zip(city_ids, hosting_costs)})
    
    # 5. VARIABLES
    model.x = pyo.Var(model.I, within=pyo.NonNegativeIntegers, bounds=(0, max_matches_per_city))  # Number of matches per city
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum((weight_population * model.population[i] + 
                    weight_gdp * model.gdp[i] + 
                    weight_temperature * model.avg_temperature[i]) * model.x[i] for i in model.I)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    # Total matches constraint
    def total_matches_rule(model):
        return sum(model.x[i] for i in model.I) <= total_matches
    model.total_matches_constraint = pyo.Constraint(rule=total_matches_rule)
    
    # Budget constraint
    def budget_rule(model):
        return sum(model.hosting_cost[i] * model.x[i] for i in model.I) <= total_budget
    model.budget_constraint = pyo.Constraint(rule=budget_rule)
    
    # Maximum matches per city constraint
    def max_matches_rule(model, i):
        return model.x[i] <= max_matches_per_city
    model.max_matches_constraint = pyo.Constraint(model.I, rule=max_matches_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}] = {x_val:.0f}")
        
    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
if __name__ == "__main__":
    host_city_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 2227040.8
**Execution Time**: 0.88 seconds
**Reliability**: high
**Analysis**: Pyomo found the optimal solution with an execution time of 0.881 seconds. It is reliable but slightly slower than Gurobipy.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 2227040.80 | 0.19s | N/A | N/A |
| Docplex | OPTIMAL | 2227040.80 | 0.97s | N/A | N/A |
| Pyomo | OPTIMAL | 2227040.80 | 0.88s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 2227040.8
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy is recommended due to its faster execution time and high reliability. All solvers produced consistent results, but Gurobipy performed the best in terms of speed.

### Optimal Decision Variables
- **x_1** = 2.000
  - *Business Meaning*: Hosting 2 matches in City 1 maximizes the benefit from its population, GDP, and temperature.
- **x_2** = 2.000
  - *Business Meaning*: Hosting 2 matches in City 2 maximizes the benefit from its population, GDP, and temperature.
- **x_3** = 2.000
  - *Business Meaning*: Hosting 2 matches in City 3 maximizes the benefit from its population, GDP, and temperature.

### Business Interpretation
**Overall Strategy**: The optimal solution suggests hosting 2 matches in each city to maximize the total benefit, which is a combination of population, GDP, and average temperature. This allocation ensures the budget is fully utilized while adhering to the constraints.
**Objective Value Meaning**: The optimal objective value of 2,227,040.8 represents the total benefit achieved by hosting 2 matches in each city, considering the weighted contributions of population, GDP, and temperature.
**Resource Allocation Summary**: The budget of 1,000,000 is fully utilized by hosting 2 matches in each city, with hosting costs of 120,000, 180,000, and 100,000 respectively. This ensures the maximum benefit is achieved within the constraints.
**Implementation Recommendations**: 1. Allocate resources to host 2 matches in each city. 2. Ensure the budget is not exceeded by monitoring hosting costs. 3. Verify that the maximum matches per city constraint is adhered to.