## 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.