Application of Simulated Annealing Algorithm in Discrete Factory Location Problem

1 Background introduction
1.1 The problem of
factory location The problem of factory location is one of the classic problems in operations research. It describes the comprehensive consideration of factors such as factory construction costs, production volume, product transportation costs, and local demand. How to formulate an appropriate location plan and logistics transportation plan to complete the production and operation activities of the enterprise. The research model of this problem has quite general adaptability. It is not only in the field of logistics, but also in production and life, and even in the military.
1.2
Simulated Annealing Algorithm The Simulated Annealing algorithm (Simulated Annealing, SA) is derived from the similarity with solid annealing, and the Metropolis criterion (accepting the new state with probability) is used as the acceptance criterion. The so-called solid annealing is a heat treatment process to obtain a solid low-energy lattice state. Firstly, the solid is heated to melt into a completely disordered liquid state. At this time, the particles are in a state of free motion; then the temperature gradually decreases, and the particle motion gradually tends to be ordered. When the temperature drops to the degree of crystallization, the particle motion becomes around the lattice The slight vibration of the point causes the liquid to solidify into a solid crystal. In 1983, Kirkpatrick made full use of the principle of solid annealing and proposed a simulated annealing algorithm. The core idea of ​​the algorithm simulates the solid annealing process, and shows strong robustness, globality and applicability in solving optimization problems. The following is a rough correspondence between the elements of the annealing algorithm and the solid annealing process. For a detailed introduction to the simulated annealing algorithm, please see here

Solid annealing Annealing algorithm
A state of the particle i A solution of the optimized state x
The lowest energy state Optimal solution
System energy Objective function
temperature Control parameters (decide the probability of accepting the new solution)
Tend to thermal equilibrium at a constant temperature Generate a new solution—judgment (accept, discard)

2 Problem description
Taking the problem of discrete factory location as an example, suppose there are n factories delivering goods to m distribution centers, and the demand for each distribution center is bj, j=1,2,...,m. On the premise of meeting the needs of the distribution center, any combination of factories can be selected to produce transportation products. Suppose that if a certain factory is selected, this requires a fixed cost di (which can be understood as construction cost or operating cost), i=1,2,...n, and the production capacity of each factory is ai. If the unit transportation cost from factory i to distribution center j is cij, how to formulate a location plan (which factories to choose) and transportation plan to minimize the total cost while meeting the demand of the distribution center?

3 Python code design
3.1 Parameter setting
Considering the problem of factory location consisting of 5 factories (AE) to 10 distribution centers (0-9), the parameter settings are as follows:
(1) The supply capacity of each factory, local demand, and construction /Fixed operating costs:

supply_capacity = [4500, 3500, 5000, 3000, 2500]
demand = [729, 630, 321, 293, 251, 573, 207, 732, 481, 783]
fix_cost_lst = [304, 281, 459, 292, 241]

(2) Freight rate table (saved in a file named'transcost.csv')

   A   B   C   D   E
0  9   2   3   7   3
1  5   1  10   6   5
2  6   4   5   3   6
3  8   6   7   3   4
4  8   7   5   6   8
5  1   7   1   5   4
6  9   1   8  10  10
7  8   5   5   3   7
8  4  10   8   5   8
9  3   5  10   7   3

(3) Randomly generate initial site selection plan

location = np.random.randint(0,2,5) 

3.2 Program code The
code is mainly divided into two major modules: one is to calculate the minimum total cost according to the site selection plan, and the other is to use the annealing algorithm to optimize the site selection plan.
(1) First import the required package, read the data, and set the initial parameters.

import pandas as pd   
import numpy as np
import pulp
import math
import matplotlib.pyplot as plt

data = pd.read_csv(r'transcost.csv')
demand = [729, 630, 321, 293, 251, 573, 207, 732, 481, 783]
supply_capacity = [4500, 3500, 5000, 3000, 2500]
fix_cost_lst = [304, 281, 459, 292, 241]
dic = {
    
    0:'A',1:'B',2:'C',3:'D',4:'E'}  #建立字典方便后面通过DataFrame组合相应的运价表

(2) The change of the site selection plan in the iterative process is a partially random process. In this process, a site selection plan whose demand cannot be met may be generated. At this time, it is necessary to adjust the plan that does not meet the conditions: if all If the demand is greater than the total supply of the selected factory, an unselected factory is randomly added until the demand can be met.

#计算选址方案的总供应量
def Cal_total_capacity(location,capacity):
    total_capacity_lst = [capacity[i] for i in range(len(location)) if location[i] == 1]
    return sum(total_capacity_lst)
