# Complete Optimization Problem and Solution: ship_mission

## 1. Problem Context and Goals

### Context  
A shipping company operates a fleet of ships and needs to assign them to various missions efficiently. Each mission has specific requirements, including minimum tonnage, minimum speed, and a required nationality for the ship. The company aims to minimize the total operational cost of the fleet, which is influenced by the tonnage and speed of the ships assigned to missions. The cost is calculated as a weighted sum of the ship's tonnage and speed, where the weights are predefined cost coefficients. The company must ensure that each mission is assigned to exactly one ship and that each ship is assigned to at most one mission. Additionally, the assigned ship must meet the mission's tonnage, speed, and nationality requirements. The decision to assign a ship to a mission is represented by a binary indicator, which is the core decision variable in this problem.

### Goals  
The primary goal of this optimization problem is to minimize the total operational cost of the fleet. This cost is determined by the sum of the costs associated with the tonnage and speed of the ships assigned to missions. The cost coefficients for tonnage and speed are predefined and reflect the company's operational priorities. Success is measured by achieving the lowest possible total cost while ensuring that all mission requirements are met and that the assignment constraints are satisfied.

## 2. Constraints  

1. **Mission Assignment Constraint**: Each mission must be assigned to exactly one ship. This ensures that all missions are fulfilled without overlap or omission.  
2. **Ship Assignment Constraint**: Each ship can be assigned to at most one mission. This prevents overcommitment of ships and ensures efficient resource utilization.  
3. **Tonnage Requirement Constraint**: The tonnage of the ship assigned to a mission must meet or exceed the minimum tonnage required by the mission. This ensures that the ship has the necessary capacity to handle the mission.  
4. **Speed Requirement Constraint**: The speed of the ship assigned to a mission must meet or exceed the minimum speed required by the mission. This ensures that the ship can complete the mission within the required timeframe.  
5. **Nationality Requirement Constraint**: The nationality of the ship assigned to a mission must match the required nationality specified by the mission. This ensures compliance with regulatory or contractual obligations.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 2 Database Schema
-- Objective: Added a new table for ship-to-mission assignments to address the missing decision variable x_ij. Updated the data dictionary and business configuration logic to reflect these changes.

CREATE TABLE missions (
  mission_id INTEGER,
  min_tonnage INTEGER,
  min_speed INTEGER,
  required_nationality STRING
);

CREATE TABLE ships (
  ship_id INTEGER,
  tonnage INTEGER,
  speed_knots INTEGER,
  nationality STRING
);

CREATE TABLE ship_mission_assignments (
  ship_id INTEGER,
  mission_id INTEGER,
  is_assigned BOOLEAN
);
```

### Data Dictionary  
- **Missions**: Contains details of each mission, including the minimum tonnage, minimum speed, and required nationality.  
  - `mission_id`: Unique identifier for each mission.  
  - `min_tonnage`: Minimum tonnage required for the mission.  
  - `min_speed`: Minimum speed required for the mission.  
  - `required_nationality`: Required nationality for the mission.  

- **Ships**: Contains details of each ship, including tonnage, speed, and nationality.  
  - `ship_id`: Unique identifier for each ship.  
  - `tonnage`: Tonnage of the ship.  
  - `speed_knots`: Speed of the ship in knots.  
  - `nationality`: Nationality of the ship.  

- **Ship Mission Assignments**: Tracks the assignment of ships to missions.  
  - `ship_id`: Unique identifier for each ship.  
  - `mission_id`: Unique identifier for each mission.  
  - `is_assigned`: Indicates whether the ship is assigned to the mission.  

### Current Stored Values  
```sql
-- Iteration 2 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on realistic shipping industry standards, ensuring that ships meet mission requirements and that the optimization problem remains solvable. Parameters were set to reflect typical operational costs and constraints.

-- Realistic data for missions
INSERT INTO missions (mission_id, min_tonnage, min_speed, required_nationality) VALUES (1, 5000, 20, 'USA');
INSERT INTO missions (mission_id, min_tonnage, min_speed, required_nationality) VALUES (2, 6000, 25, 'UK');
INSERT INTO missions (mission_id, min_tonnage, min_speed, required_nationality) VALUES (3, 7000, 30, 'Canada');

-- Realistic data for ships
INSERT INTO ships (ship_id, tonnage, speed_knots, nationality) VALUES (1, 5500, 22, 'USA');
INSERT INTO ships (ship_id, tonnage, speed_knots, nationality) VALUES (2, 6500, 27, 'UK');
INSERT INTO ships (ship_id, tonnage, speed_knots, nationality) VALUES (3, 7500, 32, 'Canada');

