# Complete Optimization Problem and Solution: wrestler

## 1. Problem Context and Goals

### Context  
The objective is to optimize the assignment of wrestlers to various events in a manner that maximizes the total number of days titles are held. This involves making strategic decisions about which wrestlers participate in which events. Each decision is represented by a binary choice: whether a wrestler is assigned to a specific event. The operational parameters include the maximum number of events a wrestler can participate in and the maximum number of participants allowed per event. These parameters are crucial for ensuring that the assignments adhere to the constraints of wrestler availability and event capacity. The business configuration specifies that each wrestler can participate in a limited number of events, and each event can accommodate a limited number of wrestlers. This setup ensures that the optimization problem remains linear, focusing on maximizing the total days titles are held without involving complex nonlinear relationships.

### Goals  
The primary goal of this optimization problem is to maximize the total number of days that titles are held by wrestlers. This is achieved by strategically assigning wrestlers to events, with the aim of maximizing the cumulative days titles are held across all assignments. The success of this optimization is measured by the total days titles are held, which is directly influenced by the assignments made. The goal is articulated in a linear fashion, focusing on maximizing the sum of the days titles are held, as determined by the assignments of wrestlers to events.

## 2. Constraints    

The constraints for this optimization problem are designed to ensure that the assignments of wrestlers to events are feasible and adhere to the operational limits. Each wrestler can only participate in a limited number of events, as specified by their maximum event participation capacity. Similarly, each event can only accommodate a certain number of participants, ensuring that the event remains competitive and engaging. These constraints are expressed in a linear form, ensuring that the problem remains within the realm of linear optimization. Specifically, the constraints are:
- Each wrestler can participate in no more than their maximum allowed number of events.
- Each event can have no more participants than its maximum capacity allows.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 1 Database Schema
-- Objective: Schema changes include creating new tables for decision variables and constraint bounds, modifying existing tables to fill mapping gaps, and updating business configuration logic for scalar parameters and formulas.

CREATE TABLE Wrestler (
  WrestlerID INTEGER,
  Days_held INTEGER,
  MaxEvents INTEGER
);

CREATE TABLE WrestlerEventAssignment (
  WrestlerID INTEGER,
  EventID INTEGER,
  Assigned BOOLEAN
);

CREATE TABLE EventConstraints (
  EventID INTEGER,
  MaxParticipants INTEGER
);
```

### Data Dictionary  
The data dictionary provides a comprehensive overview of the tables and columns used in this optimization problem, detailing their business purposes and roles in the optimization process:

- **Wrestler Table**: This table stores information about each wrestler, including their unique identifier, the number of days they hold a title, and the maximum number of events they can participate in. The data in this table is used to determine the objective coefficients and constraint bounds for wrestler participation.

- **WrestlerEventAssignment Table**: This table tracks the assignments of wrestlers to events. It includes the wrestler's identifier, the event's identifier, and a binary indicator of whether the wrestler is assigned to the event. This table is crucial for defining the decision variables in the optimization problem.

- **EventConstraints Table**: This table contains constraints related to event participation, including the event's identifier and the maximum number of participants allowed. The data in this table is used to establish the constraint bounds for event participation.

### Current Stored Values  
```sql
-- Iteration 1 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on typical wrestling event structures and participation limits, ensuring a balance between wrestler availability and event capacity.

-- Realistic data for Wrestler
INSERT INTO Wrestler (WrestlerID, Days_held, MaxEvents) VALUES (1, 30, 5);
INSERT INTO Wrestler (WrestlerID, Days_held, MaxEvents) VALUES (2, 45, 6);
INSERT INTO Wrestler (WrestlerID, Days_held, MaxEvents) VALUES (3, 60, 7);

-- Realistic data for WrestlerEventAssignment
INSERT INTO WrestlerEventAssignment (WrestlerID, EventID, Assigned) VALUES (1, 101, True);
INSERT INTO WrestlerEventAssignment (WrestlerID, EventID, Assigned) VALUES (2, 102, True);
INSERT INTO WrestlerEventAssignment (WrestlerID, EventID, Assigned) VALUES (3, 103, True);
INSERT INTO WrestlerEventAssignment (WrestlerID, EventID, Assigned) VALUES (1, 102, False);
INSERT INTO WrestlerEventAssignment (WrestlerID, EventID, Assigned) VALUES (2, 103, False);

-- Realistic data for EventConstraints
INSERT INTO EventConstraints (EventID, MaxParticipants) VALUES (101, 10);
INSERT INTO EventConstraints (EventID, MaxParticipants) VALUES (102, 12);
INSERT INTO EventConstraints (EventID, MaxParticipants) VALUES (103, 15);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
Let \( x_{ij} \) be a binary decision variable where:
- \( x_{ij} = 1 \) if wrestler \( i \) is assigned to event \( j \)
- \( x_{ij} = 0 \) otherwise

