Kaggle competition: Traveling Santa 2018 - Prime Paths
<SOS> metric: TravelingSanta2 Kaggle data source: traveling-santa-2018-prime-paths<predict_on_test>
import pickle as p
import math
import random
pred_glove_test_y = model.predict([test_X], batch_size=1024, verbose=1 )<import_modules>
def load_tour(filename):
tour = []
with open(filename)as stream:
for i, line in enumerate(stream.readlines()):
if i == 0: continue
city = int(line)
 tour.append(city)
 assert(tour[0] == 0)and(tour[-1] == 0)
 return tour
def load_pickle(filename):
with open(filename, "rb")as stream:
return p.load(stream)
 popmusic = load_pickle('.. /input/travelingsantainputdata/popmusic.pickle')
 tour = load_tour('.. /input/travelingsantainputdata/tour-1516254.csv')
 print('LKH output tour & neighbor list successfully imported.' )
del embedding_matrix, model, x
time.sleep(10 )<choose_model_class>
def shuffle(l):
l = list(l)
 random.shuffle(l)
 return iter(l)
 class World:
def __init__(self, filename):
cities_x, cities_y, n = World._read_cities(filename)
 self.cities_x = tuple(cities_x)
 self.cities_y = tuple(cities_y)
 self.n = n
primes = World._prime_sieve(self.n)
 self.non_primes = tuple(1 - p for p in primes)
 @staticmethod
def _read_cities(filename):
cities_x, cities_y = [], []
with open(filename)as s:
for i, line in enumerate(s.readlines()):
if i == 0: continue
_, x, y = line.strip().split(',')
 cities_x.append(float(x))
cities_y.append(float(y))
assert len(cities_x)== i and len(cities_y)== i
return cities_x, cities_y, i
@staticmethod
def _prime_sieve(n):
assert n >= 2
primes = [False, False] + [True] *(n - 2)
 for i in range(2, n):
if primes[i]:
for j in range(2 * i, n, i):
primes[j] = False
return primes
def score(self, tour, percent=0.1):
distances = []
for i in range(1, len(tour)) :
x = self.cities_x[tour[i]] - self.cities_x[tour[i-1]]
y = self.cities_y[tour[i]] - self.cities_y[tour[i-1]]
distances.append(math.sqrt(( x * x)+(y * y)))
 penalties = []
for i in range(9, len(distances), 10):
penalties.append(self.non_primes[tour[i]] * distances[i])
 return sum(distances)+ percent * sum(penalties)
 class Tour:
@staticmethod
def load_csv(filename):
tour = []
with open(filename)as stream:
for i, line in enumerate(stream.readlines()):
if i == 0: continue
city = int(line)
 tour.append(city)
 assert(tour[0] == 0)and(tour[-1] == 0)
 return tour