-- Realistic data for ship_mission_assignments
INSERT INTO ship_mission_assignments (ship_id, mission_id, is_assigned) VALUES (1, 1, True);
INSERT INTO ship_mission_assignments (ship_id, mission_id, is_assigned) VALUES (2, 2, True);
INSERT INTO ship_mission_assignments (ship_id, mission_id, is_assigned) VALUES (3, 3, True);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- \( x_{ij} \): Binary decision variable indicating whether ship \( i \) is assigned to mission \( j \).  
  - \( x_{ij} = 1 \) if ship \( i \) is assigned to mission \( j \), otherwise \( x_{ij} = 0 \).

#### Objective Function
Minimize the total operational cost:  
\[
\text{Minimize} \quad \sum_{i} \sum_{j} \left( c_{\text{tonnage}} \cdot \text{tonnage}_i + c_{\text{speed}} \cdot \text{speed}_i \right) \cdot x_{ij}
\]  
Where:  
- \( c_{\text{tonnage}} \): Cost coefficient for tonnage (assume \( c_{\text{tonnage}} = 1 \) for simplicity).  
- \( c_{\text{speed}} \): Cost coefficient for speed (assume \( c_{\text{speed}} = 1 \) for simplicity).  
- \( \text{tonnage}_i \): Tonnage of ship \( i \) (from `ships.tonnage`).  
- \( \text{speed}_i \): Speed of ship \( i \) (from `ships.speed_knots`).

#### Constraints
1. **Mission Assignment Constraint**: Each mission must be assigned to exactly one ship.  
\[
\sum_{i} x_{ij} = 1 \quad \forall j
\]

2. **Ship Assignment Constraint**: Each ship can be assigned to at most one mission.  
\[
\sum_{j} x_{ij} \leq 1 \quad \forall i
\]

3. **Tonnage Requirement Constraint**: The tonnage of the ship assigned to a mission must meet or exceed the minimum tonnage required by the mission.  
\[
\text{tonnage}_i \cdot x_{ij} \geq \text{min_tonnage}_j \cdot x_{ij} \quad \forall i, j
\]

4. **Speed Requirement Constraint**: The speed of the ship assigned to a mission must meet or exceed the minimum speed required by the mission.  
\[
\text{speed}_i \cdot x_{ij} \geq \text{min_speed}_j \cdot x_{ij} \quad \forall i, j
\]

5. **Nationality Requirement Constraint**: The nationality of the ship assigned to a mission must match the required nationality specified by the mission.  
\[
\text{nationality}_i \cdot x_{ij} = \text{required_nationality}_j \cdot x_{ij} \quad \forall i, j
\]

#### Data Source Verification
- \( \text{tonnage}_i \): `ships.tonnage`  
- \( \text{speed}_i \): `ships.speed_knots`  
- \( \text{min_tonnage}_j \): `missions.min_tonnage`  
- \( \text{min_speed}_j \): `missions.min_speed`  
- \( \text{nationality}_i \): `ships.nationality`  
- \( \text{required_nationality}_j \): `missions.required_nationality`  

This formulation provides a complete, immediately solvable LINEAR mathematical model with all numerical coefficients derived from the provided data.

## 5. Gurobipy Implementation

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

import gurobipy as gp
from gurobipy import GRB

