# Complete Optimization Problem and Solution: Climbing

## 1. Problem Context and Goals

### Context  
In the context of a climbing competition, the organizer aims to strategically assign climbers to various mountains to maximize the overall points scored. Each climber can be assigned to only one mountain, and every mountain must have at least one climber assigned to it. The decision-making process involves determining whether a climber is assigned to a specific mountain, represented by a binary decision variable. The objective is to maximize the total points, which is calculated by summing the points scored by each climber assigned to a mountain. This linear optimization problem is driven by the need to efficiently allocate resources (climbers) to meet the competition's requirements while maximizing the scoring potential. The operational parameters include the points each climber can score, which serve as coefficients in the objective function. The business configuration involves ensuring that each climber is assigned to one mountain and that each mountain is climbed by at least one climber, aligning with the linear constraints of the problem.

### Goals  
The primary goal of this optimization problem is to maximize the total points scored in the climbing competition. This is achieved by optimizing the assignment of climbers to mountains, ensuring that the total points, calculated as the sum of the points scored by each climber assigned to a mountain, is maximized. Success is measured by the total points achieved, which directly correlates with the effective assignment of climbers based on their scoring potential. The focus is on a linear optimization goal, where the objective is to maximize the sum of the points associated with each climber's assignment.

## 2. Constraints    

The constraints for this optimization problem are designed to ensure fair and efficient allocation of climbers to mountains:

- Each climber must be assigned to exactly one mountain. This constraint ensures that no climber is left unassigned and that their scoring potential is fully utilized.
- Each mountain must have at least one climber assigned to it. This ensures that all mountains are part of the competition and contribute to the overall scoring.

These constraints are expressed in business terms that naturally lead to linear mathematical forms, ensuring that the problem remains within the realm of linear optimization.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 1 Database Schema
-- Objective: Schema changes include the creation of a decision_variables table to address missing binary variable mapping, and updates to existing tables to ensure alignment with optimization requirements.

CREATE TABLE climber (
  Climber_ID INTEGER,
  Points INTEGER
);

CREATE TABLE mountain (
  Mountain_ID INTEGER
);

CREATE TABLE decision_variables (
  Climber_ID INTEGER,
  Mountain_ID INTEGER,
  assignment BOOLEAN
);
```

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

- **Climber Table**: This table stores information about each climber, including their unique identifier and the points they can score. The points serve as coefficients in the objective function, representing the scoring potential of each climber.

- **Mountain Table**: This table contains information about each mountain, identified by a unique identifier. The mountains are the resources to which climbers are assigned, ensuring that each mountain is climbed by at least one climber.

- **Decision Variables Table**: This table captures the binary decision variables that indicate whether a climber is assigned to a specific mountain. It links climbers to mountains and plays a crucial role in the optimization process by determining the assignment of climbers.

### Current Stored Values  
```sql
-- Iteration 1 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on typical climbing competition scenarios, ensuring a diverse range of points and assignments to create a meaningful optimization problem.

-- Realistic data for climber
INSERT INTO climber (Climber_ID, Points) VALUES (1, 15);
INSERT INTO climber (Climber_ID, Points) VALUES (2, 25);
INSERT INTO climber (Climber_ID, Points) VALUES (3, 10);

-- Realistic data for mountain
INSERT INTO mountain (Mountain_ID) VALUES (1);
INSERT INTO mountain (Mountain_ID) VALUES (2);
INSERT INTO mountain (Mountain_ID) VALUES (3);

