# Complete Optimization Problem and Solution: wta_1

## 1. Problem Context and Goals

### Context  
The business problem revolves around optimizing the scheduling of players across various tournaments to maximize the total ranking points earned. This involves making decisions about which players should participate in which tournaments, considering their availability, the maximum number of tournaments each player can participate in, and the maximum number of players allowed in each tournament. 

The decision to be made is whether a specific player participates in a specific tournament, represented as a binary choice. The operational parameters include the ranking points each player can earn in each tournament, which directly influence the objective of maximizing the total points. 

Business configuration parameters play a crucial role in defining the constraints. For instance, each player is limited to participating in a maximum of five tournaments to balance their schedule and performance. Additionally, each tournament has a cap on the total ranking points that can be earned, set at 2000 points, to reflect realistic point distributions.

### Goals  
The primary goal of this optimization is to maximize the total ranking points earned by players across all tournaments. This is achieved by strategically deciding which players participate in which tournaments, ensuring that the constraints on player participation and tournament capacity are respected. Success is measured by the total ranking points accumulated, which directly ties back to the operational data on ranking points earned by players in specific tournaments.

## 2. Constraints    

The optimization problem is subject to several constraints that ensure the decisions made are feasible and align with business rules:

1. **Player Participation Limit**: Each player can participate in a maximum of five tournaments. This constraint ensures that players are not over-scheduled, balancing their performance and well-being.

2. **Tournament Capacity Limit**: Each tournament has a maximum number of players that can participate. This constraint ensures that the tournaments do not exceed their capacity, maintaining the quality and manageability of the events.

3. **Player Availability**: A player can only participate in a tournament if they are available. This constraint ensures that the scheduling respects the availability status of each player, preventing unrealistic or impossible assignments.

These constraints are designed to be linear, ensuring that the optimization problem remains straightforward and solvable using linear or mixed-integer optimization techniques.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 2 Database Schema
-- Objective: Schema changes include creating a new table for ranking points, modifying existing tables to align with OR expert's mapping, and updating business configuration logic for scalar parameters and formulas.

CREATE TABLE player_availability (
  player_id INTEGER,
  availability BOOLEAN
);

CREATE TABLE player_tournament_participation (
  player_id INTEGER,
  tournament_id INTEGER,
  participation BOOLEAN
);

CREATE TABLE matches (
  match_id INTEGER,
  tournament_id INTEGER,
  draw_size INTEGER,
  max_players_per_tournament INTEGER
);

CREATE TABLE player_tournament_ranking_points (
  player_id INTEGER,
  tournament_id INTEGER,
  ranking_points INTEGER
);
```

### Data Dictionary  
- **player_availability**: Tracks whether players are available to participate in tournaments. The `player_id` uniquely identifies each player, and the `availability` column indicates their participation status.
  
- **player_tournament_participation**: Represents the decision of whether a player participates in a specific tournament. The `player_id` and `tournament_id` link the participation to specific players and tournaments, while the `participation` column is the binary decision variable.

- **matches**: Contains details about matches in tournaments. The `tournament_id` links matches to specific tournaments, and the `max_players_per_tournament` column specifies the maximum number of players allowed in each tournament.

- **player_tournament_ranking_points**: Records the ranking points earned by players in specific tournaments. The `player_id` and `tournament_id` link the points to specific players and tournaments, and the `ranking_points` column provides the points earned, which are used as coefficients in the objective function.

### Current Stored Values  
```sql
-- Iteration 2 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on realistic tournament scheduling, player availability, and ranking points allocation, ensuring that the optimization problem remains meaningful and solvable.

-- Realistic data for player_availability
INSERT INTO player_availability (player_id, availability) VALUES (1, True);
INSERT INTO player_availability (player_id, availability) VALUES (2, False);
INSERT INTO player_availability (player_id, availability) VALUES (3, True);

-- Realistic data for player_tournament_participation
INSERT INTO player_tournament_participation (player_id, tournament_id, participation) VALUES (1, 101, True);
INSERT INTO player_tournament_participation (player_id, tournament_id, participation) VALUES (2, 102, False);
INSERT INTO player_tournament_participation (player_id, tournament_id, participation) VALUES (3, 103, True);

-- Realistic data for matches
INSERT INTO matches (match_id, tournament_id, draw_size, max_players_per_tournament) VALUES (1001, 101, 32, 32);
INSERT INTO matches (match_id, tournament_id, draw_size, max_players_per_tournament) VALUES (1002, 102, 64, 64);
INSERT INTO matches (match_id, tournament_id, draw_size, max_players_per_tournament) VALUES (1003, 103, 128, 128);