def ship_mission_optimization():
    # 1. MODEL & DATA SETUP
    model = gp.Model("ship_mission_optimization")
    
    # Data from the database
    missions = [
        {'mission_id': 1, 'min_tonnage': 5000, 'min_speed': 20, 'required_nationality': 'USA'},
        {'mission_id': 2, 'min_tonnage': 6000, 'min_speed': 25, 'required_nationality': 'UK'},
        {'mission_id': 3, 'min_tonnage': 7000, 'min_speed': 30, 'required_nationality': 'Canada'}
    ]
    
    ships = [
        {'ship_id': 1, 'tonnage': 5500, 'speed_knots': 22, 'nationality': 'USA'},
        {'ship_id': 2, 'tonnage': 6500, 'speed_knots': 27, 'nationality': 'UK'},
        {'ship_id': 3, 'tonnage': 7500, 'speed_knots': 32, 'nationality': 'Canada'}
    ]
    
    # CRITICAL: Validate array lengths before loops
    assert len(missions) > 0 and len(ships) > 0, "Empty missions or ships data"
    
    # 2. VARIABLES
    # Decision variable x_ij: whether ship i is assigned to mission j
    x = model.addVars(len(ships), len(missions), vtype=GRB.BINARY, name="x")
    
    # 3. OBJECTIVE FUNCTION
    # Minimize the total operational cost
    c_tonnage = 1
    c_speed = 1
    model.setObjective(
        gp.quicksum(
            (c_tonnage * ships[i]['tonnage'] + c_speed * ships[i]['speed_knots']) * x[i, j]
            for i in range(len(ships))
            for j in range(len(missions))
        ),
        GRB.MINIMIZE
    )
    
    # 4. CONSTRAINTS
    
    # Mission Assignment Constraint: Each mission must be assigned to exactly one ship
    for j in range(len(missions)):
        model.addConstr(
            gp.quicksum(x[i, j] for i in range(len(ships))) == 1,
            name=f"mission_assignment_{j}"
        )
    
    # Ship Assignment Constraint: Each ship can be assigned to at most one mission
    for i in range(len(ships)):
        model.addConstr(
            gp.quicksum(x[i, j] for j in range(len(missions))) <= 1,
            name=f"ship_assignment_{i}"
        )
    
    # Tonnage Requirement Constraint: The tonnage of the ship must meet or exceed the mission's minimum tonnage
    for i in range(len(ships)):
        for j in range(len(missions)):
            model.addConstr(
                ships[i]['tonnage'] * x[i, j] >= missions[j]['min_tonnage'] * x[i, j],
                name=f"tonnage_requirement_{i}_{j}"
            )
    
    # Speed Requirement Constraint: The speed of the ship must meet or exceed the mission's minimum speed
    for i in range(len(ships)):
        for j in range(len(missions)):
            model.addConstr(
                ships[i]['speed_knots'] * x[i, j] >= missions[j]['min_speed'] * x[i, j],
                name=f"speed_requirement_{i}_{j}"
            )
    
    # Nationality Requirement Constraint: The nationality of the ship must match the mission's required nationality
    for i in range(len(ships)):
        for j in range(len(missions)):
            if ships[i]['nationality'] == missions[j]['required_nationality']:
                model.addConstr(
                    x[i, j] == x[i, j],
                    name=f"nationality_requirement_{i}_{j}"
                )
            else:
                model.addConstr(
                    x[i, j] == 0,
                    name=f"nationality_requirement_{i}_{j}"
                )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in range(len(ships)):
            for j in range(len(missions)):
                if x[i, j].x > 0.5:
                    print(f"Ship {ships[i]['ship_id']} assigned to Mission {missions[j]['mission_id']}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Execute the optimization
ship_mission_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 19581.0
**Execution Time**: 0.16 seconds
**Reliability**: high
**Retry Attempt**: 1
**Analysis**: Gurobipy successfully found an optimal solution with a low execution time, indicating high efficiency and reliability.

## 6. DOCplex Implementation

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

from docplex.mp.model import Model

def ship_mission_optimization():
    # 1. MODEL & DATA SETUP
    mdl = Model(name="ship_mission_optimization")
    
    # Data from the database
    missions = [
        {'mission_id': 1, 'min_tonnage': 5000, 'min_speed': 20, 'required_nationality': 'USA'},
        {'mission_id': 2, 'min_tonnage': 6000, 'min_speed': 25, 'required_nationality': 'UK'},
        {'mission_id': 3, 'min_tonnage': 7000, 'min_speed': 30, 'required_nationality': 'Canada'}
    ]
    
    ships = [
        {'ship_id': 1, 'tonnage': 5500, 'speed_knots': 22, 'nationality': 'USA'},
        {'ship_id': 2, 'tonnage': 6500, 'speed_knots': 27, 'nationality': 'UK'},
        {'ship_id': 3, 'tonnage': 7500, 'speed_knots': 32, 'nationality': 'Canada'}
    ]
    
    # Cost coefficients
    c_tonnage = 1
    c_speed = 1
    
    # 2. VARIABLES
    x = {(i['ship_id'], j['mission_id']): mdl.binary_var(name=f"x_{i['ship_id']}_{j['mission_id']}") 
         for i in ships for j in missions}
    
    # 3. OBJECTIVE FUNCTION
    objective = mdl.sum((c_tonnage * i['tonnage'] + c_speed * i['speed_knots']) * x[(i['ship_id'], j['mission_id'])] 
                        for i in ships for j in missions)
    mdl.minimize(objective)
    
    # 4. CONSTRAINTS
    
    # Mission Assignment Constraint: Each mission must be assigned to exactly one ship
    for j in missions:
        mdl.add_constraint(mdl.sum(x[(i['ship_id'], j['mission_id'])] for i in ships) == 1, 
                           ctname=f"mission_assignment_{j['mission_id']}")
    
    # Ship Assignment Constraint: Each ship can be assigned to at most one mission
    for i in ships:
        mdl.add_constraint(mdl.sum(x[(i['ship_id'], j['mission_id'])] for j in missions) <= 1, 
                           ctname=f"ship_assignment_{i['ship_id']}")
    
    # Tonnage Requirement Constraint: The tonnage of the ship must meet or exceed the mission's minimum tonnage
    for i in ships:
        for j in missions:
            mdl.add_constraint(i['tonnage'] * x[(i['ship_id'], j['mission_id'])] >= j['min_tonnage'] * x[(i['ship_id'], j['mission_id'])], 
                               ctname=f"tonnage_requirement_{i['ship_id']}_{j['mission_id']}")
    
    # Speed Requirement Constraint: The speed of the ship must meet or exceed the mission's minimum speed
    for i in ships:
        for j in missions:
            mdl.add_constraint(i['speed_knots'] * x[(i['ship_id'], j['mission_id'])] >= j['min_speed'] * x[(i['ship_id'], j['mission_id'])], 
                               ctname=f"speed_requirement_{i['ship_id']}_{j['mission_id']}")
    
    # Nationality Requirement Constraint: The nationality of the ship must match the mission's required nationality
    for i in ships:
        for j in missions:
            mdl.add_constraint((i['nationality'] == j['required_nationality']) * x[(i['ship_id'], j['mission_id'])] == x[(i['ship_id'], j['mission_id'])], 
                               ctname=f"nationality_requirement_{i['ship_id']}_{j['mission_id']}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in ships:
            for j in missions:
                if solution.get_value(x[(i['ship_id'], j['mission_id'])]) > 0.5:
                    print(f"Ship {i['ship_id']} assigned to Mission {j['mission_id']}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Execute the optimization
ship_mission_optimization()
```

### Execution Results
**Status**: ERROR
**Error**: Traceback (most recent call last):
  File "/tmp/tmp1o_us3_2.py", line 81, in <module>
    ship_mission_optimization()
  File "/tmp/tmp1o_us3_2.py", line 62, in ship_mission_optimization
    mdl.add_constraint((i['nationality'] == j['required_nationality']) * x[(i['ship_id'], j['mission_id'])] == x[(i['ship_id'], j['mission_id'])], 
  File "/dccstor/nl2opt/miniforge3/envs/nl2opt_optim/lib/python3.10/site-packages/docplex/mp/dvar.py", line 281, in __rmul__
    return self.times(e)
  File "/dccstor/nl2opt/miniforge3/envs/nl2opt_optim/lib/python3.10/site-packages/docplex/mp/dvar.py", line 278, in times
    return self.to_linear_expr().multiply(e)
  File "/dccstor/nl2opt/miniforge3/envs/nl2opt_optim/lib/python3.10/site-packages/docplex/mp/linear.py", line 941, in multiply
    self.fatal("Multiply expects variable, expr or number, {0!r} was passed (type is {1})", e, type(e))
  File "/dccstor/nl2opt/miniforge3/envs/nl2opt_optim/lib/python3.10/site-packages/docplex/mp/basic.py", line 51, in fatal
    self.logger.fatal(msg, args)
  File "/dccstor/nl2opt/miniforge3/envs/nl2opt_optim/lib/python3.10/site-packages/docplex/mp/error_handler.py", line 210, in fatal
    raise DOcplexException(resolved_message)
docplex.mp.utils.DOcplexException: Multiply expects variable, expr or number, True was passed (type is <class 'bool'>)

**Analysis**: DOCplex encountered an error due to an invalid multiplication operation involving a boolean value. This suggests an issue with the implementation of the constraints.

## 7. Pyomo Implementation

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

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

def ship_mission_optimization():
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    missions = [
        {'mission_id': 1, 'min_tonnage': 5000, 'min_speed': 20, 'required_nationality': 'USA'},
        {'mission_id': 2, 'min_tonnage': 6000, 'min_speed': 25, 'required_nationality': 'UK'},
        {'mission_id': 3, 'min_tonnage': 7000, 'min_speed': 30, 'required_nationality': 'Canada'}
    ]
    
    ships = [
        {'ship_id': 1, 'tonnage': 5500, 'speed_knots': 22, 'nationality': 'USA'},
        {'ship_id': 2, 'tonnage': 6500, 'speed_knots': 27, 'nationality': 'UK'},
        {'ship_id': 3, 'tonnage': 7500, 'speed_knots': 32, 'nationality': 'Canada'}
    ]
    
    # 3. SETS
    model.MISSIONS = pyo.Set(initialize=[m['mission_id'] for m in missions])
    model.SHIPS = pyo.Set(initialize=[s['ship_id'] for s in ships])
    
    # 4. PARAMETERS
    model.min_tonnage = pyo.Param(model.MISSIONS, initialize={m['mission_id']: m['min_tonnage'] for m in missions})
    model.min_speed = pyo.Param(model.MISSIONS, initialize={m['mission_id']: m['min_speed'] for m in missions})
    model.required_nationality = pyo.Param(model.MISSIONS, initialize={m['mission_id']: m['required_nationality'] for m in missions})
    
    model.tonnage = pyo.Param(model.SHIPS, initialize={s['ship_id']: s['tonnage'] for s in ships})
    model.speed = pyo.Param(model.SHIPS, initialize={s['ship_id']: s['speed_knots'] for s in ships})
    model.nationality = pyo.Param(model.SHIPS, initialize={s['ship_id']: s['nationality'] for s in ships})
    
    # 5. VARIABLES
    model.x = pyo.Var(model.SHIPS, model.MISSIONS, within=pyo.Binary)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum((model.tonnage[i] + model.speed[i]) * model.x[i, j] for i in model.SHIPS for j in model.MISSIONS)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.minimize)
    
    # 7. CONSTRAINTS
    def mission_assignment_rule(model, j):
        return sum(model.x[i, j] for i in model.SHIPS) == 1
    model.mission_assignment = pyo.Constraint(model.MISSIONS, rule=mission_assignment_rule)
    
    def ship_assignment_rule(model, i):
        return sum(model.x[i, j] for j in model.MISSIONS) <= 1
    model.ship_assignment = pyo.Constraint(model.SHIPS, rule=ship_assignment_rule)
    
    def tonnage_requirement_rule(model, i, j):
        return model.tonnage[i] * model.x[i, j] >= model.min_tonnage[j] * model.x[i, j]
    model.tonnage_requirement = pyo.Constraint(model.SHIPS, model.MISSIONS, rule=tonnage_requirement_rule)
    
    def speed_requirement_rule(model, i, j):
        return model.speed[i] * model.x[i, j] >= model.min_speed[j] * model.x[i, j]
    model.speed_requirement = pyo.Constraint(model.SHIPS, model.MISSIONS, rule=speed_requirement_rule)
    
    def nationality_requirement_rule(model, i, j):
        return model.nationality[i] == model.required_nationality[j] if model.x[i, j].value == 1 else pyo.Constraint.Skip
    model.nationality_requirement = pyo.Constraint(model.SHIPS, model.MISSIONS, rule=nationality_requirement_rule)
    
    # 8. SOLVING WITH GUROBI
    solver = SolverFactory('gurobi')
    results = solver.solve(model, tee=True)
    
    # 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
ship_mission_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 19581.0
**Execution Time**: 0.95 seconds
**Reliability**: medium
**Retry Attempt**: 1
**Analysis**: Pyomo also found an optimal solution, but with a significantly higher execution time compared to Gurobipy, indicating lower efficiency.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 19581.00 | 0.16s | N/A | 1 |
| Docplex | ERROR | N/A | 1.15s | N/A | 1 |
| Pyomo | OPTIMAL | 19581.00 | 0.95s | N/A | 1 |

### Solver Consistency Analysis
**Result**: All solvers produced consistent results ✓
**Consistent Solvers**: gurobipy, pyomo
**Majority Vote Optimal Value**: 19581.0
**Solver Retry Summary**: gurobipy: 1 attempts, docplex: 1 attempts, pyomo: 1 attempts

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

### Business Interpretation
**Overall Strategy**: The optimal solution minimizes the total operational cost of assigning ships to missions, ensuring all constraints are met.
**Objective Value Meaning**: The optimal objective value of 19581.0 represents the minimum total operational cost for assigning ships to missions while meeting all requirements.
**Resource Allocation Summary**: Ships should be assigned to missions based on their tonnage, speed, and nationality to minimize costs while fulfilling mission requirements.
**Implementation Recommendations**: Use the Gurobipy solver for future optimizations due to its reliability and efficiency. Ensure all constraints are correctly implemented to avoid errors.