# 对不满足条件的选址方案进行修正
def fixpop(location,capacity,demand):
    while sum(demand) > Cal_total_capacity(location, capacity):
        ind = np.random.randint(0,len(location))
        if location[ind] == 0:
            location[ind] = 1
    return location

(3) Generate the corresponding freight rate table according to the combination of location schemes. If the location plan is [1,0,1,1,0], then the column ['A','C','D'] of data is selected accordingly, and the combination is the tariff table under the plan.

def Get_Translist(location,data,dic):
    transcost = [list(data[dic[i]]) for i in range(len(location)) if location[i] == 1]
    return transcost

(4) With the location plan and the corresponding freight rate table, start to use pulp to calculate the optimal transportation plan. The function is to refer to the code here (https://www.jianshu.com/p/9be417cbfebb). I understand this part as a package for solving transportation problems. You don’t need to understand the code logic. You can directly change the original code. Used to solve transportation problems.

def transportation_problem(costs, x_max, y_max):
    row = len(costs)
    col = len(costs[0])
    prob = pulp.LpProblem('Transportation Problem', sense=pulp.LpMinimize)
    var = [[pulp.LpVariable(f'x{i}{j}', lowBound=0, cat=pulp.LpInteger) for j in range(col)] for i in range(row)]
    flatten = lambda x: [y for l in x for y in flatten(l)] if type(x) is list else [x]
    prob += pulp.lpDot(flatten(var), costs.flatten())
    for i in range(row):
        prob += (pulp.lpSum(var[i]) <= x_max[i])
    for j in range(col):
        prob += (pulp.lpSum([var[i][j] for i in range(row)]) == y_max[j])
    prob.solve()
    return {
    
    'objective':pulp.value(prob.objective), 'var': [[pulp.value(var[i][j]) for j in range(col)] for i in range(row)]}

(5) Calculate the minimum cost (including transportation cost and fixed cost) under a certain site selection plan that meets the conditions

def Cal_cost(location):
    pop = fixpop(location,supply_capacity,demand)   #对原来的选址方案进行检验修正,返回处理过的选址方案 pop
    fix_cost = 0
    capacity_new = []
    for i in range(len(pop)):
        if pop[i] == 1:
            fix_cost += fix_cost_lst[i]              #计算该方案下的固定费用fix_cost
            capacity_new.append(supply_capacity[i])  #capacity_new是选址方案pop对应的供应能力列表
    translist = Get_Translist(pop, data, dic)        #得到运价表
    costs = np.array(translist)
    
    max_plant = capacity_new
    max_cultivation = demand
    res = transportation_problem(costs, max_plant, max_cultivation)  #求解
    return res['var'],res["objective"] + fix_cost    #返回的第一个值是pop的最优运输方案,第二个值是pop的最小总费用

The Cal_cost(location) function is the final goal of the first part. It can input the location scheme (location) and output the minimum total cost. Written here, the first part (calculation of the total cost based on the location plan) code is completed. Now start to prepare for annealing.
(6) Obtain the initial annealing temperature. There is currently no uniform rule for obtaining the initial temperature. Here you can refer to the method of obtaining the initial temperature in other blogs to obtain a more appropriate initial temperature. The np.random.permutation() function is used to randomly shuffle the list sorting.

def GetInitialTem(location):
    cost1 = 0 ; cost2 = 0 ; dif = 0
    for i in range(10):
        location1 = list(np.random.permutation(location))
        location2 = list(np.random.permutation(location))
        cost1 = Cal_cost(location1)[1]
        cost2 = Cal_cost(location2)[1]
        difNew = abs(cost1 - cost2)
        if difNew >dif:
            dif = difNew
    pr = 0.5
    T0 = dif / pr
    return T0

(7) Disturbance is generated randomly and a new solution is obtained. The perturbation method is to randomly select a value in location for 0-1 exchange, and return to the new location plan

def Disturbance(location_old):
    location_new = location_old.copy()
    ind = np.random.randint(0,len(location_new))
    if location_new[ind] == 0:
        location_new[ind] = 1
    else:
        location_new[ind] = 0 
    return location_new

(8) Acceptance criterion function, used to judge whether to accept the new solution, is the key to the whole algorithm. The input deltaE of the function refers to the difference between the new value (ValueNew) and the old value (ValueOld), and T is the current temperature. If deltaE<0, it means that the new solution is better than the old solution, and the function returns 1, which means that the new solution is accepted; if deltaE>0 (the new solution is worse than the old solution), the new solution is accepted with the probability of (probility> random number). The initial temperature is higher, the probility is greater, and the probability of accepting a new solution is greater. It can jump out of the local optimal solution and prevent premature maturity; but when the temperature gradually decreases, the annealing process gradually stabilizes and the probability of accepting inferior solutions If it becomes smaller, the result will slowly approach the optimal solution of the optimization problem.

def Judge(deltaE,T):
    if deltaE < 0:
        return 1
    else:
        probility = math.exp(-deltaE/T)
        if probility > np.random.random():
            return 1
        else:
            return 0

At this point, the second part of the annealing process is also ready. The next step is to set up the main function part of the code.

location = np.random.randint(0,2,5)    #随机生成初始选址方案
ValueOld = Cal_cost(location)[1]       #计算初始方案的最小费用
count = 0							   #计数器
record_value = []					   #空列表,用来储存每一个被接受的解
tmp = GetInitialTem(location)		   #初始温度
tmp_min = 1e-5						   #最小温度,和计数器一起用于判定退火过程的结束点
alpha = 0.98						   #控制退火过程的快慢,即每产生一次较优解,温度下降(0.02*当前温度)

Begin annealing.

while (tmp > tmp_min and count < 2000):
    location_n = Disturbance(location)     #根据旧的选址方案产生新的选址方案
    location_new = fixpop(location_n, supply_capacity, demand)    #对新的选址方案进行检验和修正
    ValueNew = Cal_cost(location_new)[1]   #计算新选址方案的最小总费用
    deltaE = ValueNew - ValueOld           #比较新旧方案的费用
    if Judge(deltaE, tmp) == 1:			   #如果接受新解
        location = location_new			   #更新选址方案和最小总费用并记录	
        #print(ValueNew)				   #可以用来监视程序执行过程中的结果。
                                           #否则代码运行时间较长,我一直怀疑电脑卡住了。。。
        ValueOld = ValueNew
        record_value.append(ValueNew)
    if deltaE < 0 :
        tmp = tmp * alpha				   #如果生成了一个较优解,则退火,温度下降
    else:
        count += 1
#打印结果
print('='*80,'\n',
      'The minvalue is: {}'.format(min(record_value)),'\n',
      'The best location is: {}'.format(location),'\n',
      'The best transport schedule is: {}'.format(Cal_cost(location)[0]),'\n',
      '='*80)

Visualization of the annealing process

x = len(record_value)
index = [i+1 for i in range(x)]
plt.plot(index,record_value)
plt.xlabel('iterations')
plt.ylabel('minvalue')
plt.title('Simulated Annealing')

4 Operation results:

================================================================================ 
 The minvalue is: 13562.0 
 The best location is: [1 1 0 1 0] 
 The best transport schedule is: 
 [[0.0, 0.0, 0.0, 0.0, 0.0, 573.0, 0.0, 0.0, 481.0, 783.0],
  [729.0, 630.0, 0.0, 0.0, 0.0, 0.0, 207.0, 0.0, 0.0, 0.0], 
  [0.0, 0.0, 321.0, 293.0, 251.0, 0.0, 0.0, 732.0, 0.0, 0.0]] 
 ================================================================================

Insert picture description here
5 Some ideas
At present, I have tried a variety of heuristic algorithms to solve this problem, such as simulated annealing, tabu search and genetic algorithm. As far as the solution process is concerned, the optimal solution can be obtained by tabu search the fastest, and the optimal solution can be found within basically 10 steps. The reason is probably because there are fewer'mountains' to search, but as the solution scale grows, As the neighborhood area becomes larger, the process of solving tabu search may become very long; I personally think that the annealing algorithm is more versatile (I have tried it myself, and the annealing algorithm can achieve satisfactory results on TSP, VRP, CVRP, VRPTW, etc. The result of ), there is also strong robustness, but the annealing process in this example is very long (one is because the alpha=0.98 is larger, although the ergodicity of understanding is increased, but the convergence speed is affected; the second may be because of Cal_cost( location) calls many other functions, the process of obtaining the objective function value is slightly more complicated), but the effect of the solution scale on time should be smaller than that of taboo search; genetic algorithm is a group optimization algorithm, the principle is more complicated, and the code is very long to write , The optimization speed is not ideal.
Finally, when Xiaobai wrote a blog for the first time, he was not familiar with Python, so he wrote it step by step with a humble attitude of asking for advice. There must be some areas where the code is not concise, not standardized, and even the algorithm understanding is biased. I also hope the big guys are not tired of enlightenment. Next, I plan to demonstrate the use of tabu search to solve the problem, and I will write another article when I have time.

Guess you like

Origin blog.csdn.net/weixin_44086712/article/details/107413339