# Complete GUROBIPY implementation - Retry Attempt 4

import gurobipy as gp
from gurobipy import GRB

def optimize_research_allocation():
    # 1. MODEL & DATA SETUP
    model = gp.Model("ResearchAllocation")

    # Data from Authorship table
    authorship_data = [
        (1, 101, 201, 1),
        (2, 102, 202, 2),
        (3, 103, 203, 3)
    ]

    # Extract unique researchers, institutions, and papers
    researchers = list(set(auth[0] for auth in authorship_data))
    institutions = list(set(auth[1] for auth in authorship_data))
    papers = list(set(auth[2] for auth in authorship_data))

    # Create weights dictionary based on authOrder
    weights = {(auth[0], auth[2]): auth[3] for auth in authorship_data}

    # Business configuration parameters
    MaxResearchersPerInstitution = 2
    MaxPapersPerResearcher = 2

    # Validate array lengths
    assert len(researchers) > 0, "No researchers found"
    assert len(institutions) > 0, "No institutions found"
    assert len(papers) > 0, "No papers found"

    # 2. VARIABLES
    x = model.addVars(researchers, institutions, papers, vtype=GRB.BINARY, name="x")

    # 3. OBJECTIVE FUNCTION
    model.setObjective(
        gp.quicksum(weights[a, p] * x[a, i, p] for a in researchers for i in institutions for p in papers if (a, p) in weights),
        GRB.MAXIMIZE
    )

    # 4. CONSTRAINTS

    # Maximum Researchers per Institution
    for i in institutions:
        model.addConstr(
            gp.quicksum(x[a, i, p] for a in researchers for p in papers) <= MaxResearchersPerInstitution,
            name=f"max_researchers_{i}"
        )

    # Maximum Papers per Researcher
    for a in researchers:
        model.addConstr(
            gp.quicksum(x[a, i, p] for i in institutions for p in papers) <= MaxPapersPerResearcher,
            name=f"max_papers_{a}"
        )

    # Single Assignment per Paper
    for p in papers:
        model.addConstr(
            gp.quicksum(x[a, i, p] for a in researchers for i in institutions) == 1,
            name=f"single_assignment_{p}"
        )

    # 5. SOLVING & RESULTS
    model.optimize()

    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for a in researchers:
            for i in institutions:
                for p in papers:
                    if x[a, i, p].x > 0.5:
                        print(f"Researcher {a} assigned to Institution {i} for Paper {p}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")

    return model

# Execute the optimization
optimize_research_allocation()