Example #1
0
    def __init__(self, algorithm_class, algorithm_config, max_evaluations,
                 trials):
        """ Experiments run a GA several times and report results

        Args:
            algorithm_class (class): class of the GeneticAlgorithm that should be run
            algorithm_config (dict): dictionary of named arguments that will be passed to the algorithm constructor
            max_evaluations (int): the maximum number of fitness evaluations to be performed during each run
            trials (int): number of trials to run

        """
        self.algorithm_class = algorithm_class
        self.algorithm_config = algorithm_config
        self.max_evaluations = max_evaluations
        self.trials = trials
        self.histories = list()

        self.results_directory = storage.get(
            os.path.join(algorithm_class.__name__))
        if not os.path.exists(self.results_directory):
            os.mkdir(self.results_directory)

        self.trials_directory = storage.get(
            os.path.join(algorithm_class.__name__, 'trials'))
        if not os.path.exists(self.trials_directory):
            os.mkdir(self.trials_directory)
Example #2
0
    def seed_population(self, pool_size, time_limit):
        """ Creates a Population using the best runners from a pool of randomly-generated runners

        This selects the best `self.pop_size` Individuals from a pool of randomly generated individuals, using
        distance achieved as the selection criterion.
        If the seeding procedure has already been run, then the individuals will instead be loaded from disk.

        Args:
            pool_size (int): the size of the randomly generated pool from which the initial population will be drawn
            time_limit (int): time limit (in seconds) for each evaluation in the pool

        Returns:
            totter.evolution.Population.Population: Population seeded with good runners

        """
        population_filepath = storage.get(os.path.join(self.__class__.__name__, 'population_seeds'))
        population_file = os.path.join(population_filepath, f'seed_{pool_size}_{self.pop_size}.tsd')

        # if the population has not previously been seeded, then generate the seeded pop
        if not os.path.exists(population_file):
            # temporarily set time limit
            default_time_limit = self.qwop_evaluator.simulator.time_limit
            self.qwop_evaluator.simulator.time_limit = time_limit

            # generate pool of random individuals
            pool = [Individual(self.generate_random_genome()) for i in range(0, pool_size)]
            candidates = list()
            for indv in pool:
                # custom evaluation
                phenotype = self.genome_to_phenotype(indv.genome)
                strategy = QwopStrategy(execution_function=phenotype)
                distance, run_time = self.qwop_evaluator.evaluate(strategy)[0]
                indv.fitness = self.compute_fitness(distance, run_time)
                candidates.append((indv, distance))

            # sort by descending distance run
            sorted_candidates = sorted(candidates, key=lambda c: -c[1])
            # grab the ones who ran farthest
            best_indvs = sorted_candidates[:pool_size]
            best_indvs = list(map(lambda c: c[0], best_indvs))
            # save the individuals found
            with open(population_file, 'wb') as data_file:
                pickle.dump(best_indvs, data_file)

            # reset time limit to its normal value
            self.qwop_evaluator.simulator.time_limit = default_time_limit

        # load best_individuals from a file
        with open(population_file, 'rb') as data_file:
            best_indvs = pickle.load(data_file)

        return Population(best_indvs)
