# Complete Optimization Problem and Solution: body_builder

## 1. Problem Context and Goals

### Context  
The fitness organization is focused on enhancing the competitive performance of bodybuilders by optimizing their training regimen. The primary decision variables in this optimization are the weights lifted in the Snatch and Clean & Jerk events. These variables are continuous and directly mapped to the bodybuilder's performance in these lifts. The operational goal is to maximize the total weight lifted across these events, aligning with the linear objective of summing the weights lifted in Snatch and Clean & Jerk.

The business configuration includes several key parameters: the target total weight to be lifted by a bodybuilder, which serves as a constraint in the optimization model, and the impact coefficients for Snatch and Clean & Jerk training, which adjust the focus of training in the optimization model. These parameters are crucial for ensuring that the optimization aligns with realistic training impacts and performance targets.

The organization uses current operational data to inform decision-making, ensuring that the optimization problem remains grounded in practical, achievable goals. The constraints are designed to reflect resource limitations and performance targets, ensuring that the optimization remains linear and avoids nonlinear relationships such as variable products or divisions. The business configuration parameters are referenced throughout to maintain consistency and alignment with the optimization objectives.

### Goals  
The primary goal of the optimization is to maximize the total weight lifted by bodybuilders in competitions. This is achieved by focusing on the Snatch and Clean & Jerk lifts, with the objective being to maximize the sum of the weights lifted in these events. Success is measured by the alignment of the optimization with the expected impact coefficients for training, ensuring that the focus on Snatch and Clean & Jerk lifts leads to improved competitive performance. The optimization goal is clearly defined in natural language, emphasizing the linear nature of the objective without resorting to mathematical formulas or symbolic notation.

## 2. Constraints    

The optimization problem includes constraints that ensure the total weight lifted by each bodybuilder does not exceed specified limits. These constraints are directly mapped to the total weight lifted by the bodybuilder, ensuring that the optimization remains within realistic performance boundaries. Additionally, each bodybuilder has a performance target, which serves as a constraint in the optimization model. These constraints are described in business terms, naturally leading to linear mathematical forms without involving variable products or divisions.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 1 Database Schema
-- Objective: Schema adjustments and configuration logic updates were made to address missing performance targets and training impact coefficients, ensuring alignment with optimization requirements.

CREATE TABLE body_builder (
  Snatch FLOAT,
  Clean_Jerk FLOAT,
  Total FLOAT,
  Snatch_Impact FLOAT,
  Clean_Jerk_Impact FLOAT
);

CREATE TABLE bodybuilder_performance (
  Bodybuilder_ID INTEGER,
  Performance_Target FLOAT
);
```

### Data Dictionary  
The data dictionary provides a comprehensive mapping of tables and columns to their business purposes and optimization roles. The `body_builder` table stores individual lift data for bodybuilders, with columns representing the weight lifted in Snatch and Clean & Jerk, the total weight lifted, and the impact coefficients for training. These columns serve as decision variables and objective coefficients in the optimization model. The `bodybuilder_performance` table stores performance metrics and targets for each bodybuilder, linking performance data to individual bodybuilders and serving as a constraint in the optimization model.

### Current Stored Values  
```sql
-- Iteration 1 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on typical competitive weightlifting standards and the need to balance training impact coefficients with performance targets.

-- Realistic data for body_builder
INSERT INTO body_builder (Snatch, Clean_Jerk, Total, Snatch_Impact, Clean_Jerk_Impact) VALUES (85.0, 105.0, 190.0, 1.2, 1.5);
INSERT INTO body_builder (Snatch, Clean_Jerk, Total, Snatch_Impact, Clean_Jerk_Impact) VALUES (95.0, 115.0, 210.0, 1.3, 1.6);
INSERT INTO body_builder (Snatch, Clean_Jerk, Total, Snatch_Impact, Clean_Jerk_Impact) VALUES (100.0, 120.0, 220.0, 1.1, 1.4);

-- Realistic data for bodybuilder_performance
INSERT INTO bodybuilder_performance (Bodybuilder_ID, Performance_Target) VALUES (1, 300.0);
INSERT INTO bodybuilder_performance (Bodybuilder_ID, Performance_Target) VALUES (2, 320.0);
INSERT INTO bodybuilder_performance (Bodybuilder_ID, Performance_Target) VALUES (3, 340.0);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- Let \( x_1 \) be the weight lifted in the Snatch event for a bodybuilder.
- Let \( x_2 \) be the weight lifted in the Clean & Jerk event for a bodybuilder.

#### Objective Function
Maximize the total weight lifted:
\[ \text{Maximize } Z = x_1 + x_2 \]

#### Constraints
For each bodybuilder, we have the following constraints:

