Files
hashcode-2018-qualification/__main__.py

226 lines
6.6 KiB
Python

import numpy as np
import threading
INPUT_FILE = "data/medium.in"
POPULATION = 1000
MUTATION_AMOUNT = 250
ITERATIONS = 30
data = [line for line in open(INPUT_FILE)]
params = list(map(int, data[0].split(" ")))
data = [[0 if x=="T" else 1 for x in line] for line in data[1:]]
data = np.array(data)[:, :-1]
clusters = np.arange(params[0]*params[1]).reshape((1, params[0], params[1]))
clusters = np.repeat(clusters, POPULATION, axis=0)+1
print(params)
print(data)
print(clusters[0])
values = {}
first = True
class myThread (threading.Thread):
def __init__(self, cluster, clean, id):
threading.Thread.__init__(self)
self.cluster = cluster
self.clean = clean
self.id = id
self.result = None
self.first = False
self.values = {}
self.vfunc = np.vectorize(self.myfunc)
def run(self):
# calc fitness
self.values = {}
self.first = True
self.vfunc(self.cluster, data)
self.result = get_fitness(self.values, clean=self.clean)
print("Exit thread", self.id)
def myfunc(self, a, b):
if self.first:
self.first = False
return
if a not in values:
self.values[a] = [0, 0]
self.values[a][b] += 1
def get_fitnesses(clusts, clean=False):
threads = []
for i, cluster in enumerate(clusters):
if i % 20 == 0:
print("fitness", i, iteration)
# Create new threads
thread = myThread(cluster, clean, i)
# Start new Threads
thread.start()
# Add threads to thread list
threads.append(thread)
# Wait for all threads to complete
for t in threads:
t.join()
print("Exiting Main Thread")
return np.array([thread.result for thread in threads])
def get_fitness(vals, clean=False):
fit = 0
for key, val in vals.items():
if key == 0:
continue
size = sum(val)
if size <= params[3] and min(val) >= params[2]:
fit += size
if clean:
continue
size_diff = params[3]-size
if size_diff < 0:
fit += 1-size_diff**2
elif size_diff > 0:
fit += np.exp(-abs(size-params[3]))
return fit
def get_left_bound(clust, y, x):
val = clust[y, x]
while x > 0 and val == clust[y, x-1]:
x -= 1
return (y, x)
def get_right_bound(clust, y, x):
val = clust[y, x]
while x+1 < clust.shape[1] and val == clust[y, x+1]:
x += 1
return (y, x)
def get_top_bound(clust, y, x):
val = clust[y, x]
while y > 0 and val == clust[y-1, x]:
y -= 1
return (y, x)
def get_bottom_bound(clust, y, x):
val = clust[y, x]
while y+1 < clust.shape[0] and val == clust[y+1, x]:
y += 1
return (y, x)
def set_area(clust, y1, x1, y2, x2, value):
for y in range(y1, y2+1):
for x in range(x1, x2+1):
clust[y, x] = value
return clust
def mutation(clust):
for _ in range(np.random.random_integers(MUTATION_AMOUNT)):
y = np.random.random_integers(params[0])-1
x = np.random.random_integers(params[1])-1
z = np.random.random()
if z < 0.2:
if y > 0: # expand to top
yn = y-1
_, inner_left = get_left_bound(clust, y, x)
_, outer_left = get_left_bound(clust, yn, inner_left)
_, inner_right = get_right_bound(clust, y, x)
_, outer_right = get_right_bound(clust, yn, inner_right)
clust = set_area(clust, yn, outer_left, yn, outer_right, 0)
clust = set_area(clust, yn, inner_left, yn, inner_right, clust[y, x])
elif z < 0.4:
if x > 0: # expand to left
xn = x-1
inner_top, _ = get_top_bound(clust, y, x)
outer_top, _ = get_top_bound(clust, inner_top, xn)
inner_bot, _ = get_bottom_bound(clust, y, x)
outer_bot, _ = get_bottom_bound(clust, inner_bot, xn)
clust = set_area(clust, outer_top, xn, outer_bot, xn, 0)
clust = set_area(clust, inner_top, xn, inner_bot, xn, clust[y, x])
elif z < 0.6:
if y < params[0]-1: # expand to bottom
yn = y+1
_, inner_left = get_left_bound(clust, y, x)
_, outer_left = get_left_bound(clust, yn, inner_left)
_, inner_right = get_right_bound(clust, y, x)
_, outer_right = get_right_bound(clust, yn, inner_right)
clust = set_area(clust, yn, outer_left, yn, outer_right, 0)
clust = set_area(clust, yn, inner_left, yn, inner_right, clust[y, x])
elif z < 0.8:
if x < params[1]-1: # expand to right
xn = x+1
inner_top, _ = get_top_bound(clust, y, x)
outer_top, _ = get_top_bound(clust, inner_top, xn)
inner_bot, _ = get_bottom_bound(clust, y, x)
outer_bot, _ = get_bottom_bound(clust, inner_bot, xn)
clust = set_area(clust, outer_top, xn, outer_bot, xn, 0)
clust = set_area(clust, inner_top, xn, inner_bot, xn, clust[y, x])
else:
pass#clust[y, x] = np.amax(clust)+1
return clust
def myfunc(a, b):
global first, values
if first:
first = False
return
if a not in values:
values[a] = [0, 0]
values[a][b] += 1
vfunc = np.vectorize(myfunc)
# mutation
xx = MUTATION_AMOUNT
for i in range(POPULATION):
if i % 20 == 0:
print("mutation", i)
MUTATION_AMOUNT = 500
clusters[i] = mutation(clusters[i])
MUTATION_AMOUNT = xx
for iteration in range(ITERATIONS):
fitnesses = get_fitnesses(clusters, clean=False)
# calc fitness
fitnesses = np.zeros((POPULATION, ))
for i, cluster in enumerate(clusters):
if i % 20 == 0:
print("fitness", i, iteration)
values = {}
first = True
vfunc(cluster, data)
fitnesses[i] = get_fitness(values)
# select
z_exp = [np.exp(i) for i in fitnesses]
sum_z_exp = sum(z_exp)
softmax = [i / sum_z_exp for i in z_exp]
idx = np.random.choice(POPULATION, POPULATION, p=softmax)
clusters = clusters[idx, :, :]
# print best
max_idx = np.argmax(fitnesses)
print(clusters[max_idx])
print(iteration, max(fitnesses))
# mutation
for i in range(POPULATION):
clusters[i] = mutation(clusters[i])
fitnesses = np.zeros((POPULATION, ))
for i, cluster in enumerate(clusters):
values = {}
first = True
vfunc(cluster, data)
fitnesses[i] = get_fitness(values, clean=True)
max_idx = np.argmax(fitnesses)
print(clusters[max_idx])
print(max(fitnesses))