def pclone(self, cities, particle, a): target = self.path[a] b = particle.maping[target] # assert b == list(particle.path).index(target) if abs(a - b) < 2 or abs(a - b) == len(self.path) - 1: return src, dst = [a, b], [b, a] cost_src = cost(cities, self.path, src, src) cost_dst = cost(cities, self.path, src, dst) return self.mutate(cities, src, dst, cost_src, cost_dst)
def generate_paretos(self): already_visited = [] CURRENT = self.evaluator.get_paretos_instances() PREVIOUS = [] while self.ratio[0] <= 1.0: print 'Looking for ratios: %s' % (' '.join(map(str, self.ratio))) PREVIOUS = deepcopy(CURRENT) cnt = 0 n = len(self.evaluator.get_paretos_instances()) to_copy = random.choice(self.evaluator.get_paretos_instances()) while to_copy in already_visited: cnt += 1 if cnt > n: break to_copy = random.choice(self.evaluator.get_paretos_instances()) already_visited.append(to_copy) ct = tsp.cost(to_copy, self.objectives) stop = False while not stop: best_neighbor = None best_cost = float('inf') # best neighbor for i in xrange(len(self.instance)): for j in xrange(i+1, len(self.instance)): neighbor = deepcopy(to_copy) neighborhood.swap(neighbor, i, j) c = tsp.cost(neighbor, self.objectives) cr = self.proportional_score(c) if cr < best_cost: best_neighbor = neighbor best_cost = cr if best_cost < self.proportional_score(ct): to_copy = best_neighbor ct = tsp.cost(to_copy, self.objectives) else: stop = True self.evaluator.update(to_copy, tuple(ct.values())) CURRENT = self.evaluator.get_paretos_instances() with open('results.txt', 'w') as f: for res in CURRENT: if res: f.write(';'.join(map(str, res))) f.write('\n') self.update_ratios() yield self.evaluator.get_paretos_costs()
def mutation(cities, population, a): pop_m = population[a].copy() idx = mutate(pop_m, degree=random.randint(2, MUT_DEGREE)) mutation = idx.copy() np.random.shuffle(mutation) cost_x = cost(cities, pop_m, idx, idx) cost_y = cost(cities, pop_m, idx, mutation) cost_m = fitness[a] - (cost_x - cost_y) pop_m[idx] = pop_m[mutation] # cost_w = cost(cities, pop_m, *[range(0, len(pop_m), 2)]*2) # print(cost_m, cost_w) # assert cost_m.round() == cost_w.round() return pop_m, cost_m
def run(self, instance, objectives, budget=100, best=None, plot=False): self.N = 0 self.budget = budget self.best = best self.instance = instance self.objectives = objectives c = tsp.cost(self.instance, self.objectives) self.evaluator.update(self.instance, tuple(c.values())) if plot: fig, ax = plt.subplots() if self.best: line, = ax.plot([o for o,_ in self.best], [o for _,o in self.best], 'g.') self.line, = ax.plot([], [], 'r.') ani = animation.FuncAnimation(fig, self.update, self.generate_paretos) plt.show() else: gen = self.generate_paretos() try: while True: gen.next() except StopIteration: pass return self.evaluator
def generate_paretos(self): already_visited = [] CURRENT = self.evaluator.get_paretos_instances() PREVIOUS = [] while self.N < self.budget: self.N += 1 PREVIOUS = deepcopy(CURRENT) to_copy = random.choice(self.evaluator.get_paretos_instances()) while to_copy in already_visited: to_copy = random.choice(self.evaluator.get_paretos_instances()) already_visited.append(to_copy) for i in xrange(len(self.instance)): for j in xrange(i+1, len(self.instance)): neighbor = deepcopy(to_copy) neighborhood.swap(neighbor, i, j) c = tsp.cost(neighbor, self.objectives) self.evaluator.update(neighbor, tuple(c.values())) self.evaluator.clean_dominated() CURRENT = self.evaluator.get_paretos_instances() with open('results.txt', 'w') as f: for res in CURRENT: if res: f.write(';'.join(map(str, res))) f.write('\n') yield self.evaluator.get_paretos_costs()
def run(self, instance, objectives, step=0.1, best=None, plot=False): self.step = step self.best = best self.instance = instance self.objectives = objectives c = tsp.cost(self.instance, self.objectives) self.evaluator.update(self.instance, tuple(c.values())) n = len(objectives) for i in xrange(n): self.ratio.append(i / (n - 1.0)) if plot: fig, ax = plt.subplots() if self.best: line, = ax.plot([o for o,_ in self.best], [o for _,o in self.best], 'g.') self.line, = ax.plot([], [], 'r.') ani = animation.FuncAnimation(fig, self.update, self.generate_paretos) plt.show() else: gen = self.generate_paretos() try: while True: gen.next() except StopIteration: pass return self.evaluator
def pclone(cities, population, a, b): for _ in range(len(cities) * 2): a_i = random.randint(0, len(cities) - 1) if population[a][a_i] == population[b][a_i]: continue pop_c = population[a].copy() target = pop_c[a_i] b_i = list(population[b]).index(target) if abs(a_i - b_i) < 2 or abs(a_i - b_i) == len(pop_c) - 1: continue src, dst = [a_i, b_i], [b_i, a_i] cost_src = cost(cities, pop_c, src, src) cost_dst = cost(cities, pop_c, src, dst) cost_c = fitness[a] - (cost_src - cost_dst) pop_c[src] = pop_c[dst] return pop_c, cost_c return None, None
def _backprop(self): self.particles.sort(key=lambda p: p.error) # skip outliers e_min, e_max = self.particles[3].error, self.particles[-4].error for i, p in enumerate(self.particles): # velocity like NN momentum velocity = self._proportional_distance_velocity( e_min, e_max, p.error, i) p.error_degree = int(p.optimizer.step(velocity * (e_max - e_min))) error_degree = 1 + p.error_degree #min(len(p.path)**2, p.error_degree) leader = random.randint( 0, 0 if i < ELITE else ELITE) #i // 4)#random.randint(0, ELITE)# for j in range(error_degree): self.total += 1 # backprop idx = random.randint(0, len(p.path) - 1) if self.lrg * velocity > random.random(): if self.particles[0].best.path[idx] != p.path[idx]: p.pclone(self.cities, self.particles[leader], idx) idx = random.randint(0, len(p.path) - 1) if self.lrp * velocity > random.random(): if p.best.path[idx] != p.path[idx]: p.pclone(self.cities, p.best, idx) exploration = 2 > random.randint(0, error_degree) if not exploration: continue # best we want less to improvize src = mutate(p.path, degree=2) dst = [src[1], src[0]] # noise -> out of local minima t = self.temperature[p.cooldown()] cost_src = cost(self.cities, p.path, src, src) cost_dst = cost(self.cities, p.path, src, dst) if np.exp((cost_src - cost_dst) / t) > random.random(): p.mutate(self.cities, src, dst, cost_src, cost_dst) return np.mean([p.error for p in self.particles])
def crossover(cities, population, a, b): pop_a, pop_b = population[a], population[b] a, b = sorted(random.sample(range(len(pop_a)), 2)) # a = 0 gene = list(pop_a[a:b]) pop_c = list(filter(lambda g: g not in gene, pop_b)) a = random.randint(0, len(pop_c) - 1) pop_c = np.array(pop_c[:a] + gene + pop_c[a:]).reshape(-1) cost_c = cost(cities, pop_c, *[range(0, len(pop_c), 2)] * 2) return pop_c, cost_c
def __init__(self, cities, optimizer, temperature): out_size = len(cities) assert 0 == out_size % 2, "current cost function accepts only odd #cities" self.optimizer = optimizer self.path = np.random.choice(np.arange(out_size), out_size, replace=False) self.error = cost(cities, self.path, *[range(0, len(self.path), 2)] * 2) self.maping = list(np.argsort(self.path)) self.temp = temperature self.best = None self.best = copy.copy(self) self.error_degree = None
# https://ericphanson.com/blog/2016/the-traveling-salesman-and-10-lines-of-python/ # *also 10 lines of code (w/o comments, empty lines + merge 'if' into one line ) # but with usage of tsp 'library' :) ~ simulated annealing logic is all here. import random import numpy as np from tsp import tsp_map, mutate, cost, plot cities = tsp_map(n_cities=20, scale=1000) path = np.random.choice(np.arange(len(cities)), len(cities), replace=False) # we cut half of logspace as first/second half is bit brutal for temperature for temperature in reversed(np.logspace(0, 3, 1e5)[:int(1e5 / 2)]): a, b = mutate(path, degree=2) # interesting trick: we dont need to checkpoint best path !! # as if new path better np.exp(-..) is low, best path will survive most likely if np.exp( (cost(cities, path, [a, b], [a, b]) - cost(cities, path, [a, b], [b, a])) / temperature) > random.random(): path[[a, b]] = path[[b, a]] plot(cities, path)
]) # for idx in trails[:ELITE_POOL, len(visited)]: # assert diffs[int(mu), int(idx)] == list(neighbours[int(mu)]).index(int(idx)) # heuristic, if 2 possible soutions we dont want to oscilate sigma.sort() return sigma[1:len(sigma) // 2 + 1].mean() scores = [] trails = np.vstack([ np.random.choice(range(len(cities)), size=len(cities), replace=False) for _ in range(EVAPORATION_POOL + COUNT) ]) total_cost = np.array( [cost(cities, trail, *[range(0, len(trail), 2)] * 2) for trail in trails]) print("INITIAL SCORE", np.mean(total_cost)) for i in range(200): # * EVAPORATION_POOL // COUNT): var = 0 for c in range(COUNT): score = 0 visited = [] stats = [] trail = trails[random.randint(0, ELITE_POOL - 1)] #EVAPORATION_POOL)]# while len(visited) != len(cities): mu = trail[len(visited)] # not actually same as learning rate,
COUNT = 100 MUT_DEGREE = 3 #8# BOUT_SIZE = 7 TOURNAMENT_COUNT = 10 ELITE = 10 cities = tsp_map(24, 1000) population = [ np.random.choice(np.arange(len(cities)), len(cities), replace=False) for _ in range(COUNT) ] fitness = [ cost(cities, path, *[range(0, len(path), 2)] * 2) for path in population ] def crossover(cities, population, a, b): pop_a, pop_b = population[a], population[b] a, b = sorted(random.sample(range(len(pop_a)), 2)) # a = 0 gene = list(pop_a[a:b]) pop_c = list(filter(lambda g: g not in gene, pop_b)) a = random.randint(0, len(pop_c) - 1) pop_c = np.array(pop_c[:a] + gene + pop_c[a:]).reshape(-1) cost_c = cost(cities, pop_c, *[range(0, len(pop_c), 2)] * 2) return pop_c, cost_c