1. **Performance Target Constraint**: The total weight lifted should not exceed the performance target for each bodybuilder.
   - For Bodybuilder 1: 
     \[ x_1 + x_2 \leq 300.0 \]
   - For Bodybuilder 2: 
     \[ x_1 + x_2 \leq 320.0 \]
   - For Bodybuilder 3: 
     \[ x_1 + x_2 \leq 340.0 \]

2. **Training Impact Constraints**: These constraints ensure that the training impact coefficients are considered in the optimization.
   - For Bodybuilder 1:
     \[ 1.2 \times x_1 + 1.5 \times x_2 \leq 190.0 \]
   - For Bodybuilder 2:
     \[ 1.3 \times x_1 + 1.6 \times x_2 \leq 210.0 \]
   - For Bodybuilder 3:
     \[ 1.1 \times x_1 + 1.4 \times x_2 \leq 220.0 \]

3. **Non-negativity Constraints**: The weights lifted must be non-negative.
   \[ x_1 \geq 0 \]
   \[ x_2 \geq 0 \]

Data Source Verification:
- The coefficients for the objective function and constraints are derived from the `body_builder` table:
  - Snatch and Clean & Jerk weights are decision variables.
  - Snatch_Impact and Clean_Jerk_Impact are used in the training impact constraints.
- The performance targets are derived from the `bodybuilder_performance` table:
  - Performance_Target is used in the performance target constraints.

## 5. Gurobipy Implementation

```python
# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_bodybuilder_performance():
    """Optimize the total weight lifted by bodybuilders in Snatch and Clean & Jerk events."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("body_builder_optimization")
    
    # Data from the database
    snatch_impact = [1.2, 1.3, 1.1]
    clean_jerk_impact = [1.5, 1.6, 1.4]
    performance_targets = [300.0, 320.0, 340.0]
    training_limits = [190.0, 210.0, 220.0]
    
    n_bodybuilders = len(performance_targets)
    
    # CRITICAL: Validate array lengths before loops
    assert len(snatch_impact) == len(clean_jerk_impact) == len(training_limits) == n_bodybuilders, "Array length mismatch"
    
    # 2. VARIABLES
    # Variables for weights lifted in Snatch and Clean & Jerk
    x_snatch = {i: model.addVar(vtype=GRB.CONTINUOUS, name=f"x_snatch_{i}", lb=0) 
                for i in range(n_bodybuilders)}
    x_clean_jerk = {i: model.addVar(vtype=GRB.CONTINUOUS, name=f"x_clean_jerk_{i}", lb=0) 
                    for i in range(n_bodybuilders)}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total weight lifted
    model.setObjective(gp.quicksum(x_snatch[i] + x_clean_jerk[i] for i in range(n_bodybuilders)), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Performance Target Constraints
    for i in range(n_bodybuilders):
        model.addConstr(x_snatch[i] + x_clean_jerk[i] <= performance_targets[i], name=f"performance_target_{i}")
    
    # Training Impact Constraints
    for i in range(n_bodybuilders):
        model.addConstr(snatch_impact[i] * x_snatch[i] + clean_jerk_impact[i] * x_clean_jerk[i] <= training_limits[i], 
                        name=f"training_impact_{i}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in range(n_bodybuilders):
            print(f"Bodybuilder {i+1}: Snatch = {x_snatch[i].x:.3f}, Clean & Jerk = {x_clean_jerk[i].x:.3f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
optimize_bodybuilder_performance()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 519.8717948717948
**Execution Time**: 1.21 seconds
**Reliability**: high
**Analysis**: Gurobipy found an optimal solution quickly with a precise optimal value. It is known for its efficiency and reliability in solving linear programming problems.

## 6. DOCplex Implementation

```python
# Complete DOCPLEX implementation

from docplex.mp.model import Model

