Esempio n. 1
0
def uncap_mip_iter(customers, facilities, triple=True, max_time=60):
    min_fac = est_facilities(customers, facilities)

    # Round 1
    sol, status = uncap_mip(customers, facilities, max_time)
    print(f"Initial Status: {status}")

    # Round 2
    if triple:
        cntr = list(Counter(sol).items())
        fid = sorted(cntr, key=itemgetter(1), reverse=True)[: min_fac * 2]
        fid = sorted([o[0] for o in fid])
        fac_sub = [f for i, f in enumerate(facilities) if i in fid]
        sol, status = uncap_mip(customers, fac_sub, max_time)
        sol = [fid[f] for f in sol]
        print(f"Intermediate Status: {status}")

    # Round 3
    cntr = list(Counter(sol).items())
    fid = sorted(cntr, key=itemgetter(1), reverse=True)[:min_fac]
    fid = sorted([o[0] for o in fid])
    fac_sub = [f for i, f in enumerate(facilities) if i in fid]
    sol, status = uncap_mip(customers, fac_sub, max_time)
    sol = [fid[f] for f in sol]
    print(f"Final Status: {status}")

    return sol
Esempio n. 2
0
def clustering(customers, facilities):
    n_cust = len(customers)
    n_fac = len(facilities)

    # Find minimum number of clusters
    min_fac = est_facilities(customers, facilities)

    # Find cluster centroids
    kmean = KMeans(min_fac)
    xy = np.array([c.location for c in customers])
    kmean.fit(xy)
    centroids = kmean.cluster_centers_

    # Find the distance between facilities and centroids
    fneigh = KDTree(np.array([f.location for f in facilities]))
    dist, locs = fneigh.query(centroids, min_fac)

    # Match facilities with centroids
    inactive_facs = set(range(n_fac))
    active_facs = []
    for i in range(min_fac):
        for j in range(min_fac):
            if locs[i, j] in inactive_facs:
                inactive_facs = inactive_facs - {locs[i, j]}
                active_facs.append(locs[i, j])
                break

    # Assign facilities to customers
    turn = 0
    remaining_cap = [facilities[f].capacity for f in active_facs]
    remaining_cust = set(range(n_cust))
    max_iter = len(active_facs) * n_cust
    sol = -np.ones(n_cust, dtype=int)
    pbar = trange(n_cust)
    for _ in range(max_iter):
        turn += 1
        turn %= len(active_facs)
        turn_fac = active_facs[turn]
        kd_cust = KDTree(
            np.array([customers[c].location for c in list(remaining_cust)])
        )
        dist, pot_cust = kd_cust.query([facilities[turn_fac].location], 1)
        pot_cust = list(remaining_cust)[pot_cust[0][0]]
        if remaining_cap[turn] < customers[pot_cust].demand:
            continue
        else:
            pbar.update()
            sol[pot_cust] = turn_fac
            remaining_cust = remaining_cust - {pot_cust}
            remaining_cap[turn] -= customers[pot_cust].demand
        if min(sol) >= 0:
            break
    else:
        IterationError("Maximum number of iteration reached.")
    return sol
Esempio n. 3
0
def double_trial(customers, facilities, solver, **kwargs):
    # First attempt
    solution = solver(customers, facilities, **kwargs)
    cost = total_cost(solution, customers, facilities)
    # Choosing top facilities
    min_fac = est_facilities(customers, facilities)
    sorted_facs = sorted(Counter(solution).items(), key=itemgetter(1), reverse=True)
    top_facs_idx = [f for f, i in sorted_facs[:min_fac]]
    top_facs = [facilities[f] for f in top_facs_idx]

    # Second Attempt
    solution2 = greedy_furthest(customers, top_facs, **kwargs)

    # Rearrange the solution
    solution2 = [top_facs_idx[f] for f in solution2]
    cost2 = total_cost(solution2, customers, facilities)
    if cost2 < cost:
        return solution2
    else:
        return solution