-- Realistic data for decision_variables
INSERT INTO decision_variables (Climber_ID, Mountain_ID, assignment) VALUES (1, 1, True);
INSERT INTO decision_variables (Climber_ID, Mountain_ID, assignment) VALUES (2, 2, True);
INSERT INTO decision_variables (Climber_ID, Mountain_ID, assignment) VALUES (3, 3, True);
```

## 4. Mathematical Optimization Formulation

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

#### Objective Function
Maximize the total points scored by all climbers assigned to mountains:
\[ \text{Maximize } Z = 15x_{11} + 15x_{12} + 15x_{13} + 25x_{21} + 25x_{22} + 25x_{23} + 10x_{31} + 10x_{32} + 10x_{33} \]

#### Constraints
1. Each climber must be assigned to exactly one mountain:
   \[
   \begin{align*}
   x_{11} + x_{12} + x_{13} &= 1 \quad \text{(Climber 1)} \\
   x_{21} + x_{22} + x_{23} &= 1 \quad \text{(Climber 2)} \\
   x_{31} + x_{32} + x_{33} &= 1 \quad \text{(Climber 3)}
   \end{align*}
   \]

2. Each mountain must have at least one climber assigned to it:
   \[
   \begin{align*}
   x_{11} + x_{21} + x_{31} &\geq 1 \quad \text{(Mountain 1)} \\
   x_{12} + x_{22} + x_{32} &\geq 1 \quad \text{(Mountain 2)} \\
   x_{13} + x_{23} + x_{33} &\geq 1 \quad \text{(Mountain 3)}
   \end{align*}
   \]

3. Binary constraints for decision variables:
   \[
   x_{ij} \in \{0, 1\} \quad \forall i \in \{1, 2, 3\}, \forall j \in \{1, 2, 3\}
   \]

Data Source Verification:
- Coefficients in the objective function (15, 25, 10) are derived from the `climber.Points` column.
- Constraints are based on the requirement that each climber is assigned to one mountain and each mountain has at least one climber, as described in the problem context.

## 5. Gurobipy Implementation

```python
# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def climbing_optimization():
    """Optimize climber assignments to mountains to maximize points."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("climbing_competition")
    
    # Data: Points each climber can score
    climber_points = [15, 25, 10]
    n_climbers = len(climber_points)
    
    # Data: Number of mountains
    n_mountains = 3
    
    # CRITICAL: Validate array lengths before loops
    assert n_climbers == 3, "Unexpected number of climbers"
    assert n_mountains == 3, "Unexpected number of mountains"
    
    # 2. VARIABLES
    # Decision variables: x[i, j] = 1 if climber i is assigned to mountain j
    x = model.addVars(n_climbers, n_mountains, vtype=GRB.BINARY, name="x")
    
    # 3. OBJECTIVE FUNCTION
    # Maximize total points scored by all climbers assigned to mountains
    model.setObjective(gp.quicksum(climber_points[i] * x[i, j] 
                                   for i in range(n_climbers) 
                                   for j in range(n_mountains)), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Each climber must be assigned to exactly one mountain
    model.addConstrs((gp.quicksum(x[i, j] for j in range(n_mountains)) == 1 
                      for i in range(n_climbers)), name="climber_assignment")
    
    # Each mountain must have at least one climber assigned to it
    model.addConstrs((gp.quicksum(x[i, j] for i in range(n_climbers)) >= 1 
                      for j in range(n_mountains)), name="mountain_assignment")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in range(n_climbers):
            for j in range(n_mountains):
                if x[i, j].x > 1e-6:
                    print(f"Climber {i+1} assigned to Mountain {j+1}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
climbing_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 50.0
**Execution Time**: 0.31 seconds
**Reliability**: high
**Analysis**: Gurobipy found an optimal solution quickly with the expected optimal value.

## 6. DOCplex Implementation

```python
# Complete DOCPLEX implementation

from docplex.mp.model import Model

def climbing_optimization():
    """Climbing competition optimization using DOCPLEX"""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="climbing_competition")
    
    # Data: Points each climber can score
    climber_points = [15, 25, 10]
    n_climbers = len(climber_points)
    
    # Data: Number of mountains
    n_mountains = 3
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert n_climbers == 3, "Unexpected number of climbers"
    assert n_mountains == 3, "Unexpected number of mountains"
    
    # Safe range for indexing
    climber_range = range(n_climbers)
    mountain_range = range(n_mountains)
    
    # 2. VARIABLES
    # Binary decision variables: x[i][j] = 1 if climber i is assigned to mountain j
    x = {(i, j): mdl.binary_var(name=f"x_{i}_{j}") for i in climber_range for j in mountain_range}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize total points scored by all climbers assigned to mountains
    objective = mdl.sum(climber_points[i] * x[i, j] for i in climber_range for j in mountain_range)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # Each climber must be assigned to exactly one mountain
    for i in climber_range:
        mdl.add_constraint(mdl.sum(x[i, j] for j in mountain_range) == 1, ctname=f"climber_{i}_assignment")
    
    # Each mountain must have at least one climber assigned to it
    for j in mountain_range:
        mdl.add_constraint(mdl.sum(x[i, j] for i in climber_range) >= 1, ctname=f"mountain_{j}_coverage")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in climber_range:
            for j in mountain_range:
                if solution.get_value(x[i, j]) > 0.5:  # Check if the climber is assigned to the mountain
                    print(f"Climber {i+1} is assigned to Mountain {j+1}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Run the optimization
climbing_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 50.0
**Execution Time**: 3.76 seconds
**Reliability**: high
**Analysis**: DOCplex also found the optimal solution, albeit with a longer execution time.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

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

def climbing_optimization():
    """Climbing competition optimization using Pyomo"""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    climbers = [1, 2, 3]
    mountains = [1, 2, 3]
    points = {1: 15, 2: 25, 3: 10}
    
    # CRITICAL: Validate array lengths before indexing
    assert len(climbers) == len(points), "Array length mismatch for climbers and points"
    
    # 3. SETS
    model.C = pyo.Set(initialize=climbers)
    model.M = pyo.Set(initialize=mountains)
    
    # 4. PARAMETERS
    model.points = pyo.Param(model.C, initialize=points)
    
    # 5. VARIABLES
    model.x = pyo.Var(model.C, model.M, within=pyo.Binary)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.points[i] * model.x[i, j] for i in model.C for j in model.M)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    
    # Each climber must be assigned to exactly one mountain
    def climber_assignment_rule(model, i):
        return sum(model.x[i, j] for j in model.M) == 1
    model.climber_assignment = pyo.Constraint(model.C, rule=climber_assignment_rule)
    
    # Each mountain must have at least one climber assigned to it
    def mountain_assignment_rule(model, j):
        return sum(model.x[i, j] for i in model.C) >= 1
    model.mountain_assignment = pyo.Constraint(model.M, rule=mountain_assignment_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
    # 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("\nAssignment of climbers to mountains:")
        for i in model.C:
            for j in model.M:
                if pyo.value(model.x[i, j]) > 0.5:  # Only print assignments
                    print(f"Climber {i} assigned to Mountain {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
climbing_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 50.0
**Execution Time**: 2.90 seconds
**Reliability**: high
**Analysis**: Pyomo achieved the optimal solution with a reasonable execution time.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 50.00 | 0.31s | N/A | N/A |
| Docplex | OPTIMAL | 50.00 | 3.76s | N/A | N/A |
| Pyomo | OPTIMAL | 50.00 | 2.90s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 50.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.

### Optimal Decision Variables
- **x_11** = 0.000
  - *Business Meaning*: Resource allocation for x_11
- **x_12** = 1.000
  - *Business Meaning*: Resource allocation for x_12
- **x_13** = 0.000
  - *Business Meaning*: Resource allocation for x_13
- **x_21** = 1.000
  - *Business Meaning*: Resource allocation for x_21
- **x_22** = 0.000
  - *Business Meaning*: Resource allocation for x_22
- **x_23** = 0.000
  - *Business Meaning*: Resource allocation for x_23
- **x_31** = 0.000
  - *Business Meaning*: Resource allocation for x_31
- **x_32** = 0.000
  - *Business Meaning*: Resource allocation for x_32
- **x_33** = 1.000
  - *Business Meaning*: Resource allocation for x_33

### Business Interpretation
**Overall Strategy**: The optimal assignment of climbers to mountains maximizes the total points scored, ensuring each mountain has at least one climber.
**Objective Value Meaning**: The optimal objective value of 50 points represents the maximum achievable score by assigning climbers to mountains under the given constraints.
**Resource Allocation Summary**: Climber 1 should be assigned to Mountain 2, Climber 2 to Mountain 1, and Climber 3 to Mountain 3.
**Implementation Recommendations**: Implement the assignment strategy as per the recommended decision variables to achieve the optimal score.