class genetic_runner: def __init__(self, size, num_of_obstacles=0): self.num_of_obstacles = num_of_obstacles self.size = size self.create_maze() self.generation_min = [] self.generation_avg = [] self.generation_max = [] self.fitness_dict = {} def create_maze(self): allPoints = [(x, y) for x in range(self.size) for y in range(self.size)] sampled = random.sample(allPoints, self.num_of_obstacles + 2) start = sampled[0] destination = sampled[1] obstacles = sampled[2:] self.maze = Maze(size=self.size, start=start, destination=destination, obstacles=obstacles) def run(self): result = self.run_generations() return result def fitness_value(self, chromosome): stats = self.maze.walk_maze(chromosome)[0] cost = stats.steps * STEP_COST + \ REPEAT_COST * stats.repeats + \ stats.disFromDest ** DISTANCE_LEFT_EXPONENTIAL + \ (0 if stats.disFromDest == 0 else UNSOLVED_COST) return 1 / cost def crossover(self, p1, p2): crossover_point = random.randint(0, CHROMOSOME_SIZE) return (p1[:crossover_point] + p2[crossover_point:], p2[:crossover_point] + p1[crossover_point:]) def mutate(self, chromosome): mutate_point = random.randint(0, CHROMOSOME_SIZE - 1) mutation = chromosome[:mutate_point] + random_gene( ) + chromosome[mutate_point + 1:] return mutation def generate_generation(self, population, fitness_array): total_fitness = sum(fitness_array) roulette_array = [fitness / total_fitness for fitness in fitness_array] next_population = population[:ELITE_COUNT] while len(next_population) < POPULATION_IN_GENERATION: c1, c2 = npr.choice(population, 2, p=roulette_array) if random.random() < CROSSOVER_PCT: c1, c2 = self.crossover(c1, c2) if random.random() < MUTATE_PCT: c1, c2 = self.mutate(c1), self.mutate(c2) next_population.extend([c1, c2]) # next_population, next_fitness_array = self.create_fitness_array_and_sort_population(population) self.update_fitness_dict(next_population) next_population.sort(key=lambda x: self.fitness_dict[x], reverse=True) next_fitness_array = [self.fitness_dict[x] for x in next_population] return (next_population, next_fitness_array) def run_generations(self): population, fitness_array = self.initiate_population() self.data_tracking(population, fitness_array) for generation in range(GENERATIONS_NUM): population, fitness_array = self.generate_generation( population, fitness_array) self.data_tracking(population, fitness_array) if self.check_if_convergence(): break return population[0] def check_if_convergence(self): if len(self.generation_max) <= CONVERGENCE_GENERATIONS: return False else: last_generations_max = self.generation_max[ -CONVERGENCE_GENERATIONS:] firstMax = last_generations_max[0] for mx in last_generations_max: if mx != firstMax: return False return True def initiate_population(self): population = random_population() self.update_fitness_dict(population) fitness_array = [self.fitness_dict[x] for x in population] return (population, fitness_array) def data_tracking(self, generation, fitness_array): print(generation[0]) self.generation_max.append(fitness_array[0]) self.generation_avg.append(avg(fitness_array)) self.generation_min.append(fitness_array[POPULATION_IN_GENERATION - 1]) def update_fitness_dict(self, population): for chromosome in population: if chromosome not in self.fitness_dict: self.fitness_dict[chromosome] = self.fitness_value(chromosome) def showGraphs(self): # plotting the line 1 points plt.plot(self.generation_min, label="minimum fitness") plt.plot(self.generation_avg, label="average fitness") plt.plot(self.generation_max, label="maximum fitness") plt.xlabel('x - generation number') plt.ylabel('y - fitness') plt.title( 'fitness of chromosomes in {} generations'.format(GENERATIONS_NUM)) plt.legend() plt.figure()