示例#1
0
def optimisation(N=80, G=100, p_c=0.7, p_m=0.001, log=False, gui=False):

    fit_eval = FitnessEvaluator()

    if log:
        print('Performance optimisation')
        print('------------------------')
        print('Genetic algorithm:')
        print('Population:  N = ', N)
        print('Generations: G = ', G)
        print('Crossover probability: p_c = ', p_c)
        print('Mutation probability:  p_m = ', p_m)

    if gui:
        p_graph.init('Performance profile',
                     f'p_c = {p_c}   p_m = {p_m}   N = {N}   G = {G}')

    # Generates random population
    perf_run = GeneticRun(gc.length(gc.default_gene_format), N)
    if log:
        print('\nGenerated initial population')
        print('Generation: 0\n')

    # Evolution loop
    while perf_run.generation < G:
        perf_run.evolve(fit_eval.performance, p_c, p_m)
        #        if log:
        #            print(f'Generation: {perf_run.generation}')
        #            print(f'Average fitness: {perf_run.record[-1]["avg_fitness"]}')
        #            print(f'Max fitness: {perf_run.record[-1]["max_fitness"]}\n')
        if gui:
            p_graph.update(perf_run.record)

    # Present best specimen so far
    best_specimen_ch = max(perf_run.record,
                           key=lambda x: x['max_fitness'])['fittest_specimen']
    best_specimen = gc.decode(best_specimen_ch)
    distance, fuel, speed = fit_eval.simulate(best_specimen_ch, tries=10)
    if log:
        print('-------------')
        print('Best specimen')
        print('-------------')
        print('Best chromosones:\n', best_specimen_ch)
        print('Best parameters:')
        pp.pprint(best_specimen)
        print('Specimen performance:')
        print(' Distance (m):', distance)
        print(' Fuel use (l):', fuel)
        print(' Speed (km/h):', speed)

    if gui:
        p_graph.join()

    # Return best specimen
    return best_specimen
示例#2
0
    def __init__(self, size, mu, log):
        self.population = []
        self.size = size
        self.lamb = size
        self.mu   = mu
        self.fiteval = FitnessEvaluator(8)
        self.number = 1
        self.log = log

        self.results_folder = "./results"

        self.tournament_rounds = 3
        self.mutation_rate = 0.20
