-
Notifications
You must be signed in to change notification settings - Fork 26
/
tsp.py
141 lines (117 loc) · 4.31 KB
/
tsp.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#coding:utf-8
import numpy as np
import sqlite3
import json
from util import plot
def sum_distmat(p, distmat):
dist = 0
num_location = p.shape[0]
for i in range(num_location-1):
dist += distmat[p[i]][p[i+1]]
dist += distmat[p[0]][p[num_location-1]]
return dist
def get_distmat(p):
num_location = p.shape[0]
# 1 degree of lat/lon ~ 111km = 111000m in Taiwan
p *= 111000
distmat = np.zeros((num_location, num_location))
for i in range(num_location):
for j in range(i, num_location):
distmat[i][j] = distmat[j][i] = np.linalg.norm(p[i] - p[j])
return distmat
def swap(sol_new):
while True:
n1 = np.int(np.floor(np.random.uniform(0, sol_new.shape[0])))
n2 = np.int(np.floor(np.random.uniform(0, sol_new.shape[0])))
if n1 != n2:
break
sol_new[n1], sol_new[n2] = sol_new[n2], sol_new[n1]
return sol_new
def reverse(sol_new):
while True:
n1 = np.int(np.floor(np.random.uniform(0, sol_new.shape[0])))
n2 = np.int(np.floor(np.random.uniform(0, sol_new.shape[0])))
if n1 != n2:
break
sol_new[n1:n2] = sol_new[n1:n2][::-1]
return sol_new
def transpose(sol_new):
while True:
n1 = np.int(np.floor(np.random.uniform(0, sol_new.shape[0])))
n2 = np.int(np.floor(np.random.uniform(0, sol_new.shape[0])))
n3 = np.int(np.floor(np.random.uniform(0, sol_new.shape[0])))
if n1 != n2 != n3 != n1:
break
#Let n1 < n2 < n3
n1, n2, n3 = sorted([n1, n2, n3])
#Insert data between [n1,n2) after n3
tmplist = sol_new[n1:n2].copy()
sol_new[n1 : n1+n3-n2+1] = sol_new[n2 : n3+1].copy()
sol_new[n3-n2+1+n1 : n3+1] = tmplist.copy()
return sol_new
def accept(cost_new, cost_current, T):
# If new cost better than current, accept it
# If new cost not better than current, accept it by probability P(dE)
# P(dE) = exp(dE/(kT)), defined by thermodynamics
return ( cost_new < cost_current or
np.random.rand() < np.exp(-(cost_new - cost_current) / T) )
def save_sqlite(payloads):
conn = sqlite3.connect('data/tsp.sqlite')
c = conn.cursor()
c.execute("CREATE TABLE IF NOT EXISTS TSP (costs REAL, route TEXT, markov_step INTEGER) ")
c.execute('INSERT INTO TSP VALUES (?,?,?)' , payloads)
conn.commit()
conn.close()
def main():
filename = "data/pokestops.csv"
coordinates = np.loadtxt(filename, delimiter=',')
# Params Initial
num_location = coordinates.shape[0]
markov_step = 10 * num_location
T, T_MIN, T_ALPHA = 100, 1, 0.99
# Build distance matrix to accelerate cost computing
distmat = get_distmat(coordinates)
# States: New, Current and Best
sol_new, sol_current, sol_best = (np.arange(num_location), ) * 3
cost_new, cost_current, cost_best = (float('inf'), ) * 3
# Record costs during the process
costs = []
# Simulated Annealing
while T > T_MIN:
for i in np.arange(markov_step):
# Use three different methods to generate new solution
# Swap, Reverse, and Transpose
choice = np.random.randint(3)
if choice == 0:
sol_new = swap(sol_new)
elif choice ==1:
sol_new = reverse(sol_new)
else:
sol_new = transpose(sol_new)
# Get the total distance of new route
cost_new = sum_distmat(sol_new, distmat)
if accept(cost_new, cost_current, T):
# Update sol_current
sol_current = sol_new.copy()
cost_current = cost_new
if cost_new < cost_best:
sol_best = sol_new.copy()
cost_best = cost_new
else:
sol_new = sol_current.copy()
# Lower the temperature
T *= T_ALPHA
costs.append(cost_best)
# Monitor the temperature & cost
print("Temperature:", T,", Distance:",cost_best)
# Show final cost & route
print("Final Distance:", costs[-1])
print("Best Route", sol_best)
route = json.dumps(sol_best.tolist())
payloads = [ costs[-1], route, markov_step ]
# Save the result to sqlite
save_sqlite(payloads)
# Plot cost function and TSP-route
plot(sol_best.tolist(), coordinates, costs)
if __name__ == "__main__":
main()