@staticmethod
def save_csv(tour, filename):
with open(filename, "w")as s:
s.write("Path
")
 for city in tour:
s.write("%d
" % city)
 class Builder:
def noop(self):
self.add_forward(0, -1)
 return self
def forward_2opt(self, a, b):
self.add_forward(0, a)
 self.add_backward(a + 1, b)
 self.add_forward(b + 1, -1)
 return self
def forward_3opt(self, a, b, c):
self.add_forward(0, a)
 self.add_forward(b + 1, c)
 self.add_forward(a + 1, b)
 self.add_forward(c + 1, -1)
 return self
def forward_2x_bridge(self, a, b, c, d):
self.add_forward(0, a)
 self.add_forward(c + 1, d)
 self.add_forward(b + 1, c)
 self.add_forward(a + 1, b)
 self.add_forward(d + 1, -1)
 return self
class Candidates:
@staticmethod
def tour_inverse(tour):
tour_inverse = [0] * len(tour)
 for i, j in enumerate(tour):
tour_inverse[j] = i
return tour_inverse
@staticmethod
def pairs_2opt(tour, neighbors):
tour_inverse = Candidates.tour_inverse(tour)
 def n(x):
for y in neighbors[tour[x]]: yield tour_inverse[y]
for a0 in shuffle(range(len(tour)- 1)) :
a1 = a0 + 1
for b0 in n(a0):
b1 = b0 + 1
if a0 < b0 and b1 in n(a1):
yield a0, b0
@staticmethod
def triples_3opt(tour, neighbors):
tour_inverse = Candidates.tour_inverse(tour)
 def n(x):
for y in neighbors[tour[x]]: yield tour_inverse[y]
for a0 in shuffle(range(len(tour))):
a1 = a0 + 1
for b1 in n(a0):
b0 = b1 - 1
if a0 < b0:
for c1 in n(b0):
c0 = c1 - 1
if b0 < c0 and a1 in n(c0):
yield a0, b0, c0
@staticmethod
def quads_2x_bridge(tour, neighbors):
tour_inverse = Candidates.tour_inverse(tour)
 def n(x):
for y in neighbors[tour[x]]: yield tour_inverse[y]
pairs = []
for a0 in range(len(tour)- 1):
a1 = a0 + 1
for b1 in n(a0):
b0 = b1 - 1
if a0 < b0 and b0 in n(a1):
pairs.append(( a0, b0))
for i in shuffle(range(len(pairs))):
a, c = pairs[i]
quads = []
for j in range(i + 1, len(pairs)) :
b, d = pairs[j]
if not a < b: continue
if not b < c: break
if c < d: quads.append(( a, b, c, d))
for quad in shuffle(quads):
yield quad
class Scorer:
def __init__(self, tour, world, percent=0.1):
self.tour = tour
self.world = world
self.percent = percent
self.forward_scores = self._preprocess(tour)
 self.backward_scores = self._preprocess(tour[::-1])
 def _preprocess(self, tour):
distances = []
for i in range(1, len(tour)) :
x = self.world.cities_x[tour[i]] - self.world.cities_x[tour[i-1]]
y = self.world.cities_y[tour[i]] - self.world.cities_y[tour[i-1]]
distances.append(math.sqrt(( x * x)+(y * y)))
 scores = []
for j in range(10):
penalties = [0] * len(distances)
 for k in range(j, len(distances), 10):
penalties[k] = self.percent * self.world.non_primes[tour[k]] * distances[k]
cumsum = []
cumsum.append(distances[0] + penalties[0])
 for k in range(1, len(distances)) :
cumsum.append(cumsum[-1] + distances[k] + penalties[k])
 scores.append(cumsum)
 return scores
def score_forward(self, a, b, new_start):
assert b >= a
offset =(a - new_start + 9)% 10
distance = self.forward_scores[offset][b - 1]
if a > 0: distance -= self.forward_scores[offset][a - 1]
return distance
def score_edge(self, i, j, edge_index):
assert i != j
x = self.world.cities_x[j] - self.world.cities_x[i]
y = self.world.cities_y[j] - self.world.cities_y[i]
distance = math.sqrt(x * x + y * y)
 if edge_index % 10 == 9 and self.world.non_primes[i]:
distance *=(1 + self.percent)
 return distance
class ScoreBuilder(Builder):
def __init__(self, scorer):
self.scorer = scorer
self.score = 0.0
self.tour_length = 0
self.last_city = None
def add_forward(self, a, b):
if b == -1: b = len(self.scorer.tour)- 1
assert b >= a
if self.tour_length > 0:
self.score += self.scorer.score_edge(
 self.last_city,
self.scorer.tour[a],
self.tour_length - 1)
 if b > a:
self.score += self.scorer.score_forward(
 a, b, self.tour_length)
 self.tour_length += b - a + 1
self.last_city = self.scorer.tour[b]
def out(self):
return self.score
class TourBuilder(Builder):
def __init__(self, tour):
self.tour = tour
self.new_tour = []
def add_forward(self, a, b):
if b == -1: b = len(self.tour)- 1
assert b >= a
self.new_tour.extend(self.tour[a:b+1])
 def out(self):
return self.new_tour
class Neighbors:
@staticmethod
def load_pickle(filename, limit=None):
with open(filename, "rb")as stream:
neighbors = p.load(stream)
 if limit is not None:
for i in range(len(neighbors)) :
neighbors[i] = neighbors[i][:limit]
for i in range(len(neighbors)) :
for j in neighbors[i]:
if i not in neighbors[j]:
neighbors[j].append(i)
 return neighbors
class Move:
def __init__(self, name, candidates, score_forward, tour_forward):
self.name = name
self.candidates = candidates
self.score_forward = score_forward
self.tour_forward = tour_forward
move_3opt = Move(
 name="3-OPT",
candidates=lambda t: Candidates.triples_3opt(t, popmusic),
score_forward=lambda s, c: ScoreBuilder(s ).forward_3opt(*c ).out() ,
tour_forward=lambda t, c: TourBuilder(t ).forward_3opt(*c ).out() , 
)
 move_2x_bridge = Move(
 name="DOUBLE BRIDGE",
candidates=lambda t: Candidates.quads_2x_bridge(t, popmusic),
score_forward=lambda s, c: ScoreBuilder(s ).forward_2x_bridge(*c ).out() ,
tour_forward=lambda t, c: TourBuilder(t ).forward_2x_bridge(*c ).out() , 
)
 def progress_one(tour, world, move, percent=0.1, epsilon=1e-3):
scorer = Scorer(tour, world, percent)
 base_score = ScoreBuilder(scorer ).noop().out()
for candidate in move.candidates(tour):
score = move.score_forward(scorer, candidate)
 if score < base_score - epsilon:
return move.tour_forward(tour, candidate)
 def progress_many(tour, world, move, percent=0.1, epsilon=1e-3, limit=None, verbose=5):
counter = 0
while True:
counter += 1
if limit is not None and counter > limit: return tour
if verbose is not None:
if counter % verbose == 0: print(counter, world.score(tour, percent))
previous_tour = tour
tour = progress_one(previous_tour, world, move, percent, epsilon)
 if tour is None:
return previous_tour
def progress_all(tour, world, moves, percent=0.1, epsilon=1e-3, verbose=20):
counter = 0
while True:
for move in moves:
previous_tour = tour
tour = progress_many(tour, world, move, percent, epsilon, verbose=verbose)
 counter = 0 if tour != previous_tour else counter + 1
if counter == len(moves)- 1:
return tour
def kick(tour, world, move):
limit = int(random.uniform(50,100))
epsilon = - random.uniform(0.1, 0.5)
 penalty = 0.1
print("Begin kicking...epsilon =", epsilon, ', number of kicks =', limit, ', kick penalty =', penalty)
 return progress_many(tour, world, move, percent=penalty, epsilon=epsilon, limit=limit, verbose=None)
 print('Successfully importing custom library' )
embedding_matrix = load_fasttext_fast(tokenizer.word_index)
 inp = Input(shape=(maxlen,))
x = Embedding(max_features, embed_size, weights=[embedding_matrix] )(inp)
 x = Bidirectional(CuDNNGRU(64, return_sequences=True))(x)
 x = GlobalMaxPool1D()(x)
 x = Dense(16, activation="relu" )(x)
 x = Dropout(0.1 )(x)
 x = Dense(1, activation="sigmoid" )(x)
 model = Model(inputs=inp, outputs=x)
 model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
 model.summary()<train_model>
VERBOSE = 5
world = World('.. /input/traveling-santa-2018-prime-paths/cities.csv')
 print('INPUT SCORE:', world.score(tour))
for penalty in [0.01,0.02,0.03,0.04,0.05,0.06,0.07,0.08,0.09,0.1]:
for epsilon in [0]:
print('Penalty:', penalty, '.Epsilon:', epsilon)
 tour = progress_all(tour, world,(move_2x_bridge, move_3opt), percent=penalty, epsilon=epsilon, verbose=VERBOSE)
 print('SCORE AFTER OPTIMIZATION:', world.score(tour))
model.fit(train_X, train_y, batch_size=512, epochs=3, validation_data=(val_X, val_y))<predict_on_test>
nb_kicks = 10
verbose = 1
kick_move = move_2x_bridge
reoptimize_moves =(move_3opt , move_2x_bridge)
 cnt_success = 0
cnt_fail = 0
for k in range(nb_kicks):
input_score = world.score(tour)
 print('Kick', k ,'INPUT SCORE:', input_score)
 broken_tour = kick(tour, world, kick_move)
 broken_score = world.score(broken_tour)
 print("Kicked.Score increase", broken_score - input_score, '(', broken_score ,')')
 print("Now fixing...")
 fixed_tour = progress_all(broken_tour, world, reoptimize_moves)
 score = world.score(fixed_tour)
 if score < input_score:
cnt_success += 1
print('KICK SUCCESSFUL! New score:', score)
 filename = "tour-" + "%.2f" % score + ".csv"
Tour.save_csv(tour, filename)
 tour = fixed_tour
else:
print('KICK FAILED!')
 cnt_fail += 1
print('Success count:', cnt_success, 'Fail count:', cnt_fail )
pred_fasttext_val_y = model.predict([val_X], batch_size=1024, verbose=1)
 max_f1 = 0
for thresh in np.arange(0.2, 0.601, 0.01):
thresh = np.round(thresh, 2)
 current_f1 = metrics.f1_score(val_y,(pred_fasttext_val_y>thresh ).astype(int))
print("ค่า F1 ที่ threshold %.2f is %.4f" %(thresh, current_f1))
if current_f1 > max_f1:
best_thresh = thresh
max_f1 = current_f1
print('ค่า threshold ที่ดีที่สุดคือ %.2f ให้ค่า F1 เท่ากับ %.4f' %(best_thresh,max_f1)) 
<predict_on_test>
class World:
def __init__(self, filename):
cities_x, cities_y, n = World._read_cities(filename)
 self.cities_x = tuple(cities_x)
 self.cities_y = tuple(cities_y)
 self.n = n
primes = World._prime_sieve(self.n)
 self.non_primes = tuple(1 - p for p in primes)
 @staticmethod
def _read_cities(filename):
cities_x, cities_y = [], []
with open(filename)as s:
for i, line in enumerate(s.readlines()):
if i == 0: continue
_, x, y = line.strip().split(',')
 cities_x.append(float(x))
cities_y.append(float(y))
assert len(cities_x)== i and len(cities_y)== i
return cities_x, cities_y, i
@staticmethod
def _prime_sieve(n):
assert n >= 2
primes = [False, False] + [True] *(n - 2)
 for i in range(2, n):
if primes[i]:
for j in range(2 * i, n, i):
primes[j] = False
return primes
def distance(self, i, j):
x = self.cities_x[i] - self.cities_x[j]
y = self.cities_y[i] - self.cities_y[j]
return math.sqrt(( x * x)+(y * y))
def score(self, tour, percent=0.1):
distances = []
for i in range(1, len(tour)) :
distances.append(self.distance(tour[i], tour[i-1]))
penalties = []
for i in range(9, len(tour)- 1, 10):
penalties.append(self.non_primes[tour[i]] * distances[i])
 return sum(distances)+ percent * sum(penalties)
 class Tour:
@staticmethod
def load_csv(filename):
tour = []
with open(filename)as stream:
for i, line in enumerate(stream.readlines()):
if i == 0: continue
city = int(line)
 tour.append(city)
 assert(tour[0] == 0)and(tour[-1] == 0)
 return tour
@staticmethod
def save_csv(tour, filename):
with open(filename, "w")as s:
s.write("Path
")
 for city in tour:
s.write("%d
" % city)
 @staticmethod
def edge_distance(tour1, tour2):
assert len(tour1)== len(tour2)
 def edges(tour):
for i in range(len(tour)- 1):
yield tuple(sorted(( tour[i], tour[i + 1])))
 edges2 = set(edges(tour2))
distance = 0
for edge in edges(tour1):
if edge not in edges2:
distance += 1
return distance
def prime_distance(tour1, tour2):
assert len(tour1)== len(tour2)
 primes = World._prime_sieve(len(tour1))
def placed_primes(tour):
for i in range(9, len(tour)- 1, 10):
if primes[tour[i]]: yield tour[i]
primes1 = set(placed_primes(tour1))
primes2 = set(placed_primes(tour2))
return len(primes1 | primes2)- len(primes1 & primes2)
 @staticmethod
def distance(tour1, tour2):
return(
 Tour.edge_distance(tour1, tour2),
Tour.prime_distance(tour1, tour2))
class Neighbors:
@staticmethod
def load_pickle(filename):
with open(filename, "rb")as stream:
return p.load(stream)
<import_modules><EOS> 
class Dict:
def __init__(self):
self.index = dict()
self.keys = list()
self.values = list()
def __contains__(self, key):
return key in self.index
def __len__(self):
return len(self.keys)
 def __setitem__(self, key, value):
assert key not in self
self.index[key] = len(self.keys)
 self.keys.append(key)
 self.values.append(value)
 def __getitem__(self, key):
return self.values[self.index[key]]
def __delitem__(self, key):
assert key in self
i = self.index[key]
k = self.keys[-1]
v = self.values[-1]
self.index[k] = i
self.keys[i] = k
self.values[i] = v
del self.index[key]
del self.keys[-1]
del self.values[-1]
def random_key(self):
return random.choice(self.keys)
 class Graph:
def __init__(self):
self.edges = Dict()
def empty(self):
return len(self.edges)== 0
def add_edge(self, v0, v1):
if v0 not in self.edges:
self.edges[v0] = list()
self.edges[v0].append(v1)
 if v1 not in self.edges:
self.edges[v1] = list()
self.edges[v1].append(v0)
 def remove_edge(self, v0, v1):
self.edges[v0].remove(v1)
 if len(self.edges[v0])== 0:
del self.edges[v0]
self.edges[v1].remove(v0)
 if len(self.edges[v1])== 0:
del self.edges[v1]
def random_vertex(self):
assert not self.empty()
return self.edges.random_key()
def random_adjacent(self, v):
assert v in self.edges
return random.choice(self.edges[v])
 @staticmethod
def from_subtours(subtours):
graph = Graph()
for subtour in subtours:
assert subtour[0] == subtour[-1]
for i in range(len(subtour)- 1):
v0, v1 = subtour[i], subtour[i + 1]
graph.add_edge(v0, v1)
 return graph
def to_subtours(self, v=0):
while not self.empty() :
if v not in self.edges:
v = self.random_vertex()
subtour = [v]
while True:
v0 = subtour[-1]
if v0 not in self.edges: break
v1 = self.random_adjacent(v0)
 self.remove_edge(v0, v1)
 subtour.append(v1)
 assert subtour[0] == subtour[-1]
yield subtour
class AB_Cycles:
@staticmethod
def _find_cycle(path):
assert len(path)> 0
last_vertex = path[-1]
for index in range(len(path)- 3, -1, -2):
if path[index] == last_vertex:
return index
@staticmethod
def _remove_cycle(path):
index = AB_Cycles._find_cycle(path)
 if index is not None:
path, cycle = path[:(index + 1)], path[index:]
if len(path)& 1 == 0: cycle = cycle[1:] + [cycle[1]]
if len(path)== 1: path = []
else: cycle = None
return path, cycle
@staticmethod
def ab_cycles(tour_a, tour_b):
assert len(tour_a)== len(tour_b)
 path = list()
graphs =(
 Graph.from_subtours([tour_a]),
Graph.from_subtours([tour_b]))
while True:
if len(path)== 0:
if graphs[0].empty() : break
vertex = graphs[0].random_vertex()
path.append(vertex)
 ab = 1 -(len(path)& 1)
 v0 = path[-1]
v1 = graphs[ab].random_adjacent(v0)
 graphs[ab].remove_edge(v0, v1)
 path.append(v1)
 path, cycle = AB_Cycles._remove_cycle(path)
 if cycle is not None: yield cycle
assert graphs[0].empty()
assert graphs[1].empty()
assert len(path)== 0
class EAX:
@staticmethod
def _eset_rand(ab_cycles):
for ab_cycle in ab_cycles:
if len(ab_cycle)> 3 and random.uniform(0, 1)> 0.5:
yield ab_cycle
@staticmethod
def _intermediate_solution(tour_a, eset):
graph = Graph.from_subtours([tour_a])
 for ab_cycle in eset:
ab = 0
for i in range(len(ab_cycle)- 1):
v0, v1 = ab_cycle[i], ab_cycle[i + 1]
if ab == 0: graph.remove_edge(v0, v1)
 else: graph.add_edge(v0, v1)
 ab = 1 - ab
return graph
@staticmethod
def _shortest_subtour(subtours):
lengths = [len(subtour)for subtour in subtours]
i = lengths.index(min(lengths))
shortest = subtours[i]
others = subtours[:i] + subtours[(i + 1):]
return shortest, others
@staticmethod
def _quads(subtours, neighbors):
shortest, others = EAX._shortest_subtour(subtours)
 x = list()
for i in range(len(shortest)- 1):
v0, v1 = shortest[i], shortest[i + 1]
x.append(( v0, v1))
y = set()
for subtour in others:
for i in range(len(subtour)- 1):
v0, v1 = subtour[i], subtour[i + 1]
y.add(( v0, v1))
y.add(( v1, v0))
for v0, v1 in x:
for v2 in neighbors[v0]:
for v3 in neighbors[v1]:
if(v2, v3)in y:
yield v0, v1, v2, v3
@staticmethod
def _min_quad(subtours, world, neighbors):
min_distance = None
min_quad = None
for v0, v1, v2, v3 in EAX._quads(subtours, neighbors):
removed = world.distance(v0, v1)+ world.distance(v2, v3)
 for(v2, v3)in(( v2, v3),(v3, v2)) :
added = world.distance(v0, v2)+ world.distance(v1, v3)
 distance = added - removed
if min_distance is None or distance < min_distance:
min_distance = distance
min_quad = v0, v1, v2, v3
return min_quad
def _offspring_solution(graph, world, neighbors):
while True:
subtours = tuple(graph.to_subtours())
 if len(subtours)== 1: return subtours[0]
print("subtours", len(subtours))
v0, v1, v2, v3 = EAX._min_quad(subtours, world, neighbors)
 graph = Graph.from_subtours(subtours)
 graph.remove_edge(v0, v1)
 graph.remove_edge(v2, v3)
 graph.add_edge(v0, v2)
 graph.add_edge(v1, v3)
 @staticmethod
def eax(tour_a, tour_b, world, neighbors):
ab_cycles = AB_Cycles.ab_cycles(tour_a, tour_b)
 eset = EAX._eset_rand(ab_cycles)
 intermediate = EAX._intermediate_solution(tour_a, eset)
 return EAX._offspring_solution(intermediate, world, neighbors)