Example #3
0
def main():
    logger = logging.getLogger('totter')
    logger.setLevel(logging.DEBUG)
    logger.addHandler(logging.StreamHandler(stream=sys.stdout))

    genetic_algorithms = dict()
    for algorithm in GeneticAlgorithm.__subclasses__():
        genetic_algorithms[algorithm.__name__] = algorithm

    parser = argparse.ArgumentParser(
        description='Totter: Evolutionary Computing for QWOP',
        argument_default=argparse.SUPPRESS,
    )

    parser.add_argument('--algorithm',
                        default='ExampleGA',
                        type=str,
                        choices=list(genetic_algorithms.keys()),
                        help='The name of the GA that you would like to run.')

    subcommands = parser.add_subparsers()

    # evolution - runs an experiment
    evolve = subcommands.add_parser(
        'evolve',
        argument_default=argparse.SUPPRESS,
        description='Evolve solutions using the selected GA.')
    evolve.set_defaults(action='evolve')
    evolve.add_argument(
        '--trials',
        type=int,
        default=1,
        help='Number of trials to run.  '
        'Each trial will run the GA for the specified number of evaluations.')
    evolve.add_argument(
        '--evaluations',
        type=int,
        default=1000,
        help=
        'Maximum number of fitness evaluations before the algorithm terminates'
    )
    evolve.add_argument(
        '--eval_time_limit',
        type=int,
        default=180,
        help=
        'Maximum time (in seconds) that an individual is allowed to run before the simulation is '
        'killed.')
    evolve.add_argument('--pop_size',
                        type=int,
                        default=30,
                        help='Size of the population')
    evolve.add_argument(
        '--cx_prob',
        type=float,
        default=0.9,
        help='Probability of crossover, expressed as a decimal')
    evolve.add_argument('--mt_prob',
                        type=float,
                        default=0.05,
                        help='Probability of mutation, expressed as a decimal')
    evolve.add_argument(
        '--generational',
        action='store_true',
        help=
        'If set, the GA will run in generational mode instead of steady-state mode'
    )
    evolve.add_argument(
        '--population_seeding_pool',
        type=int,
        default=None,
        help='Size of pool used for seeding the initial population')
    evolve.add_argument(
        '--seeding_time_limit',
        type=int,
        default=60,
        help='The time limit used when constructing the seeded population')

    # population seeding
    seed = subcommands.add_parser(
        'seed',
        argument_default=argparse.SUPPRESS,
        description='Seed the population of the selected algorithm.')
    seed.set_defaults(action='seed')
    seed.add_argument(
        '--pool_size',
        type=int,
        help='Size of the random pool from which seeds will be drawn.')
    seed.add_argument(
        '--pop_size',
        type=int,
        help='Number of individuals to be drawn out of the pool.')

    # simulation
    simulate = subcommands.add_parser(
        'simulate',
        argument_default=argparse.SUPPRESS,
        description='Play the game with the best solution discovered by the GA.'
    )
    simulate.set_defaults(action='simulate')
    simulate.add_argument('--saved_result',
                          type=str,
                          help='Path to the results file that should be used')

    args = parser.parse_args()
    args = vars(args)

    # every subparser has an action arg specifying which action to perform
    action = args.pop('action')
    # `args.algorithm` will be the name of one of the GA subclasses
    algorithm_name = args.pop('algorithm')
    algorithm_class = genetic_algorithms[algorithm_name]

    if action == 'evolve':
        evolution_config = {
            'eval_time_limit': args['eval_time_limit'],
            'pop_size': args['pop_size'],
            'cx_prob': args['cx_prob'],
            'mt_prob': args['mt_prob'],
            'steady_state': False if 'generational' in args else True,
            'population_seeding_pool': args['population_seeding_pool'],
            'seeding_time_limit': args['seeding_time_limit'],
        }
        evaluations = args['evaluations']
        trials = args['trials']
        logger.info(
            f'Running GA {algorithm_name} for {trials} trials with config:\n{evolution_config}'
        )

        # setup the experiment
        experiment = Experiment(algorithm_class, evolution_config, evaluations,
                                trials)
        output_directory = experiment.run()

        # report results and save state
        logger.info(
            f'Evolution completed.\n Results saved to: {output_directory}')

    elif action == 'seed':
        pool_size = args['pool_size'] if 'pool_size' in args else 500
        pop_size = args['pop_size'] if 'pop_size' in args else 30
        logger.info(
            f'Seeding algorithm {algorithm_class.__name__} '
            f'using pool size {pool_size} and population size {pop_size}')
        algorithm = algorithm_class(pop_size=pop_size,
                                    population_seeding_pool=pool_size)
        logger.info('Done.')

    elif action == 'simulate':
        if 'saved_result' in args:
            filepath = args['saved_result']
        else:
            filepath = storage.get(
                os.path.join(algorithm_class.__name__, 'solution.json'))

        if not os.path.exists(filepath):
            # if there is no results file, then we should quit
            logging.error(
                f'No results found for algorithm {algorithm_name} at {filepath}.\n'
                f'Make sure you run evolve before using the simulate command.')
        else:
            # run the simulation
            with open(filepath, 'r') as results_file:
                data = json.load(results_file)

            best_genome = data['best_genome']
            algorithm = algorithm_class(
                pop_size=1)  # we just need a shell to get the execute method
            strategy = QwopStrategy(
                execution_function=algorithm.genome_to_phenotype(best_genome))
            simulator = QwopSimulator(
                time_limit=600)  # TODO: time limit is rather arbitrary
            simulator.simulate(strategy, qwop_started=True)
            stop_qwop()