def cap_mip2(customers,
             facilities,
             max_time=60,
             min_fac=None,
             max_fac=None,
             k_neigh=None):
    n_fac = len(facilities)
    n_cust = len(customers)
    solver = Solver.CreateSolver("FacilityLocation", "SCIP")
    if min_fac is None:
        min_fac = min_facilities(customers, facilities)
    print(f'Minimum Facilities: {min_fac}')
    est_fac = est_facilities(customers, facilities)
    est_fac = max(5, min_fac)
    print('Estimated Facilities:', est_fac)
    if k_neigh is None:
        k_neigh = n_fac // est_fac * 2
        k_neigh = min(k_neigh, n_fac // 2)
    print(f'Only using {k_neigh} nearest facilities')

    # Estimate Customers per Facilitiy
    cpf = n_cust // min_fac
    cpf = np.clip(cpf, 2, n_cust // 2)

    # Define Variables
    x = []
    y = []
    for f in range(n_fac):
        y.append([solver.BoolVar(f"y_{f}_{c}") for c in range(n_cust)])
        x.append(solver.BoolVar(f"x_{f}"))

    caps = np.array([f.capacity for f in facilities])
    setup = np.array([f.setup_cost * 100 for f in facilities])
    dist = distance_matrix(customers, facilities) * 100
    dist = dist.astype(int)
    demands = np.array([c.demand for c in customers])

    # Problem Analysis
    free_setup = np.where(caps == 0)[0]
    is_fixed_setup = True if np.std(caps[caps > 0]) == 0 else False

    # Add Constraints
    print('\t Adding Constaints')
    # If facility is closed then it is not connected to any customer
    for f in range(n_fac):
        for c in range(n_cust):
            solver.Add(y[f][c] <= x[f])

    # Each customer is connected to only one facility
    for c in range(n_cust):
        solver.Add(sum([y[f][c] for f in range(n_fac)]) == 1)

    # The demand is not more than the capacity of the facility
    for f in range(n_fac):
        solver.Add(
            sum([y[f][c] * demands[c]
                 for c in range(n_cust)]) <= caps[f] * x[f])
        solver.Add(
            sum([y[f][c] * demands[c] for c in range(n_cust)]) <= caps[f])

    # Customers per facility
    for f in range(n_fac):
        solver.Add(sum([y[f][c] for c in range(n_cust)]) <= n_cust * x[f])

    # The free facilities must be open
    for f in free_setup:
        solver.Add(x[f] == 1)

    # Customer can ONLY connect to nearby facilities
    for c in range(n_cust):
        idx = np.argsort(dist[:, c])
        for f in idx[k_neigh:]:
            solver.Add(y[f][c] == 0)

    print('\t Adding Covercut Constaints')
    # Cover cut for customers which can be connected to a facility
    #     round1 = []
    #     round2 = []
    #     for f in range(n_fac):
    #         argsort = np.argsort(dist[f, :])
    #         idx = argsort[:cpf]
    #         if sum(demands[idx]) > caps[f]:
    #             round1.append(f)
    #             solver.Add(sum([y[f][c] for c in idx]) <= (cpf - 1))
    #         if cpf > 4:
    #             k = int(cpf * .9)
    #             idx = argsort[:k]
    #             if sum(demands[idx]) > caps[f]:
    #                 round2.append(f)
    #                 solver.Add(sum([y[f][c] for c in idx]) <= (k - 1))

    #     print(round1)
    #     print(round2, flush=True)

    # Maximum Facility open
    solver.Add(sum(x) >= min_fac)
    if max_fac is not None:
        solver.Add(sum(x) <= max_fac)
    #     solver.Add(sum(x)>=2)

    # Define objective
    obj = 0
    for f in range(n_fac):
        # Setup cost
        if not is_fixed_setup:
            obj += setup[f] * x[f]
        # Service cost
        obj += sum([dist[f][c] * y[f][c] for c in range(n_cust)])
    solver.Minimize(obj)

    STATUS = {
        Solver.FEASIBLE: "FEASIBLE",
        Solver.UNBOUNDED: "UNBOUNDED",
        Solver.BASIC: "BASIC",
        Solver.INFEASIBLE: "INFEASIBLE",
        Solver.NOT_SOLVED: "NOT_SOLVED",
        Solver.OPTIMAL: "OPTIMAL",
    }
    solver.SetTimeLimit(max_time * 1000)

    # Solve
    print('\t Starting the Solver')
    status = solver.Solve()
    STATUS[status]

    # Retreive values
    a = []
    for f in range(n_fac):
        a.append([y[f][c].solution_value() for c in range(n_cust)])

    # Convert solution matrix to facility index
    sol = np.array(a).argmax(axis=0)

    return sol, STATUS[status]
def est_facilities(customers, facilities):
    caps = [f.capacity for f in facilities]
    cumsum = np.cumsum(sorted(caps))
    demands = [c.demand for c in customers]
    total_demand = sum(demands)
    for i in range(len(facilities)):
        if cumsum[i]>=total_demand:
            print(f'Demand = {total_demand}, Capacity = {cumsum[i]}')
            return i+1
    
#     return math.ceil(total_demand/max(caps))


# -

est_facilities(customers,facilities)

customers, facilities = load_data('fl_1000_2')
n_cust = len(customers)
n_fac = len(facilities)
n_cust,n_fac

[f.setup_cost for f in facilities]


# +
# total_demand(customers)

# +
# [f.capacity for f in facilities]
# -