def optimize_bodybuilder_performance():
    """Optimize the total weight lifted by bodybuilders in Snatch and Clean & Jerk events."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="body_builder_optimization")
    
    # Data from the database
    snatch_impact = [1.2, 1.3, 1.1]
    clean_jerk_impact = [1.5, 1.6, 1.4]
    performance_targets = [300.0, 320.0, 340.0]
    total_limits = [190.0, 210.0, 220.0]
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(snatch_impact) == len(clean_jerk_impact) == len(performance_targets) == len(total_limits), "Array length mismatch"
    safe_range = range(min(len(snatch_impact), len(clean_jerk_impact), len(performance_targets), len(total_limits)))  # Safe indexing
    
    # 2. VARIABLES
    x1 = {i: mdl.continuous_var(name=f"x1_{i}", lb=0) for i in safe_range}
    x2 = {i: mdl.continuous_var(name=f"x2_{i}", lb=0) for i in safe_range}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total weight lifted
    objective = mdl.sum(x1[i] + x2[i] for i in safe_range)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Performance Target Constraints
    for i in safe_range:
        mdl.add_constraint(x1[i] + x2[i] <= performance_targets[i], ctname=f"performance_target_{i}")
    
    # Training Impact Constraints
    for i in safe_range:
        mdl.add_constraint(snatch_impact[i] * x1[i] + clean_jerk_impact[i] * x2[i] <= total_limits[i], ctname=f"training_impact_{i}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in safe_range:
            x1_value = solution.get_value(x1[i])
            x2_value = solution.get_value(x2[i])
            if x1_value > 1e-6 or x2_value > 1e-6:
                print(f"x1[{i}] = {x1_value:.3f}, x2[{i}] = {x2_value:.3f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Run the optimization
optimize_bodybuilder_performance()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 519.8717948717949
**Execution Time**: 6.89 seconds
**Reliability**: high
**Analysis**: DOCplex also found an optimal solution, albeit with a slightly longer execution time. The optimal value is consistent with Gurobipy's result within a very small tolerance.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

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

def bodybuilder_optimization():
    """Optimization for maximizing total weight lifted by bodybuilders"""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # Data from the database
    snatch_impact = [1.2, 1.3, 1.1]
    clean_jerk_impact = [1.5, 1.6, 1.4]
    performance_targets = [300.0, 320.0, 340.0]
    total_limits = [190.0, 210.0, 220.0]
    
    # Validate array lengths
    n_bodybuilders = len(performance_targets)
    assert len(snatch_impact) == len(clean_jerk_impact) == len(total_limits) == n_bodybuilders, "Array length mismatch"
    
    # 3. SETS
    model.I = pyo.RangeSet(1, n_bodybuilders)  # 1-based indexing for bodybuilders
    
    # 4. PARAMETERS
    model.snatch_impact = pyo.Param(model.I, initialize={i+1: snatch_impact[i] for i in range(n_bodybuilders)})
    model.clean_jerk_impact = pyo.Param(model.I, initialize={i+1: clean_jerk_impact[i] for i in range(n_bodybuilders)})
    model.performance_target = pyo.Param(model.I, initialize={i+1: performance_targets[i] for i in range(n_bodybuilders)})
    model.total_limit = pyo.Param(model.I, initialize={i+1: total_limits[i] for i in range(n_bodybuilders)})
    
    # 5. VARIABLES
    model.x1 = pyo.Var(model.I, within=pyo.NonNegativeReals)  # Snatch weights
    model.x2 = pyo.Var(model.I, within=pyo.NonNegativeReals)  # Clean & Jerk weights
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.x1[i] + model.x2[i] for i in model.I)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    
    # Performance Target Constraints
    def performance_target_rule(model, i):
        return model.x1[i] + model.x2[i] <= model.performance_target[i]
    model.performance_target_constraint = pyo.Constraint(model.I, rule=performance_target_rule)
    
    # Training Impact Constraints
    def training_impact_rule(model, i):
        return model.snatch_impact[i] * model.x1[i] + model.clean_jerk_impact[i] * model.x2[i] <= model.total_limit[i]
    model.training_impact_constraint = pyo.Constraint(model.I, rule=training_impact_rule)
    
    # 8. SOLVING WITH GUROBI
    solver = SolverFactory('gurobi')
    
    # 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("\nVariable values:")
        for i in model.I:
            x1_val = pyo.value(model.x1[i])
            x2_val = pyo.value(model.x2[i])
            print(f"Bodybuilder {i}: Snatch = {x1_val:.3f}, Clean & Jerk = {x2_val:.3f}")
        
    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
bodybuilder_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 519.8717948717948
**Execution Time**: 6.11 seconds
**Reliability**: high
**Analysis**: Pyomo achieved an optimal solution with an execution time similar to DOCplex. The optimal value matches Gurobipy's result exactly, indicating consistency.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 519.87 | 1.21s | N/A | N/A |
| Docplex | OPTIMAL | 519.87 | 6.89s | N/A | N/A |
| Pyomo | OPTIMAL | 519.87 | 6.11s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 519.8717948717948
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy is recommended due to its faster execution time and precise results, making it suitable for time-sensitive applications.

### Business Interpretation
**Overall Strategy**: The optimal solution indicates the maximum total weight that can be lifted by the bodybuilders while respecting all constraints.
**Objective Value Meaning**: The optimal objective value represents the maximum combined weight lifted by the bodybuilders across both events, given the constraints.
**Resource Allocation Summary**: Allocate training resources to ensure bodybuilders can achieve the optimal weights in both events without exceeding performance targets or training impacts.
**Implementation Recommendations**: Focus on optimizing training programs to achieve the recommended weights in both events, ensuring compliance with all constraints.