-- Realistic data for player_tournament_ranking_points
INSERT INTO player_tournament_ranking_points (player_id, tournament_id, ranking_points) VALUES (1, 101, 100);
INSERT INTO player_tournament_ranking_points (player_id, tournament_id, ranking_points) VALUES (2, 102, 200);
INSERT INTO player_tournament_ranking_points (player_id, tournament_id, ranking_points) VALUES (3, 103, 300);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
Let \( x_{p,t} \) be a binary decision variable where:
- \( x_{p,t} = 1 \) if player \( p \) participates in tournament \( t \),
- \( x_{p,t} = 0 \) otherwise.

#### Objective Function
Maximize the total ranking points earned by all players across all tournaments:
\[
\text{Maximize} \quad \sum_{p} \sum_{t} \text{ranking\_points}_{p,t} \cdot x_{p,t}
\]
where \( \text{ranking\_points}_{p,t} \) is the ranking points player \( p \) can earn in tournament \( t \).

#### Constraints
1. **Player Participation Limit**: Each player can participate in a maximum of 5 tournaments:
\[
\sum_{t} x_{p,t} \leq 5 \quad \forall p
\]

2. **Tournament Capacity Limit**: Each tournament \( t \) has a maximum number of players that can participate:
\[
\sum_{p} x_{p,t} \leq \text{max\_players\_per\_tournament}_t \quad \forall t
\]

3. **Player Availability**: A player can only participate in a tournament if they are available:
\[
x_{p,t} \leq \text{availability}_p \quad \forall p, t
\]

#### Data Source Verification
- **ranking\_points_{p,t}**: `player_tournament_ranking_points.ranking_points`
- **max\_players\_per\_tournament_t**: `matches.max_players_per_tournament`
- **availability_p**: `player_availability.availability`

### Complete Numerical Model
Using the provided data, the numerical model is as follows:

#### Decision Variables
\( x_{1,101}, x_{1,102}, x_{1,103}, x_{2,101}, x_{2,102}, x_{2,103}, x_{3,101}, x_{3,102}, x_{3,103} \)

#### Objective Function
\[
\text{Maximize} \quad 100x_{1,101} + 200x_{2,102} + 300x_{3,103}
\]

#### Constraints
1. **Player Participation Limit**:
\[
x_{1,101} + x_{1,102} + x_{1,103} \leq 5 \\
x_{2,101} + x_{2,102} + x_{2,103} \leq 5 \\
x_{3,101} + x_{3,102} + x_{3,103} \leq 5
\]

2. **Tournament Capacity Limit**:
\[
x_{1,101} + x_{2,101} + x_{3,101} \leq 32 \\
x_{1,102} + x_{2,102} + x_{3,102} \leq 64 \\
x_{1,103} + x_{2,103} + x_{3,103} \leq 128
\]

3. **Player Availability**:
\[
x_{1,101} \leq 1, \quad x_{1,102} \leq 1, \quad x_{1,103} \leq 1 \\
x_{2,101} \leq 0, \quad x_{2,102} \leq 0, \quad x_{2,103} \leq 0 \\
x_{3,101} \leq 1, \quad x_{3,102} \leq 1, \quad x_{3,103} \leq 1
\]

This is a complete, immediately solvable linear programming model.

## 5. Gurobipy Implementation

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

import gurobipy as gp
from gurobipy import GRB

def optimize_tournament_scheduling():
    # 1. MODEL & DATA SETUP
    model = gp.Model("tournament_scheduling")

    # Data from the problem
    players = [1, 2, 3]
    tournaments = [101, 102, 103]

    # Ranking points for each player in each tournament
    ranking_points = {
        (1, 101): 100,
        (2, 102): 200,
        (3, 103): 300
    }

    # Maximum players per tournament
    max_players_per_tournament = {
        101: 32,
        102: 64,
        103: 128
    }

    # Player availability
    availability = {
        1: True,
        2: False,
        3: True
    }

    # CRITICAL: Validate array lengths before loops
    assert len(players) == 3, "Player array length mismatch"
    assert len(tournaments) == 3, "Tournament array length mismatch"

    # 2. VARIABLES
    # Decision variables: x[p, t] = 1 if player p participates in tournament t, 0 otherwise
    x = model.addVars(players, tournaments, vtype=GRB.BINARY, name="x")

    # 3. OBJECTIVE FUNCTION
    # Maximize total ranking points
    model.setObjective(gp.quicksum(ranking_points[p, t] * x[p, t] for p in players for t in tournaments), GRB.MAXIMIZE)

    # 4. CONSTRAINTS

    # Player Participation Limit: Each player can participate in a maximum of 5 tournaments
    for p in players:
        model.addConstr(gp.quicksum(x[p, t] for t in tournaments) <= 5, name=f"player_limit_{p}")

    # Tournament Capacity Limit: Each tournament has a maximum number of players
    for t in tournaments:
        model.addConstr(gp.quicksum(x[p, t] for p in players) <= max_players_per_tournament[t], name=f"tournament_capacity_{t}")

    # Player Availability: A player can only participate in a tournament if they are available
    for p in players:
        for t in tournaments:
            model.addConstr(x[p, t] <= int(availability[p]), name=f"availability_{p}_{t}")

    # 5. SOLVING & RESULTS
    model.optimize()

    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for p in players:
            for t in tournaments:
                if x[p, t].x > 0.5:
                    print(f"Player {p} participates in tournament {t}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")

    return model