示例#3
0
class Generation(object):
    """
    A generation houses a collection of solutions.
    """
    def __init__(self, size, mu, log):
        self.population = []
        self.size = size
        self.lamb = size
        self.mu   = mu
        self.fiteval = FitnessEvaluator(8)
        self.number = 1
        self.log = log

        self.results_folder = "./results"

        self.tournament_rounds = 3
        self.mutation_rate = 0.20

    def random(self, sourcefile, perturb):
        starting_data = json.load(sourcefile)

        # create random solutions with these parameters
        for i in range(0,self.size):
            self.population.append(Solution(starting_data, self.number, perturb))

    def load_population(self, population):
        self.population = population
    
    def reproduce(self):
        """
        Create mu children, add them into our generation. Those who reproduce are 
        determined by a tournament. The best always get a chance.
        """
        self.log.debug("REPRODUCTION")
        ranked = self.tournament(self.tournament_rounds)
        #self.log.debug(pprint.pformat(ranked))
        children = []
        for l in ranked:
            if len(l) == 0:
                ranked.remove(l)
        for i in range(self.mu):
            parents = []
            for i in range(2):
                # Randomly select from hierarchy, upper has the most chance for individuals
                # as there are fewer members... all hierarchies have equal chance.
                level = random.sample(ranked,1)[0]
                # individuals  are then chosen randomly, weighted by fitness (W:L)
                #parents.append(choice(level, [x.fitness for x in level]))
                # parents chosen randomly
                parents.append(random.sample(level,1)[0])
            children.append(copulate(self.number, *parents))
        
        for child in children:
            if random.random() <= self.mutation_rate:
                child.mutate()

        #plus strategy
        self.population += children

    def tournament(self, rounds):
        """
        Rather than running a tournament size times, I've elected to do a single tournament
        (3 rounds per by default) that then returns a list of ranked individuals.
        This list returned is 2d. Starting with the best, each list contains 2*last
        entries for where contestants were eliminated.
        """
        participants = copy(self.population)
        losers = []
        winners = []

        while len(participants) > 1:
            self.log.debug("\nPARTS " + str(len(participants)) + ": ")
            random.shuffle(participants)
            #self.log.debug(pprint.pformat(participants))
            self.output_statistics()
            args = []
            winners = []

            # pair individuals
            for i in range(0, len(participants), 2):
                if i + 1 >= len(participants):
                    break
                args.append((participants[i], participants[i+1], rounds,))

            # get results, add to losers
            results = self.fiteval.run(args, get_winner)
            for x,a in zip(results, args):
                winners.append(self.handle_winners(x,a))

            # if we have an odd individual, they compete against a random winner
            if len(participants) % 2 != 0:
                opponent = random.sample(winners, 1)[0]
                args = [(opponent,participants[-1],rounds,)]
                result = self.fiteval.run(args, get_winner)
                winner = self.handle_winners(result[0], args[0])

                if winner is not opponent: # they get to be included... no risk to winner
                    winners.append(participants[-1])
            
            losers.append([x for x in participants if x not in winners])

            #self.log.debug("losers:")
            #self.log.debug(pprint.pformat(losers))
            participants = winners

        losers.append(winners)
        losers.reverse()
        return losers

    def handle_winners(self, results, participants):
        """
        Have to handle these on thread.
        We're passed a packed results list... 1 for participants[0] winning, 2 for participants[1]
        and we tally up how that affects each.
        """
        for i in results:
            if i == 1:
                participants[0].handle_win(participants[1])
                participants[0].wins += 1
                participants[1].losses += 1
            else:
                participants[1].handle_win(participants[0])
                participants[1].wins += 1
                participants[0].losses += 1
        if results.count(1) > results.count(2):
            return participants[0]
        else:
            return participants[1]


    def natural_selection(self):
        """
        Determine who to kill off. Currently done by tournament selection.
        """
        newpop = []
        self.log.debug("NATURAL SELECTION")

        # another tournament
        results = self.tournament(self.tournament_rounds)
        self.log.debug("RESULTS: ")
        #self.log.debug(pprint.pformat(results))

        # first 2 winners will not be eliminated
        for x in results[:2]:
            newpop.extend(x)
            for s in x:
                #self.log.debug(s.get_config_file())
                if s in self.population: #this shouldn't be necessary
                    self.population.remove(s)

        #self.log.debug("POP: ")
        #self.log.debug(pprint.pformat(self.population))
        self.population = sorted(self.population, key=lambda p: p.fitness, reverse=True)
        # top 3 ELO, if not winners, will not be eliminated
        for i in range(3):
            newpop.append(self.population.pop()) 

        results = results[2:]

        # sort our levels
        for l in results:
            l = sorted(l, key=lambda p: p.fitness, reverse=True)

        # now randomly pop elements off
        while len(newpop) < self.lamb:
            l = random.sample(results,1)[0]
            newpop.append(l.pop())
            if len(l) == 0:
                results.remove(l)
        self.population = newpop
        
    def every_ten_tournament(self):
        """
        Every 10 generations we have a tournament. It's best out of 7
        and the top 7 are output to results/every10/tournament_gen#.txt
        """
        self.log.debug("\nDetermining Top 7\n")
        ranked = self.tournament(7)
        winners = []
        for x in ranked[:3]:
            winners.extend(x)

        filename = os.path.join("generation_{0}.txt".format(self.number))
        self.output_solutions_to_file(winners, filename)


    def output_solutions_to_file(self, solutions, filename):
        """
        Takes a list of solutions and outputs them to a filename in results/.
        """
        if not os.path.exists(os.path.abspath(self.results_folder)):
            os.mkdir(os.path.abspath(self.results_folder))
        filename = os.path.join(os.path.abspath(self.results_folder),filename)
        with open(filename,'w') as f:
            f.write("Gen\tFit\tWins\tLosses\tTime\n")
            for s in solutions:
                f.write("{0}\t{1}\t{2}\t{3}\t{4}\n    {5}\n".format(s.generation, round(s.fitness), s.wins, s.losses, \
                    round(s.average_time,2), s.get_config_file()))

    
    def output_statistics(self):
        avgtime = 0
        avgfitness = 0
        avggen = 0
        best = None
        for s in self.population:
            if len(s.times) > 0:
                avgtime += s.average_time
            avgfitness += s.fitness
            if best is None or s.fitness > best.fitness:
                best = s
            avggen += s.generation

        avgfitness /= len(self.population)
        avgtime /= len(self.population)
        avggen /= len(self.population)

        print("\nType\tTime\tFit\tGen\tPop/W:L")
        print("Avg\t{0}\t{1}\t{2}\t{3}".format(round(avgtime,2),round(avgfitness,2),round(avggen,2), len(self.population)))
        print("Best\t{0}\t{1}\t{2}\t{3}/{4}".format(round(best.average_time,2),round(best.fitness,2),round(best.generation,2),best.wins,best.losses))

        # Go up 3 lines
        sys.stdout.write("\033[K\033[K\033[K")