# Complete GUROBIPY implementation - Retry Attempt 3

import gurobipy as gp
from gurobipy import GRB

def optimize_scientist_assignment():
    # 1. MODEL & DATA SETUP
    model = gp.Model("scientist_assignment")
    
    # Data from the problem
    projects_hours = [120, 150, 100]
    project_max_hours = [200, 250, 180]
    num_scientists = 5
    num_projects = len(projects_hours)
    
    # CRITICAL: Validate array lengths before loops
    assert len(projects_hours) == len(project_max_hours) == num_projects, "Array length mismatch"
    
    # 2. VARIABLES
    # Binary decision variables x_ij: 1 if scientist i is assigned to project j, 0 otherwise
    x = model.addVars(num_scientists, num_projects, vtype=GRB.BINARY, name="x")
    
    # 3. OBJECTIVE FUNCTION
    # Minimize the total project hours
    model.setObjective(
        gp.quicksum(projects_hours[j] * x[i, j] for i in range(num_scientists) for j in range(num_projects)),
        GRB.MINIMIZE
    )
    
    # 4. CONSTRAINTS
    
    # Scientist Assignment Constraint: Each scientist must be assigned to at least one project
    for i in range(num_scientists):
        model.addConstr(
            gp.quicksum(x[i, j] for j in range(num_projects)) >= 1,
            name=f"scientist_assignment_{i}"
        )
    
    # Project Hours Constraint: The total hours assigned to a project must not exceed its maximum allowed hours
    for j in range(num_projects):
        model.addConstr(
            gp.quicksum(projects_hours[j] * x[i, j] for i in range(num_scientists)) <= project_max_hours[j],
            name=f"project_hours_{j}"
        )
    
    # Project Assignment Constraint: Each project must have at least one scientist assigned to it
    for j in range(num_projects):
        model.addConstr(
            gp.quicksum(x[i, j] for i in range(num_scientists)) >= 1,
            name=f"project_assignment_{j}"
        )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in range(num_scientists):
            for j in range(num_projects):
                if x[i, j].x > 1e-6:
                    print(f"Scientist {i} assigned to Project {j}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Execute the optimization
optimize_scientist_assignment()