# Execute the optimization
optimize_tournament_scheduling()
```

### Execution Results
**Status**: ERROR
**Error**: Traceback (most recent call last):
  File "/tmp/tmp1gp_o9z_.py", line 79, in <module>
    optimize_tournament_scheduling()
  File "/tmp/tmp1gp_o9z_.py", line 45, in optimize_tournament_scheduling
    model.setObjective(gp.quicksum(ranking_points[p, t] * x[p, t] for p in players for t in tournaments), GRB.MAXIMIZE)
  File "src/gurobipy/_helpers.pyx", line 41, in gurobipy._helpers.quicksum
  File "/tmp/tmp1gp_o9z_.py", line 45, in <genexpr>
    model.setObjective(gp.quicksum(ranking_points[p, t] * x[p, t] for p in players for t in tournaments), GRB.MAXIMIZE)
KeyError: (1, 102)

**Analysis**: Gurobipy encountered a KeyError indicating a missing key in the ranking_points dictionary. This suggests a data inconsistency or missing data point for player 1 and tournament 102. The solver was unable to proceed due to this error.

## 6. DOCplex Implementation

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

from docplex.mp.model import Model

def optimize_tournament_scheduling():
    # 1. MODEL & DATA SETUP
    mdl = Model(name="tournament_scheduling")
    
    # Data from the problem
    players = [1, 2, 3]
    tournaments = [101, 102, 103]
    
    # Ranking points data
    ranking_points = {
        (1, 101): 100,
        (2, 102): 200,
        (3, 103): 300
    }
    
    # Player availability data
    availability = {
        1: True,
        2: False,
        3: True
    }
    
    # Tournament capacity data
    max_players_per_tournament = {
        101: 32,
        102: 64,
        103: 128
    }
    
    # 2. VARIABLES
    x = mdl.binary_var_dict([(p, t) for p in players for t in tournaments], name="x")
    
    # 3. OBJECTIVE FUNCTION
    objective = mdl.sum(ranking_points.get((p, t), 0) * x[p, t] for p in players for t in tournaments)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # Player Participation Limit
    for p in players:
        mdl.add_constraint(mdl.sum(x[p, t] for t in tournaments) <= 5, ctname=f"participation_limit_{p}")
    
    # Tournament Capacity Limit
    for t in tournaments:
        mdl.add_constraint(mdl.sum(x[p, t] for p in players) <= max_players_per_tournament[t], ctname=f"capacity_limit_{t}")
    
    # Player Availability
    for p in players:
        for t in tournaments:
            mdl.add_constraint(x[p, t] <= int(availability[p]), ctname=f"availability_{p}_{t}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for p in players:
            for t in tournaments:
                if solution.get_value(x[p, t]) > 0:
                    print(f"Player {p} participates in tournament {t}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Execute the optimization
optimize_tournament_scheduling()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 400.0
**Execution Time**: 1.03 seconds
**Reliability**: high
**Retry Attempt**: 4
**Analysis**: DOCplex successfully solved the problem and returned an optimal value of 400.0. The decision variables indicate that player 1 participates in tournament 101 and player 3 participates in tournament 103, while player 2 does not participate in any tournament due to availability constraints.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation - Retry Attempt 4

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

def optimize_tournament_scheduling():
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    players = [1, 2, 3]
    tournaments = [101, 102, 103]
    
    ranking_points = {
        (1, 101): 100,
        (2, 102): 200,
        (3, 103): 300
    }
    
    max_players_per_tournament = {
        101: 32,
        102: 64,
        103: 128
    }
    
    availability = {
        1: True,
        2: False,
        3: True
    }
    
    # 3. SETS
    model.P = pyo.Set(initialize=players)
    model.T = pyo.Set(initialize=tournaments)
    
    # 4. PARAMETERS
    model.ranking_points = pyo.Param(model.P, model.T, initialize=ranking_points, default=0)
    model.max_players_per_tournament = pyo.Param(model.T, initialize=max_players_per_tournament)
    model.availability = pyo.Param(model.P, initialize=availability)
    
    # 5. VARIABLES
    model.x = pyo.Var(model.P, model.T, within=pyo.Binary)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.ranking_points[p, t] * model.x[p, t] for p in model.P for t in model.T)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    
    # Player Participation Limit
    def player_participation_limit_rule(model, p):
        return sum(model.x[p, t] for t in model.T) <= 5
    model.player_participation_limit = pyo.Constraint(model.P, rule=player_participation_limit_rule)
    
    # Tournament Capacity Limit
    def tournament_capacity_limit_rule(model, t):
        return sum(model.x[p, t] for p in model.P) <= model.max_players_per_tournament[t]
    model.tournament_capacity_limit = pyo.Constraint(model.T, rule=tournament_capacity_limit_rule)
    
    # Player Availability
    def player_availability_rule(model, p, t):
        return model.x[p, t] <= model.availability[p]
    model.player_availability = pyo.Constraint(model.P, model.T, rule=player_availability_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
    if results.solver.termination_condition == pyo.TerminationCondition.optimal:
        print(f"Optimal value: {pyo.value(model.objective)}")
    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}")

# Execute the optimization
optimize_tournament_scheduling()
```