#### Objective Function
Maximize the total number of days titles are held by wrestlers:
\[
\text{Maximize } \sum_{i} \sum_{j} \text{Days\_held}_i \times x_{ij}
\]
where \(\text{Days\_held}_i\) is the number of days wrestler \( i \) holds a title, sourced from the Wrestler table.

#### Constraints
1. **Wrestler Participation Constraints**: Each wrestler can participate in no more than their maximum allowed number of events.
   \[
   \sum_{j} x_{ij} \leq \text{MaxEvents}_i \quad \forall i
   \]
   where \(\text{MaxEvents}_i\) is the maximum number of events wrestler \( i \) can participate in, sourced from the Wrestler table.

2. **Event Capacity Constraints**: Each event can have no more participants than its maximum capacity allows.
   \[
   \sum_{i} x_{ij} \leq \text{MaxParticipants}_j \quad \forall j
   \]
   where \(\text{MaxParticipants}_j\) is the maximum number of participants allowed in event \( j \), sourced from the EventConstraints table.

#### Data Source Verification
- **Objective Function Coefficients**: \(\text{Days\_held}_i\) from Wrestler.Days_held
- **Wrestler Participation Constraints**: \(\text{MaxEvents}_i\) from Wrestler.MaxEvents
- **Event Capacity Constraints**: \(\text{MaxParticipants}_j\) from EventConstraints.MaxParticipants

This formulation provides a complete and immediately solvable linear mathematical model using the provided data, ensuring that all constraints and the objective function are linear.

## 5. Gurobipy Implementation

```python
# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_wrestler_assignments():
    """Optimize wrestler assignments to maximize days titles are held."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("wrestler_assignment")
    
    # Wrestler data
    wrestlers = [
        {"WrestlerID": 1, "Days_held": 30, "MaxEvents": 5},
        {"WrestlerID": 2, "Days_held": 45, "MaxEvents": 6},
        {"WrestlerID": 3, "Days_held": 60, "MaxEvents": 7}
    ]
    
    # Event constraints
    events = [
        {"EventID": 101, "MaxParticipants": 10},
        {"EventID": 102, "MaxParticipants": 12},
        {"EventID": 103, "MaxParticipants": 15}
    ]
    
    # Validate data lengths
    n_wrestlers = len(wrestlers)
    n_events = len(events)
    assert n_wrestlers > 0 and n_events > 0, "No wrestlers or events available"
    
    # 2. VARIABLES
    # Decision variables: x[i, j] = 1 if wrestler i is assigned to event j
    x = model.addVars(n_wrestlers, n_events, vtype=GRB.BINARY, name="x")
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total number of days titles are held
    model.setObjective(
        gp.quicksum(wrestlers[i]["Days_held"] * x[i, j] for i in range(n_wrestlers) for j in range(n_events)),
        GRB.MAXIMIZE
    )
    
    # 4. CONSTRAINTS
    # Wrestler participation constraints
    for i in range(n_wrestlers):
        model.addConstr(
            gp.quicksum(x[i, j] for j in range(n_events)) <= wrestlers[i]["MaxEvents"],
            name=f"wrestler_max_events_{i}"
        )
    
    # Event capacity constraints
    for j in range(n_events):
        model.addConstr(
            gp.quicksum(x[i, j] for i in range(n_wrestlers)) <= events[j]["MaxParticipants"],
            name=f"event_max_participants_{j}"
        )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in range(n_wrestlers):
            for j in range(n_events):
                if x[i, j].x > 1e-6:
                    print(f"Wrestler {wrestlers[i]['WrestlerID']} assigned to Event {events[j]['EventID']}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
optimize_wrestler_assignments()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 405.0
**Execution Time**: 0.62 seconds
**Reliability**: high
**Analysis**: Gurobipy found an optimal solution quickly with no errors, indicating high reliability.

## 6. DOCplex Implementation

```python
# Complete DOCPLEX implementation

from docplex.mp.model import Model

