def cap_mip_gr(customers,facilities,max_time = 60): n_cust = len(customers) n_fac = len(facilities) dist = distance_matrix(customers, facilities) cartesian_prod = list(product(range(n_cust), range(n_fac))) setup_cost = [f.setup_cost for f in facilities] shipping_cost = {(c,f):dist[f,c] for c,f in cartesian_prod} demands = np.array([c.demand for c in customers]) caps = np.array([f.capacity for f in facilities]) solver = gp.Model('facility_location') # Define Variables x = solver.addVars(n_fac,vtype=GRB.BINARY, name='Select') y = solver.addVars(cartesian_prod, ub=1, vtype=GRB.CONTINUOUS, name='Assign') # Define Constraints solver.addConstrs((y[(c,f)] <= x[f] for c,f in cartesian_prod), name='Setup2ship') solver.addConstrs((gp.quicksum(y[(c,f)] for f in range(n_fac)) == 1 for c in range(n_cust)), name='Demand') solver.addConstrs((gp.quicksum(y[(c,f)]*demands[c] for c in range(n_cust)) <= caps[f] for f in range(n_fac)),name = 'Capacity') # Set Objective solver.setObjective(x.prod(setup_cost)+y.prod(shipping_cost), GRB.MINIMIZE) solver.setParam("TimeLimit",max_time) # Solve solver.optimize() # Parse Outputs solution = np.zeros((n_fac,n_cust)) for c in range(n_cust): for f in range(n_fac): solution[f,c] = y[(c,f)].x return solution.argmax(axis=0)
def ant_colony( customers, facilities, q=1, offset=0, evaporation=0.1, ants=100, generations=100, ): n_cust = len(customers) n_fac = len(facilities) dist = distance_matrix(customers, facilities) weights = np.ones_like(dist) best_sol = None best_cost = np.inf metrics = defaultdict(list) for gen in trange(generations): probs = weights / dist updates = np.zeros_like(dist) costs = [] for ant in range(ants): solution = ant_simulator(customers, facilities, dist, probs) cost = total_cost(solution, customers, facilities) edges = allocation2matrix(solution, n_fac) updates += edges * q / (cost - offset) costs.append(cost) if cost < best_cost: best_cost = cost best_sol = solution weights = weights * (1 - evaporation) + updates metrics["min_cost"].append(np.min(costs)) metrics["max_cost"].append(np.max(costs)) metrics["mean_cost"].append(np.mean(costs)) return best_sol, metrics
def cap_mip(customers, facilities, max_time=60): n_fac = len(facilities) n_cust = len(customers) solver = Solver.CreateSolver("FacilityLocation", "SCIP") 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 = [f.capacity for f in facilities] setup = [f.setup_cost for f in facilities] dist = distance_matrix(customers, facilities).astype(int) demands = [c.demand for c in customers] for f in range(n_fac): for c in range(n_cust): solver.Add(y[f][c] <= x[f]) for c in range(n_cust): solver.Add(sum([y[f][c] for f in range(n_fac)]) == 1) for f in range(n_fac): solver.Add(sum([y[f][c] * demands[c] for c in range(n_cust)]) <= caps[f]) obj = 0 for f in range(n_fac): obj += setup[f] * x[f] 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) status = solver.Solve() STATUS[status] a = [] for f in range(n_fac): a.append([y[f][c].solution_value() for c in range(n_cust)]) sol = np.array(a).argmax(axis=0) return sol, STATUS[status]
def ant_colony( customers, facilities, q=1, offset=0, evaporation=.1, ants=100, generations=100, ): n_cust = len(customers) n_fac = len(facilities) dist = distance_matrix(customers, facilities) weights = np.ones_like(dist) best_sol = None best_cost = np.inf metrics = defaultdict(list) greedy_solution = double_trial( customers, facilities, greedy_furthest, p_skip=0, pbar=False, ) for gen in trange(generations): probs = weights/dist updates = np.zeros_like(dist) costs = [] for ant in range(ants): if ant==0: solution = greedy_solution.copy() else: solution = ant_simulator(customers, facilities, dist, probs) cost = total_cost(solution,customers, facilities) edges = allocation2matrix(solution, n_fac) updates += edges*q/(cost-offset) costs.append(cost) if cost<best_cost: best_cost = cost best_sol = solution weights = weights*(1-evaporation) + updates metrics['min_cost'].append(np.min(costs)) metrics['max_cost'].append(np.max(costs)) metrics['mean_cost'].append(np.mean(costs)) return best_sol, metrics
def greedy_furthest( customers, facilities, ignore_setup=True, p_skip=0, pbar=True, order=None, ): dist = distance_matrix(customers, facilities) n_cust = len(customers) n_fac = len(facilities) solution = -np.ones(n_cust) opened_fac = np.zeros(n_fac) remaining_cap = np.array([f.capacity for f in facilities]) caps = np.array([f.capacity for f in facilities]) setup_cost = np.array([f.setup_cost for f in facilities]) dist_ord = dist.mean(axis=0).argsort() if order is not None: dist_ord = np.array(order) if pbar: iterator = tqdm(reversed(dist_ord), total=n_cust) else: iterator = reversed(dist_ord) for c in iterator: customer = customers[c] choice_cost = dist[:, c] if not ignore_setup: # choice_cost += (1 - opened_fac) * setup_cost choice_cost += setup_cost * customer.demand / caps / 2 for f in np.argsort(choice_cost): if remaining_cap[f] >= customer.demand: if np.random.rand() < p_skip: continue opened_fac[f] = 1 remaining_cap[f] -= customer.demand solution[c] = f break return solution.astype(int)
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]
# ## Constraint Programming # ### Uncapacitated Facilities from ortools.sat.python import cp_model from calc import distance_matrix customers,facilities= load_data('fl_100_1') n_cust = len(customers) n_fac = len(facilities) caps = [f.capacity for f in facilities] setup = [f.setup_cost for f in facilities] dist = distance_matrix(customers,facilities).astype(int) demands = [c.demand for c in customers] model = cp_model.CpModel() a = [] # allocation matrix (facilities x customers) for f in range(n_fac): a.append([model.NewBoolVar(f'a_{c}_{f}') for c in range(n_cust)]) # + # Only one facility per customer for c in range(n_cust): model.Add(sum([a[f][c] for f in range(n_fac)])==1) # Capacity check for f in range(n_fac):