# Complete DOCPLEX implementation - Retry Attempt 4

from docplex.mp.model import Model

def party_host_optimization():
    # 1. MODEL & DATA SETUP
    mdl = Model(name="party_host_optimization")
    
    # Data from the database
    cost_per_host = {1: 50, 2: 70, 3: 60}
    min_hosts = {1: 2, 2: 3, 3: 4}
    max_hosts = {1: 4, 2: 5, 3: 6}
    expertise_match = {(1, 1): True, (1, 2): False, (2, 2): True}
    
    # Extract sets of parties and hosts
    parties = set(min_hosts.keys())
    hosts = set(cost_per_host.keys())
    
    # 2. VARIABLES
    assign = {(p, h): mdl.binary_var(name=f"assign_{p}_{h}") for p in parties for h in hosts}
    
    # 3. OBJECTIVE FUNCTION
    total_cost = mdl.sum(cost_per_host[h] * assign[p, h] for p in parties for h in hosts)
    mdl.minimize(total_cost)
    
    # 4. CONSTRAINTS
    # Minimum hosts per party
    for p in parties:
        mdl.add_constraint(mdl.sum(assign[p, h] for h in hosts) >= min_hosts[p], ctname=f"min_hosts_{p}")
    
    # Maximum hosts per party
    for p in parties:
        mdl.add_constraint(mdl.sum(assign[p, h] for h in hosts) <= max_hosts[p], ctname=f"max_hosts_{p}")
    
    # Expertise matching
    for p in parties:
        for h in hosts:
            if (p, h) in expertise_match:
                mdl.add_constraint(assign[p, h] <= expertise_match[p, h], ctname=f"expertise_{p}_{h}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for p in parties:
            for h in hosts:
                if solution.get_value(assign[p, h]) > 0:
                    print(f"Party {p} assigned Host {h}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Execute the optimization
party_host_optimization()