def optimize_wrestler_assignments():
    """Optimize wrestler assignments to maximize days titles are held."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="wrestler_assignment")
    
    # Wrestler data
    wrestler_ids = [1, 2, 3]
    days_held = [30, 45, 60]
    max_events = [5, 6, 7]
    
    # Event data
    event_ids = [101, 102, 103]
    max_participants = [10, 12, 15]
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(wrestler_ids) == len(days_held) == len(max_events), "Wrestler array length mismatch"
    assert len(event_ids) == len(max_participants), "Event array length mismatch"
    
    # Safe indexing
    safe_wrestler_range = range(len(wrestler_ids))
    safe_event_range = range(len(event_ids))
    
    # 2. VARIABLES
    # Decision variables: x[i][j] = 1 if wrestler i is assigned to event j
    x = {(i, j): mdl.binary_var(name=f"x_{wrestler_ids[i]}_{event_ids[j]}") for i in safe_wrestler_range for j in safe_event_range}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total number of days titles are held
    objective = mdl.sum(days_held[i] * x[i, j] for i in safe_wrestler_range for j in safe_event_range)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # Wrestler Participation Constraints
    for i in safe_wrestler_range:
        mdl.add_constraint(mdl.sum(x[i, j] for j in safe_event_range) <= max_events[i], ctname=f"max_events_{wrestler_ids[i]}")
    
    # Event Capacity Constraints
    for j in safe_event_range:
        mdl.add_constraint(mdl.sum(x[i, j] for i in safe_wrestler_range) <= max_participants[j], ctname=f"max_participants_{event_ids[j]}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in safe_wrestler_range:
            for j in safe_event_range:
                value = solution.get_value(x[i, j])
                if value > 1e-6:
                    print(f"Wrestler {wrestler_ids[i]} assigned to Event {event_ids[j]}: {value}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")

    return mdl

# Run the optimization
optimize_wrestler_assignments()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 405.0
**Execution Time**: 3.03 seconds
**Reliability**: high
**Analysis**: DOCplex also found an optimal solution, albeit with a longer execution time, but still reliable.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

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

def wrestler_optimization():
    """Optimize wrestler assignments to maximize days titles are held."""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # Wrestler data
    wrestler_ids = [1, 2, 3]
    days_held = {1: 30, 2: 45, 3: 60}
    max_events = {1: 5, 2: 6, 3: 7}
    
    # Event data
    event_ids = [101, 102, 103]
    max_participants = {101: 10, 102: 12, 103: 15}
    
    # CRITICAL: Validate data lengths
    assert len(wrestler_ids) == len(days_held) == len(max_events), "Wrestler data length mismatch"
    assert len(event_ids) == len(max_participants), "Event data length mismatch"
    
    # 3. SETS
    model.Wrestlers = pyo.Set(initialize=wrestler_ids)
    model.Events = pyo.Set(initialize=event_ids)
    
    # 4. PARAMETERS
    model.DaysHeld = pyo.Param(model.Wrestlers, initialize=days_held)
    model.MaxEvents = pyo.Param(model.Wrestlers, initialize=max_events)
    model.MaxParticipants = pyo.Param(model.Events, initialize=max_participants)
    
    # 5. VARIABLES
    model.x = pyo.Var(model.Wrestlers, model.Events, within=pyo.Binary)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.DaysHeld[i] * model.x[i, j] for i in model.Wrestlers for j in model.Events)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    
    # Wrestler Participation Constraints
    def wrestler_participation_rule(model, i):
        return sum(model.x[i, j] for j in model.Events) <= model.MaxEvents[i]
    model.wrestler_participation_constraint = pyo.Constraint(model.Wrestlers, rule=wrestler_participation_rule)
    
    # Event Capacity Constraints
    def event_capacity_rule(model, j):
        return sum(model.x[i, j] for i in model.Wrestlers) <= model.MaxParticipants[j]
    model.event_capacity_constraint = pyo.Constraint(model.Events, rule=event_capacity_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)
    
    # 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("\nAssignment of wrestlers to events:")
        for i in model.Wrestlers:
            for j in model.Events:
                if pyo.value(model.x[i, j]) > 0.5:  # Only print assignments
                    print(f"Wrestler {i} assigned to Event {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
wrestler_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 405.0
**Execution Time**: 2.20 seconds
**Reliability**: high
**Analysis**: Pyomo achieved the optimal solution with a moderate execution time, confirming its reliability.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 405.00 | 0.62s | N/A | N/A |
| Docplex | OPTIMAL | 405.00 | 3.03s | N/A | N/A |
| Pyomo | OPTIMAL | 405.00 | 2.20s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 405.0
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy provided the optimal solution with the fastest execution time, making it the most efficient choice.

### Business Interpretation
**Overall Strategy**: The optimal solution maximizes the total number of days titles are held by wrestlers, ensuring maximum exposure and engagement.
**Objective Value Meaning**: The optimal objective value of 405.0 indicates the maximum possible days titles are held across all wrestlers and events.
**Resource Allocation Summary**: Wrestlers should be allocated to events in a manner that maximizes their title holding days while respecting participation and event capacity constraints.
**Implementation Recommendations**: Implement the solution by scheduling wrestlers according to the solver's optimal assignments, ensuring compliance with constraints.