# Complete Optimization Problem and Solution: roller_coaster

## 1. Problem Context and Goals

### Context  
The theme park company is focused on optimizing the allocation of maintenance resources across its roller coasters to minimize downtime while ensuring safety standards are met. The primary business decision involves determining the number of maintenance hours to allocate to each roller coaster. This decision is represented by the continuous variable of maintenance hours for each roller coaster. The operational parameters are aligned with the objective of minimizing total downtime, which is calculated as the sum of the product of downtime coefficients and maintenance hours for each roller coaster.

The business configuration includes a total of 22 maintenance hours available across all roller coasters, which serves as a constraint on the total maintenance hours that can be allocated. Business logic ensures that each roller coaster receives a minimum number of maintenance hours necessary for safety, and there is a limit on the maximum maintenance hours that can be allocated to each roller coaster. These constraints are expressed in natural language to describe linear relationships, ensuring that the problem formulation remains linear and avoids nonlinear scenarios.

### Goals  
The optimization goal is to minimize the total downtime of the roller coasters. This is achieved by optimizing the allocation of maintenance hours, with the metric to optimize being the total downtime, calculated as the sum of the product of downtime coefficients and maintenance hours for each roller coaster. Success is measured by the reduction in total downtime, aligning with the expected coefficient sources. The goal is described in natural language to maintain a clear focus on linear optimization.

## 2. Constraints    

The constraints for this optimization problem are designed to ensure that the allocation of maintenance hours is both efficient and safe:

- The total number of maintenance hours allocated across all roller coasters must not exceed the available 22 hours. This ensures that the maintenance resources are used within the limits of what is available.
- Each roller coaster must receive a minimum number of maintenance hours to meet safety requirements. This constraint ensures that safety standards are not compromised.
- The maintenance hours allocated to each roller coaster must not exceed a specified maximum. This prevents over-allocation of resources to any single roller coaster, ensuring a balanced distribution of maintenance efforts.

These constraints are expressed in business terms that naturally lead to linear mathematical forms, avoiding any nonlinear relationships.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 2 Database Schema
-- Objective: Schema adjustments include adding tables for minimum and maximum maintenance hours constraints, updating business configuration logic for scalar parameters and formulas, and ensuring all mappings are complete and adequate.

CREATE TABLE roller_coaster_maintenance (
  roller_coaster_id INTEGER,
  hours FLOAT
);

CREATE TABLE downtime_coefficients (
  roller_coaster_id INTEGER,
  coefficient FLOAT
);

CREATE TABLE maintenance_constraints (
  roller_coaster_id INTEGER,
  minimum_hours FLOAT,
  maximum_hours FLOAT
);
```

### Data Dictionary  
The data dictionary provides a comprehensive mapping of tables and columns to their business purposes and optimization roles:

- **Roller Coaster Maintenance Table**: This table stores the number of maintenance hours allocated to each roller coaster. The primary role of this table is to represent the decision variables in the optimization problem.
  - **Roller Coaster ID**: A unique identifier for each roller coaster, used to allocate maintenance hours.
  - **Hours**: The number of maintenance hours allocated, serving as the decision variable for maintenance allocation.

- **Downtime Coefficients Table**: This table represents the impact of maintenance hours on downtime for each roller coaster. It plays a crucial role in the objective function of the optimization problem.
  - **Roller Coaster ID**: A unique identifier for each roller coaster, used to associate downtime coefficients.
  - **Coefficient**: Represents the impact of maintenance hours on downtime, serving as a coefficient in the objective function.

- **Maintenance Constraints Table**: This table stores the minimum and maximum maintenance hours constraints for each roller coaster, ensuring safety and resource allocation limits.
  - **Roller Coaster ID**: A unique identifier for each roller coaster, used to apply constraints.
  - **Minimum Hours**: The minimum maintenance hours required for safety, serving as a lower bound constraint.
  - **Maximum Hours**: The maximum maintenance hours that can be allocated, serving as an upper bound constraint.

### Current Stored Values  
```sql
-- Iteration 2 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on typical maintenance schedules and downtime impacts for theme park roller coasters, ensuring a balance between safety requirements and operational efficiency.

-- Realistic data for roller_coaster_maintenance
INSERT INTO roller_coaster_maintenance (roller_coaster_id, hours) VALUES (1, 6.0);
INSERT INTO roller_coaster_maintenance (roller_coaster_id, hours) VALUES (2, 9.0);
INSERT INTO roller_coaster_maintenance (roller_coaster_id, hours) VALUES (3, 7.0);

-- Realistic data for downtime_coefficients
INSERT INTO downtime_coefficients (roller_coaster_id, coefficient) VALUES (1, 0.4);
INSERT INTO downtime_coefficients (roller_coaster_id, coefficient) VALUES (2, 0.35);
INSERT INTO downtime_coefficients (roller_coaster_id, coefficient) VALUES (3, 0.25);