### Execution Results
**Status**: ERROR
**Error**: Traceback (most recent call last):
  File "/tmp/tmpow8rodcn.py", line 87, in <module>
    optimize_tournament_scheduling()
  File "/tmp/tmpow8rodcn.py", line 64, in optimize_tournament_scheduling
    model.player_availability = pyo.Constraint(model.P, model.T, rule=player_availability_rule)
  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/constraint.py", line 722, in construct
    self._setitem_when_not_present(index, rule(block, index))
  File "/dccstor/nl2opt/miniforge3/envs/nl2opt_optim/lib/python3.10/site-packages/pyomo/core/base/initializer.py", line 349, in __call__
    return self._fcn(parent, *idx)
  File "/tmp/tmpow8rodcn.py", line 63, in player_availability_rule
    return model.x[p, t] <= model.availability[p]
TypeError: '<=' not supported between instances of 'VarData' and 'bool'

**Analysis**: Pyomo encountered a TypeError indicating an issue with the player availability constraint. The error suggests a mismatch between the types of the decision variable and the availability data, likely due to incorrect data handling or model formulation.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | ERROR | N/A | 0.27s | N/A | 4 |
| Docplex | OPTIMAL | 400.00 | 1.03s | N/A | 4 |
| Pyomo | ERROR | N/A | 0.86s | N/A | 4 |

### Solver Consistency Analysis
**Result**: Solvers produced inconsistent results
**Inconsistent Solvers**: gurobipy, docplex, pyomo
**Potential Issues**:
- Data inconsistency in ranking_points for Gurobipy
- Type mismatch in player availability constraint for Pyomo
- Only DOCplex successfully solved the problem
**Solver Retry Summary**: gurobipy: 4 attempts, docplex: 4 attempts, pyomo: 4 attempts

### Final Recommendation
**Recommended Optimal Value**: 400.0
**Confidence Level**: MEDIUM
**Preferred Solver(s)**: docplex
**Reasoning**: DOCplex is the only solver that successfully solved the problem without errors. The results are consistent with the constraints and data provided, making it the most reliable solution.

### Optimal Decision Variables
- **x_1_101** = 1.000
  - *Business Meaning*: Player 1 participates in tournament 101, earning 100 ranking points.
- **x_2_102** = 0.000
  - *Business Meaning*: Player 2 does not participate in tournament 102 due to unavailability.
- **x_3_103** = 1.000
  - *Business Meaning*: Player 3 participates in tournament 103, earning 300 ranking points.

### Business Interpretation
**Overall Strategy**: The optimal solution suggests that player 1 should participate in tournament 101 and player 3 should participate in tournament 103 to maximize the total ranking points. Player 2 is unavailable and thus does not participate in any tournament.
**Objective Value Meaning**: The total ranking points earned by all players across all tournaments is maximized to 400.0.
**Resource Allocation Summary**: Player 1 is allocated to tournament 101, and player 3 is allocated to tournament 103. Player 2 is not allocated to any tournament due to unavailability.
**Implementation Recommendations**: Ensure player availability data is accurate and consistent. Verify the ranking_points data for all player-tournament combinations. Use DOCplex for solving similar optimization problems in the future.