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