-- Realistic data for maintenance_constraints
INSERT INTO maintenance_constraints (roller_coaster_id, minimum_hours, maximum_hours) VALUES (1, 3.0, 10.0);
INSERT INTO maintenance_constraints (roller_coaster_id, minimum_hours, maximum_hours) VALUES (2, 4.0, 12.0);
INSERT INTO maintenance_constraints (roller_coaster_id, minimum_hours, maximum_hours) VALUES (3, 2.5, 9.0);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- Let \( x_1 \), \( x_2 \), and \( x_3 \) represent the maintenance hours allocated to roller coasters 1, 2, and 3, respectively. These are continuous decision variables.

#### Objective Function
- Minimize the total downtime, which is calculated as:
  \[
  \text{Minimize } 0.4x_1 + 0.35x_2 + 0.25x_3
  \]
  - Coefficients are sourced from the `downtime_coefficients` table.

#### Constraints
1. Total maintenance hours constraint:
   \[
   x_1 + x_2 + x_3 \leq 22
   \]
   - This ensures the total allocated maintenance hours do not exceed the available 22 hours.

2. Minimum maintenance hours for safety:
   \[
   x_1 \geq 3.0
   \]
   \[
   x_2 \geq 4.0
   \]
   \[
   x_3 \geq 2.5
   \]
   - These constraints ensure each roller coaster receives the minimum required maintenance hours for safety, sourced from the `maintenance_constraints` table.

3. Maximum maintenance hours constraint:
   \[
   x_1 \leq 10.0
   \]
   \[
   x_2 \leq 12.0
   \]
   \[
   x_3 \leq 9.0
   \]
   - These constraints prevent over-allocation of maintenance hours to any single roller coaster, sourced from the `maintenance_constraints` table.

Data Source Verification:
- Decision variables \( x_1, x_2, x_3 \) are represented by `roller_coaster_maintenance.hours`.
- Objective function coefficients are from `downtime_coefficients.coefficient`.
- Minimum and maximum constraints are from `maintenance_constraints.minimum_hours` and `maintenance_constraints.maximum_hours`, respectively.

## 5. Gurobipy Implementation

```python
# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_roller_coaster_maintenance():
    """Optimize maintenance hours allocation for roller coasters to minimize downtime."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("roller_coaster_maintenance")
    
    # Data from the database
    downtime_coefficients = [0.4, 0.35, 0.25]
    minimum_hours = [3.0, 4.0, 2.5]
    maximum_hours = [10.0, 12.0, 9.0]
    total_available_hours = 22
    
    # Validate array lengths
    n_coasters = len(downtime_coefficients)
    assert len(minimum_hours) == len(maximum_hours) == n_coasters, "Array length mismatch"
    
    # 2. VARIABLES
    # Create a dictionary of variables for maintenance hours
    maintenance_hours = {i: model.addVar(vtype=GRB.CONTINUOUS, name=f"x_{i+1}", lb=0) 
                         for i in range(n_coasters)}
    
    # 3. OBJECTIVE FUNCTION
    # Minimize total downtime
    model.setObjective(gp.quicksum(downtime_coefficients[i] * maintenance_hours[i] for i in range(n_coasters)), GRB.MINIMIZE)
    
    # 4. CONSTRAINTS
    # Total maintenance hours constraint
    model.addConstr(gp.quicksum(maintenance_hours[i] for i in range(n_coasters)) <= total_available_hours, name="total_hours")
    
    # Minimum and maximum maintenance hours constraints
    for i in range(n_coasters):
        model.addConstr(maintenance_hours[i] >= minimum_hours[i], name=f"min_hours_{i+1}")
        model.addConstr(maintenance_hours[i] <= maximum_hours[i], name=f"max_hours_{i+1}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in range(n_coasters):
            print(f"x[{i+1}] = {maintenance_hours[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_roller_coaster_maintenance()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 3.225
**Execution Time**: 0.19 seconds
**Reliability**: high
**Analysis**: Gurobipy found an optimal solution quickly with the lowest execution time, indicating efficient performance.

## 6. DOCplex Implementation

```python
# Complete DOCPLEX implementation

from docplex.mp.model import Model

def optimize_roller_coaster_maintenance():
    """Optimize maintenance hours allocation for roller coasters to minimize downtime."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="roller_coaster_maintenance")
    
    # Data from the database
    downtime_coefficients = [0.4, 0.35, 0.25]
    minimum_hours = [3.0, 4.0, 2.5]
    maximum_hours = [10.0, 12.0, 9.0]
    total_available_hours = 22
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(downtime_coefficients) == len(minimum_hours) == len(maximum_hours), "Array length mismatch"
    safe_range = range(len(downtime_coefficients))  # Safe indexing
    
    # 2. VARIABLES
    # Variable dictionaries (most common)
    maintenance_hours = {i: mdl.continuous_var(name=f"x_{i}", lb=0) for i in safe_range}
    
    # 3. OBJECTIVE FUNCTION
    # Minimize total downtime
    objective = mdl.sum(downtime_coefficients[i] * maintenance_hours[i] for i in safe_range)
    mdl.minimize(objective)
    
    # 4. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Total maintenance hours constraint
    total_usage = mdl.sum(maintenance_hours[i] for i in safe_range)
    mdl.add_constraint(total_usage <= total_available_hours, ctname="total_hours_limit")
    
    # Minimum and maximum maintenance hours constraints
    for i in safe_range:
        mdl.add_constraint(maintenance_hours[i] >= minimum_hours[i], ctname=f"min_hours_{i}")
        mdl.add_constraint(maintenance_hours[i] <= maximum_hours[i], ctname=f"max_hours_{i}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value:.3f}")
        for i in safe_range:
            value = solution.get_value(maintenance_hours[i])
            print(f"x[{i}] = {value:.3f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Run the optimization
optimize_roller_coaster_maintenance()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 3.225
**Execution Time**: 4.46 seconds
**Reliability**: high
**Analysis**: DOCplex also found an optimal solution, but with a significantly longer execution time compared to Gurobipy.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

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

def roller_coaster_optimization():
    """Optimize maintenance hours allocation for roller coasters to minimize downtime."""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # Data from the problem statement
    roller_coaster_ids = [1, 2, 3]
    downtime_coefficients = {1: 0.4, 2: 0.35, 3: 0.25}
    minimum_hours = {1: 3.0, 2: 4.0, 3: 2.5}
    maximum_hours = {1: 10.0, 2: 12.0, 3: 9.0}
    total_available_hours = 22
    
    # CRITICAL: Validate array lengths before indexing
    assert len(roller_coaster_ids) == len(downtime_coefficients) == len(minimum_hours) == len(maximum_hours), "Array length mismatch"
    
    # 3. SETS
    model.I = pyo.Set(initialize=roller_coaster_ids)
    
    # 4. PARAMETERS
    model.downtime_coefficient = pyo.Param(model.I, initialize=downtime_coefficients)
    model.minimum_hours = pyo.Param(model.I, initialize=minimum_hours)
    model.maximum_hours = pyo.Param(model.I, initialize=maximum_hours)
    
    # 5. VARIABLES
    model.x = pyo.Var(model.I, within=pyo.NonNegativeReals)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.downtime_coefficient[i] * model.x[i] for i in model.I)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.minimize)
    
    # 7. CONSTRAINTS
    
    # Total maintenance hours constraint
    def total_hours_rule(model):
        return sum(model.x[i] for i in model.I) <= total_available_hours
    model.total_hours_constraint = pyo.Constraint(rule=total_hours_rule)
    
    # Minimum maintenance hours constraint
    def min_hours_rule(model, i):
        return model.x[i] >= model.minimum_hours[i]
    model.min_hours_constraint = pyo.Constraint(model.I, rule=min_hours_rule)
    
    # Maximum maintenance hours constraint
    def max_hours_rule(model, i):
        return model.x[i] <= model.maximum_hours[i]
    model.max_hours_constraint = pyo.Constraint(model.I, rule=max_hours_rule)
    
    # 8. SOLVING WITH GUROBI
    solver = SolverFactory('gurobi')
    
    # 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):.3f}")
        
        # 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:.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

# Execute the optimization
roller_coaster_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 3.225
**Execution Time**: 1.45 seconds
**Reliability**: high
**Analysis**: Pyomo achieved the optimal solution with a moderate execution time, performing well overall.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 3.23 | 0.19s | N/A | N/A |
| Docplex | OPTIMAL | 3.23 | 4.46s | N/A | N/A |
| Pyomo | OPTIMAL | 3.23 | 1.45s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 3.225
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy is recommended due to its high reliability and the fastest execution time, making it suitable for real-time decision-making.

### Business Interpretation
**Overall Strategy**: The optimal maintenance schedule minimizes downtime effectively, ensuring safety and operational efficiency.
**Objective Value Meaning**: The optimal objective value of 3.225 represents the minimized total downtime across all roller coasters, enhancing operational efficiency.
**Resource Allocation Summary**: Allocate maintenance hours efficiently to meet safety requirements while minimizing downtime: 3.0 hours for roller coaster 1, 4.0 hours for roller coaster 2, and 2.5 hours for roller coaster 3.
**Implementation Recommendations**: Implement the maintenance schedule as per the recommended allocation, monitor downtime, and adjust as necessary to maintain operational efficiency.