예제 #1
0
    def __init__(self, traj,
                 optimizee_create_individual,
                 optimizee_fitness_weights,
                 parameters,
                 optimizee_bounding_func=None):
        super().__init__(traj, optimizee_create_individual=optimizee_create_individual,
                         optimizee_fitness_weights=optimizee_fitness_weights, parameters=parameters,
                         optimizee_bounding_func=optimizee_bounding_func)

        self.best_individual = None
        self.best_fitness = None

        sample_individual = self.optimizee_create_individual()

        # Generate parameter dictionary based on optimizee_param_grid
        self.param_list = {}
        _, optimizee_individual_param_spec = dict_to_list(sample_individual, get_dict_spec=True)
        self.optimizee_individual_dict_spec = optimizee_individual_param_spec

        optimizee_param_grid = parameters.param_grid
        # Assert validity of optimizee_param_grid
        assert set(sample_individual.keys()) == set(optimizee_param_grid.keys()), \
            "The Parameters of optimizee_param_grid don't match those of the optimizee individual"

        for param_name, param_type, param_length in optimizee_individual_param_spec:
            param_lower_bound, param_upper_bound, param_n_steps = optimizee_param_grid[param_name]
            if param_type == DictEntryType.Scalar:
                self.param_list[param_name] = np.linspace(param_lower_bound, param_upper_bound, param_n_steps + 1)
            elif param_type == DictEntryType.Sequence:
                curr_param_list = np.linspace(param_lower_bound, param_upper_bound, param_n_steps + 1)
                curr_param_list = np.meshgrid(*([curr_param_list] * param_length), indexing='ij')
                curr_param_list = [x.ravel() for x in curr_param_list]
                curr_param_list = np.stack(curr_param_list, axis=-1)
                self.param_list[param_name] = curr_param_list

        self.param_list = cartesian_product(self.param_list, tuple(sorted(optimizee_param_grid.keys())))
        self.size = len(self.param_list[list(self.param_list.keys())[0]])

        # Adding the bounds information to the trajectory
        traj.f_add_parameter_group('grid_spec')
        for param_name, param_grid_spec in optimizee_param_grid.items():
            traj.grid_spec.f_add_parameter(param_name + '.lower_bound', param_grid_spec[0])
            traj.grid_spec.f_add_parameter(param_name + '.uper_bound', param_grid_spec[1])
        traj.f_add_parameter('n_iteration', 1, comment='Grid search does only 1 iteration')
        #: The current generation number
        self.g = 0
        # Expanding the trajectory
        grouped_params_dict = {'individual.' + key: value for key, value in self.param_list.items()}
        final_params_dict = {'generation': [self.g],
                             'ind_idx': range(self.size)}
        final_params_dict.update(grouped_params_dict)
        traj.f_expand(cartesian_product(final_params_dict,
                                        [('ind_idx',) + tuple(grouped_params_dict.keys()), 'generation']))

        #: The population (i.e. list of individuals) to be evaluated at the next iteration
        self.eval_pop = None
예제 #2
0
 def bounding_wrapper(*args, **kwargs):
     if self.optimizee_bounding_func is None:
         return func(*args, **kwargs)
     else:
         # Deap Functions modify individuals in-place, Hence we must do the same
         result_individuals_deap = func(*args, **kwargs)
         result_individuals = [list_to_dict(x, self.optimizee_individual_dict_spec)
                               for x in result_individuals_deap]
         bounded_individuals = [self.optimizee_bounding_func(x) for x in result_individuals]
         for i, deap_indiv in enumerate(result_individuals_deap):
             deap_indiv[:] = dict_to_list(bounded_individuals[i])
         # print("Bounded Individual: {}".format(bounded_individuals))
         return result_individuals_deap
예제 #3
0
파일: optimizer.py 프로젝트: w-klijn/L2L
    def post_process(self, traj, fitnesses_results):
        """
        See :meth:`~l2l.optimizers.optimizer.Optimizer.post_process`
        """
        noisy_step, temp_decay, n_iteration, stop_criterion = \
            traj.noisy_step, traj.temp_decay, traj.n_iteration, traj.stop_criterion
        old_eval_pop = self.eval_pop.copy()
        self.eval_pop.clear()
        temperature = self.T
        temperature_end = 0
        self.T = self.cooling(temperature, self.cooling_schedule, temp_decay,
                              temperature_end, n_iteration)
        logger.info("  Evaluating %i individuals" % len(fitnesses_results))

        assert len(fitnesses_results) == traj.n_parallel_runs
        weighted_fitness_list = []
        for i, (run_index, fitness) in enumerate(fitnesses_results):

            weighted_fitness = sum(
                f * w for f, w in zip(fitness, self.optimizee_fitness_weights))
            weighted_fitness_list.append(weighted_fitness)

            # We need to convert the current run index into an ind_idx
            # (index of individual within one generation)
            traj.v_idx = run_index
            ind_index = traj.par.ind_idx
            individual = old_eval_pop[ind_index]

            # Accept or reject the new solution
            current_fitness_value_i = self.current_fitness_value_list[i]
            r = self.random_state.rand()
            p = np.exp((weighted_fitness - current_fitness_value_i) / self.T)

            # Accept
            if r < p or weighted_fitness >= current_fitness_value_i:
                self.current_fitness_value_list[i] = weighted_fitness
                self.current_individual_list[i] = np.array(
                    dict_to_list(individual))

            traj.f_add_result('$set.$.individual', individual)
            # Watchout! if weighted fitness is a tuple/np array it should be converted to a list first here
            traj.f_add_result('$set.$.fitness', weighted_fitness)

            current_individual = self.current_individual_list[i]
            new_individual = list_to_dict(
                current_individual +
                self.random_state.randn(current_individual.size) * noisy_step *
                self.T, self.optimizee_individual_dict_spec)
            if self.optimizee_bounding_func is not None:
                new_individual = self.optimizee_bounding_func(new_individual)

            logger.debug(
                "Current best fitness for individual %d is %.2f. New individual is %s",
                i, self.current_fitness_value_list[i], new_individual)
            self.eval_pop.append(new_individual)

        logger.debug("Current best fitness within population is %.2f",
                     max(self.current_fitness_value_list))

        traj.v_idx = -1  # set the trajectory back to default
        logger.info("-- End of generation {} --".format(self.g))

        # ------- Create the next generation by crossover and mutation -------- #
        # not necessary for the last generation
        if self.g < n_iteration - 1 and stop_criterion > max(
                self.current_fitness_value_list):
            fitnesses_results.clear()
            self.g += 1  # Update generation counter
            self._expand_trajectory(traj)
예제 #4
0
파일: optimizer.py 프로젝트: w-klijn/L2L
    def __init__(self,
                 traj,
                 optimizee_create_individual,
                 optimizee_fitness_weights,
                 parameters,
                 optimizee_bounding_func=None):
        super().__init__(
            traj,
            optimizee_create_individual=optimizee_create_individual,
            optimizee_fitness_weights=optimizee_fitness_weights,
            parameters=parameters,
            optimizee_bounding_func=optimizee_bounding_func)

        self.optimizee_bounding_func = optimizee_bounding_func

        # The following parameters are recorded
        traj.f_add_parameter(
            'n_parallel_runs',
            parameters.n_parallel_runs,
            comment=
            'Number of parallel simulated annealing runs / Size of Population')
        traj.f_add_parameter('noisy_step',
                             parameters.noisy_step,
                             comment='Size of the random step')
        traj.f_add_parameter(
            'temp_decay',
            parameters.temp_decay,
            comment='A temperature decay parameter (multiplicative)')
        traj.f_add_parameter('n_iteration',
                             parameters.n_iteration,
                             comment='Number of iteration to perform')
        traj.f_add_parameter('stop_criterion',
                             parameters.stop_criterion,
                             comment='Stopping criterion parameter')
        traj.f_add_parameter('seed',
                             np.uint32(parameters.seed),
                             comment='Seed for RNG')

        _, self.optimizee_individual_dict_spec = dict_to_list(
            self.optimizee_create_individual(), get_dict_spec=True)

        # Note that this array stores individuals as an np.array of floats as opposed to Individual-Dicts
        # This is because this array is used within the context of the simulated annealing algorithm and
        # Thus needs to handle the optimizee individuals as vectors
        self.current_individual_list = [
            np.array(dict_to_list(self.optimizee_create_individual()))
            for _ in range(parameters.n_parallel_runs)
        ]
        self.random_state = np.random.RandomState(parameters.seed)

        # The following parameters are NOT recorded
        self.T = 1.  # Initialize temperature
        self.g = 0  # the current generation

        # Keep track of current fitness value to decide whether we want the next individual to be accepted or not
        self.current_fitness_value_list = [-np.Inf
                                           ] * parameters.n_parallel_runs

        new_individual_list = [
            list_to_dict(
                ind_as_list + self.random_state.normal(
                    0.0, parameters.noisy_step, ind_as_list.size) *
                traj.noisy_step * self.T, self.optimizee_individual_dict_spec)
            for ind_as_list in self.current_individual_list
        ]
        if optimizee_bounding_func is not None:
            new_individual_list = [
                self.optimizee_bounding_func(ind)
                for ind in new_individual_list
            ]

        self.eval_pop = new_individual_list
        self._expand_trajectory(traj)

        self.cooling_schedule = parameters.cooling_schedule
예제 #5
0
    def post_process(self, traj, fitnesses_results):
        """
        See :meth:`~l2l.optimizers.optimizer.Optimizer.post_process`
        """

        n_iteration, smoothing, temp_decay = \
            traj.n_iteration, traj.smoothing, traj.temp_decay
        stop_criterion, n_elite = traj.stop_criterion, traj.n_elite

        weighted_fitness_list = []
        #**************************************************************************************************************
        # Storing run-information in the trajectory
        # Reading fitnesses and performing distribution update
        #**************************************************************************************************************
        for run_index, fitness in fitnesses_results:
            # We need to convert the current run index into an ind_idx
            # (index of individual within one generation)
            traj.v_idx = run_index
            ind_index = traj.par.ind_idx

            traj.f_add_result('$set.$.individual', self.eval_pop[ind_index])
            traj.f_add_result('$set.$.fitness', fitness)

            weighted_fitness_list.append(np.dot(fitness, self.optimizee_fitness_weights))
        traj.v_idx = -1  # set trajectory back to default

        weighted_fitness_list = np.array(weighted_fitness_list).ravel()

        # Performs descending arg-sort of weighted fitness
        fitness_sorting_indices = list(reversed(np.argsort(weighted_fitness_list)))

        # Sorting the data according to fitness
        sorted_population = self.eval_pop_asarray[fitness_sorting_indices]
        sorted_fitness = np.asarray(weighted_fitness_list)[fitness_sorting_indices]

        # Elite individuals are with performance better than or equal to the (1-rho) quantile.
        # See original describtion of cross entropy for optimization
        elite_individuals = sorted_population[:n_elite]

        self.best_individual_in_run = sorted_population[0]
        self.best_fitness_in_run = sorted_fitness[0]
        self.gamma = sorted_fitness[n_elite - 1]

        logger.info("-- End of generation %d --", self.g)
        logger.info("  Evaluated %d individuals", len(fitnesses_results))
        logger.info('  Best Fitness: %.4f', self.best_fitness_in_run)
        logger.info('  Average Fitness: %.4f', np.mean(sorted_fitness))
        logger.debug('  Calculated gamma: %.4f', self.gamma)

        #**************************************************************************************************************
        # Storing Generation Parameters / Results in the trajectory
        #**************************************************************************************************************
        # These entries correspond to the generation that has been simulated prior to this post-processing run

        # Documentation of algorithm parameters for the current generation
        # 
        # generation          - The index of the evaluated generation
        # gamma               - The fitness threshold inferred from the evaluated  generation
        #                       (This is used in sampling the next generation)
        # T                   - Temperature used to select non-elite elements among the individuals
        #                       of the evaluated generation
        # best_fitness_in_run - The highest fitness among the individuals in the
        #                       evaluated generation
        # pop_size            - Population size
        generation_result_dict = {
            'generation': self.g,
            'gamma': self.gamma,
            'T': self.T,
            'best_fitness_in_run': self.best_fitness_in_run,
            'average_fitness_in_run': np.mean(sorted_fitness),
            'pop_size': self.pop_size
        }

        generation_name = 'generation_{}'.format(self.g)
        traj.results.generation_params.f_add_result_group(generation_name)
        traj.results.generation_params.f_add_result(
            generation_name + '.algorithm_params', generation_result_dict,
            comment="These are the parameters that correspond to the algorithm, look at the source code"
                    " for `CrossEntropyOptimizer::post_process()` for comments documenting these"
                    " parameters")

        # new distribution fit
        individuals_to_be_fitted = elite_individuals

        # Temperature dependent sampling of non elite individuals
        if temp_decay > 0:
            # Keeping non-elite samples with certain probability dependent on temperature (like Simulated Annealing)
            non_elite_selection_probs = np.clip(np.exp((weighted_fitness_list[n_elite:] - self.gamma) / self.T),
                                                a_min=0.0, a_max=1.0)
            non_elite_selected_indices = self.random_state.binomial(1, non_elite_selection_probs).astype(bool)
            non_elite_eval_pop_asarray = sorted_population[n_elite:][non_elite_selected_indices]
            individuals_to_be_fitted = np.concatenate((elite_individuals, non_elite_eval_pop_asarray))

        # Fitting New distribution parameters.
        self.distribution_results = self.current_distribution.fit(individuals_to_be_fitted, smoothing)

        #Add the results of the distribution fitting to the trajectory
        traj.results.generation_params.f_add_result(
            generation_name + '.distribution_params', self.distribution_results,
            comment="These are the parameters of the distribution inferred from the currently evaluated"
                    " generation")

        #**************************************************************************************************************
        # Create the next generation by sampling the inferred distribution
        #**************************************************************************************************************
        # Note that this is only done in case the evaluated run is not the last run
        fitnesses_results.clear()
        self.eval_pop.clear()

        # check if to stop
        if self.g < n_iteration - 1 and self.best_fitness_in_run < stop_criterion:
            #Sample from the constructed distribution
            self.eval_pop_asarray = self.current_distribution.sample(self.pop_size)
            self.eval_pop = [list_to_dict(ind_asarray, self.optimizee_individual_dict_spec)
                             for ind_asarray in self.eval_pop_asarray]
            # Clip to boundaries
            if self.optimizee_bounding_func is not None:
                self.eval_pop = [self.optimizee_bounding_func(individual) for individual in self.eval_pop]
                self.eval_pop_asarray = np.array([dict_to_list(x) for x in self.eval_pop])
            self.g += 1  # Update generation counter
            self.T *= temp_decay
            self._expand_trajectory(traj)
예제 #6
0
    def __init__(self,
                 traj,
                 optimizee_create_individual,
                 optimizee_fitness_weights,
                 parameters,
                 optimizee_bounding_func=None):

        super().__init__(
            traj,
            optimizee_create_individual=optimizee_create_individual,
            optimizee_fitness_weights=optimizee_fitness_weights,
            parameters=parameters,
            optimizee_bounding_func=optimizee_bounding_func)
        self.optimizee_bounding_func = optimizee_bounding_func
        __, self.optimizee_individual_dict_spec = dict_to_list(
            optimizee_create_individual(), get_dict_spec=True)

        traj.f_add_parameter('seed', parameters.seed, comment='Seed for RNG')
        traj.f_add_parameter('popsize',
                             parameters.popsize,
                             comment='Population size')  # 185
        traj.f_add_parameter('CXPB', parameters.CXPB, comment='Crossover term')
        traj.f_add_parameter('MUTPB',
                             parameters.MUTPB,
                             comment='Mutation probability')
        traj.f_add_parameter('n_iteration',
                             parameters.NGEN,
                             comment='Number of generations')

        traj.f_add_parameter('indpb',
                             parameters.indpb,
                             comment='Mutation parameter')
        traj.f_add_parameter('tournsize',
                             parameters.tournsize,
                             comment='Selection parameter')

        # ------- Create and register functions with DEAP ------- #
        # delay_rate, slope, std_err, max_fraction_active
        creator.create("FitnessMax",
                       base.Fitness,
                       weights=self.optimizee_fitness_weights)
        creator.create("Individual", list, fitness=creator.FitnessMax)

        toolbox = base.Toolbox()
        # Structure initializers
        toolbox.register("individual", tools.initIterate, creator.Individual,
                         lambda: dict_to_list(optimizee_create_individual()))
        toolbox.register("population", tools.initRepeat, list,
                         toolbox.individual)

        # Operator registering
        # This complex piece of code is only necessary because we're using the
        # DEAP framework and would like to decorate the DEAP mutation operator
        def bounding_decorator(func):
            def bounding_wrapper(*args, **kwargs):
                if self.optimizee_bounding_func is None:
                    return func(*args, **kwargs)
                else:
                    # Deap Functions modify individuals in-place, Hence we must do the same
                    result_individuals_deap = func(*args, **kwargs)
                    result_individuals = [
                        list_to_dict(x, self.optimizee_individual_dict_spec)
                        for x in result_individuals_deap
                    ]
                    bounded_individuals = [
                        self.optimizee_bounding_func(x)
                        for x in result_individuals
                    ]
                    for i, deap_indiv in enumerate(result_individuals_deap):
                        deap_indiv[:] = dict_to_list(bounded_individuals[i])
                    print("Bounded Individual: {}".format(bounded_individuals))
                    return result_individuals_deap

            return bounding_wrapper

        toolbox.register("mate", tools.cxBlend, alpha=parameters.matepar)
        toolbox.decorate("mate", bounding_decorator)
        toolbox.register("mutate",
                         tools.mutGaussian,
                         mu=0,
                         sigma=parameters.mutpar,
                         indpb=traj.indpb)
        toolbox.decorate("mutate", bounding_decorator)
        toolbox.register("select",
                         tools.selTournament,
                         tournsize=traj.tournsize)

        # ------- Initialize Population and Trajectory -------- #
        # NOTE: The Individual object implements the list interface.
        self.pop = toolbox.population(n=traj.popsize)
        self.eval_pop_inds = [ind for ind in self.pop if not ind.fitness.valid]
        self.eval_pop = [
            list_to_dict(ind, self.optimizee_individual_dict_spec)
            for ind in self.eval_pop_inds
        ]

        self.g = 0  # the current generation
        self.toolbox = toolbox  # the DEAP toolbox
        self.hall_of_fame = HallOfFame(20)

        self._expand_trajectory(traj)
예제 #7
0
def main():

    name = "FIT-TEMPS"
    root_dir_path = os.path.dirname(os.path.abspath(sys.argv[0]))

    paths = Paths(name, dict(run_num="test"), root_dir_path=root_dir_path)

    print("All output logs can be found in directory ", paths.logs_path)

    traj_file = os.path.join(paths.output_dir_path, "data.h5")
    print(traj_file)
    os.makedirs(paths.output_dir_path, exist_ok=True)
    print("Trajectory file is: {}".format(traj_file))

    trajectories = load_last_trajs(
        os.path.join(paths.output_dir_path, 'per_gen_trajectories'))
    if len(trajectories):
        k = trajectories['generation']
        traj = trajectories[k]
    else:
        traj = name

    # Create an environment that handles running our simulation
    # This initializes an environment
    env = Environment(
        trajectory=traj,
        filename=traj_file,
        file_title="{} data".format(name),
        comment="{} data".format(name),
        add_time=bool(1),
        automatic_storing=bool(1),
        log_stdout=bool(0),  # Sends stdout to logs
        multiprocessing=MULTIPROCESSING,
    )
    create_shared_logger_data(logger_names=["bin", "optimizers"],
                              log_levels=["INFO", "INFO"],
                              log_to_consoles=[True, True],
                              sim_name=name,
                              log_directory=paths.logs_path)
    configure_loggers()

    # trajectories = load_last_trajs(os.path.join(paths.root_dir_path,'trajectories'))

    # env.trajectory.individuals[0] = trajectories

    # Get the trajectory from the environment
    traj = env.trajectory

    if len(trajectories) == 0:

        # Set JUBE params
        traj.f_add_parameter_group("JUBE_params", "Contains JUBE parameters")

        # Scheduler parameters
        # Name of the scheduler
        # traj.f_add_parameter_to_group("JUBE_params", "scheduler", "Slurm")

        # Command to submit jobs to the schedulers
        # traj.f_add_parameter_to_group("JUBE_params", "submit_cmd", "sbatch")

        # Template file for the particular scheduler
        traj.f_add_parameter_to_group("JUBE_params", "job_file", "job.run")
        # Number of nodes to request for each run
        traj.f_add_parameter_to_group("JUBE_params", "nodes", "1")
        # Requested time for the compute resources
        traj.f_add_parameter_to_group("JUBE_params", "walltime", "00:10:00")
        # MPI Processes per node
        traj.f_add_parameter_to_group("JUBE_params", "ppn", "1")
        # CPU cores per MPI process
        traj.f_add_parameter_to_group("JUBE_params", "cpu_pp", "1")
        # Threads per process
        traj.f_add_parameter_to_group("JUBE_params", "threads_pp", "1")
        # Type of emails to be sent from the scheduler
        traj.f_add_parameter_to_group("JUBE_params", "mail_mode", "ALL")
        # Email to notify events from the scheduler
        traj.f_add_parameter_to_group("JUBE_params", "mail_address",
                                      "*****@*****.**")
        # Error file for the job
        traj.f_add_parameter_to_group("JUBE_params", "err_file", "stderr")
        # Output file for the job
        traj.f_add_parameter_to_group("JUBE_params", "out_file", "stdout")
        # JUBE parameters for multiprocessing. Relevant even without scheduler.
        # MPI Processes per job
        traj.f_add_parameter_to_group("JUBE_params", "tasks_per_job", "1")

        # The execution command
        run_filename = os.path.join(paths.root_dir_path,
                                    "run_files/run_optimizee.py")
        command = "python3 {}".format(run_filename)
        if ON_JEWELS and not USE_MPI:
            # -N num nodes
            # -t exec time (mins)
            # -n num sub-procs
            command = "srun -t 15 -N 1 -n 4 -c 1 --gres=gpu:1 {}".format(
                command)
        elif USE_MPI:
            command = "MPIEXEC_TIMEOUT={} mpiexec -bind-to socket -np 1 {}".format(
                60, command)

        traj.f_add_parameter_to_group("JUBE_params", "exec", command)

        # Ready file for a generation
        traj.f_add_parameter_to_group(
            "JUBE_params", "ready_file",
            os.path.join(paths.root_dir_path, "readyfiles/ready_w_"))
        # Path where the job will be executed
        traj.f_add_parameter_to_group("JUBE_params", "work_path",
                                      paths.root_dir_path)

        ### Maybe we should pass the Paths object to avoid defining paths here and there
        traj.f_add_parameter_to_group("JUBE_params", "paths_obj", paths)

        csv = open('./temperature/temperature-anomaly.csv', 'r')
        temps = []
        years = []
        for i, line in enumerate(csv):
            sp = line.split(',')
            if sp[0] == 'Global':
                temps.append(float(sp[3]))
                years.append(float(sp[2]))
            if sp[0].startswith('Northern'):
                break
        csv.close()

        traj.f_add_parameter_group("simulation", "Contains JUBE parameters")
        traj.f_add_parameter_to_group("simulation", 'target', temps)  # ms
        traj.f_add_parameter_to_group("simulation", 'years', years)

    ## Innerloop simulator
    optimizee = FitOptimizee(traj, 1234)

    # Prepare optimizee for jube runs
    JUBE_runner.prepare_optimizee(optimizee, paths.root_dir_path)

    _, dict_spec = dict_to_list(optimizee.create_individual(),
                                get_dict_spec=True)
    # step_size = np.asarray([config.ATTR_STEPS[k] for (k, spec, length) in dict_spec])

    fit_weights = [
        1.0,
    ]  # 0.1]
    num_generations = 5000
    population_size = 200
    # population_size = 5

    # if len(trajectories):
    #     traj.individuals = trajectories_to_individuals(
    #                             trajectories, population_size, optimizee)

    parameters = GeneticAlgorithmParameters(
        seed=None,
        popsize=population_size,
        CXPB=0.5,  # probability of mating 2 individuals
        MUTPB=0.8,  # probability of individual to mutate
        NGEN=num_generations,
        indpb=0.1,  # probability of "gene" to mutate
        tournsize=population_size,  # number of best individuals to mate
        matepar=0.5,  # how much to mix two genes when mating
        mutpar=1.  #2.0/4.0, #standard deviations for normal distribution
    )

    optimizer = GeneticAlgorithmOptimizer(
        traj,
        optimizee_create_individual=optimizee.create_individual,
        optimizee_fitness_weights=fit_weights,
        parameters=parameters,
        optimizee_bounding_func=optimizee.bounding_func,
        percent_hall_of_fame=0.05,
        percent_elite=0.5,
    )

    # Add post processing
    ### guess this is where we want to split results from multiple runs?
    env.add_postprocessing(optimizer.post_process)

    # Run the simulation with all parameter combinations
    env.run(optimizee.simulate)

    ## Outerloop optimizer end
    optimizer.end(traj)

    # Finally disable logging and close all log-files
    env.disable_logging()
예제 #8
0
def main():

    name = "L2L-OMNIGLOT"
    root_dir_path = os.path.dirname(os.path.abspath(sys.argv[0]))

    paths = Paths(name, dict(run_num="test"), root_dir_path=root_dir_path)

    print("All output logs can be found in directory ", paths.logs_path)

    traj_file = os.path.join(paths.output_dir_path, "data.h5")
    os.makedirs(paths.output_dir_path, exist_ok=True)
    print("Trajectory file is: {}".format(traj_file))

    trajectories = load_last_trajs(
        os.path.join(paths.output_dir_path, 'per_gen_trajectories'))
    if len(trajectories):
        k = trajectories['generation']
        traj = trajectories[k]
    else:
        traj = name

    # Create an environment that handles running our simulation
    # This initializes an environment
    env = Environment(
        trajectory=traj,
        filename=traj_file,
        file_title="{} data".format(name),
        comment="{} data".format(name),
        add_time=bool(1),
        automatic_storing=bool(1),
        log_stdout=bool(0),  # Sends stdout to logs
        multiprocessing=MULTIPROCESSING,
    )
    create_shared_logger_data(logger_names=["bin", "optimizers"],
                              log_levels=["INFO", "INFO"],
                              log_to_consoles=[True, True],
                              sim_name=name,
                              log_directory=paths.logs_path)
    configure_loggers()

    # Get the trajectory from the environment
    traj = env.trajectory

    # Set JUBE params
    traj.f_add_parameter_group("JUBE_params", "Contains JUBE parameters")

    # Scheduler parameters
    # Name of the scheduler
    # traj.f_add_parameter_to_group("JUBE_params", "scheduler", "Slurm")
    # Command to submit jobs to the schedulers
    # traj.f_add_parameter_to_group("JUBE_params", "submit_cmd", "sbatch")
    # Template file for the particular scheduler
    traj.f_add_parameter_to_group("JUBE_params", "job_file", "job.run")
    # Number of nodes to request for each run
    traj.f_add_parameter_to_group("JUBE_params", "nodes", "1")
    # Requested time for the compute resources
    traj.f_add_parameter_to_group("JUBE_params", "walltime", "00:01:00")
    # MPI Processes per node
    traj.f_add_parameter_to_group("JUBE_params", "ppn", "1")
    # CPU cores per MPI process
    traj.f_add_parameter_to_group("JUBE_params", "cpu_pp", "1")
    # Threads per process
    traj.f_add_parameter_to_group("JUBE_params", "threads_pp", "1")
    # Type of emails to be sent from the scheduler
    traj.f_add_parameter_to_group("JUBE_params", "mail_mode", "ALL")
    # Email to notify events from the scheduler
    traj.f_add_parameter_to_group("JUBE_params", "mail_address",
                                  "*****@*****.**")
    # Error file for the job
    traj.f_add_parameter_to_group("JUBE_params", "err_file", "stderr")
    # Output file for the job
    traj.f_add_parameter_to_group("JUBE_params", "out_file", "stdout")
    # JUBE parameters for multiprocessing. Relevant even without scheduler.
    # MPI Processes per job
    traj.f_add_parameter_to_group("JUBE_params", "tasks_per_job", "1")

    # The execution command
    run_filename = os.path.join(paths.root_dir_path, "run_files",
                                "run_optimizee.py")
    command = "python3 {}".format(run_filename)
    if ON_JEWELS and not USE_MPI:
        # -N num nodes
        # -t exec time (mins)
        # -n num sub-procs
        command = "srun -n 1 -c {} --gres=gpu:1 {}".format(NUM_SIMS, command)
    elif USE_MPI:
        # -timeout <seconds>
        # command = "MPIEXEC_TIMEOUT={} "
        #           "mpiexec -bind-to socket -np 1 {}".format(60*240, command)
        command = "mpiexec -bind-to socket -np 1 {}".format(command)

    traj.f_add_parameter_to_group("JUBE_params", "exec", command)

    # Ready file for a generation
    traj.f_add_parameter_to_group(
        "JUBE_params", "ready_file",
        os.path.join(paths.root_dir_path, "readyfiles/ready_w_"))
    # Path where the job will be executed
    traj.f_add_parameter_to_group("JUBE_params", "work_path",
                                  paths.root_dir_path)

    # Maybe we should pass the Paths object to avoid
    # defining paths here and there
    traj.f_add_parameter_to_group("JUBE_params", "paths_obj", paths)

    traj.f_add_parameter_group("simulation", "Contains JUBE parameters")
    traj.f_add_parameter_to_group("simulation", 'num_sims', NUM_SIMS)  # ms
    traj.f_add_parameter_to_group("simulation", 'on_juwels', ON_JEWELS)
    traj.f_add_parameter_to_group("simulation", 'steps', config.STEPS)  # ms
    traj.f_add_parameter_to_group("simulation", 'duration',
                                  config.DURATION)  # ms
    traj.f_add_parameter_to_group("simulation", 'sample_dt',
                                  config.SAMPLE_DT)  # ms
    traj.f_add_parameter_to_group(
        "simulation",  # rows, cols
        'input_shape',
        config.INPUT_SHAPE)
    traj.f_add_parameter_to_group(
        "simulation",  # rows, cols
        'input_divs',
        config.INPUT_DIVS)
    traj.f_add_parameter_to_group("simulation", 'input_layers',
                                  config.N_INPUT_LAYERS)
    traj.f_add_parameter_to_group("simulation", 'num_classes',
                                  config.N_CLASSES)
    traj.f_add_parameter_to_group("simulation", 'samples_per_class',
                                  config.N_SAMPLES)
    traj.f_add_parameter_to_group("simulation", 'test_per_class',
                                  config.N_TEST)
    traj.f_add_parameter_to_group("simulation", 'num_epochs', config.N_EPOCHS)
    traj.f_add_parameter_to_group("simulation", 'total_per_class',
                                  config.TOTAL_SAMPLES)
    traj.f_add_parameter_to_group("simulation", 'kernel_width',
                                  config.KERNEL_W)
    traj.f_add_parameter_to_group("simulation", 'kernel_pad', config.PAD)
    traj.f_add_parameter_to_group("simulation", 'output_size',
                                  config.OUTPUT_SIZE)
    traj.f_add_parameter_to_group("simulation", 'use_gabor',
                                  config.USE_GABOR_LAYER)
    # traj.f_add_parameter_to_group("simulation",
    #                               'expand', config.EXPANSION_RANGE[0])
    # traj.f_add_parameter_to_group("simulation",
    #                               'conn_dist', config.CONN_DIST)
    traj.f_add_parameter_to_group("simulation", 'prob_noise',
                                  config.PROB_NOISE_SAMPLE)
    traj.f_add_parameter_to_group("simulation", 'noisy_spikes_path',
                                  paths.root_dir_path)

    # db_path = '/home/gp283/brainscales-recognition/codebase/images_to_spikes/omniglot/spikes'
    db_path = os.path.abspath('../omniglot_output_%d' % config.INPUT_SHAPE[0])
    traj.f_add_parameter_to_group("simulation", 'spikes_path', db_path)

    # dbs = [ name for name in os.listdir(db_path)
    #       if os.path.isdir(os.path.join(db_path, name)) ]
    # print(dbs)
    dbs = [
        'Mkhedruli_-Georgian-', 'Tagalog',
        'Ojibwe_-Canadian_Aboriginal_Syllabics-', 'Asomtavruli_-Georgian-',
        'Balinese', 'Japanese_-katakana-', 'Malay_-Jawi_-_Arabic-', 'Armenian',
        'Burmese_-Myanmar-', 'Arcadian', 'Futurama', 'Cyrillic',
        'Alphabet_of_the_Magi', 'Sanskrit', 'Braille', 'Bengali',
        'Inuktitut_-Canadian_Aboriginal_Syllabics-', 'Syriac_-Estrangelo-',
        'Gujarati', 'Korean', 'Early_Aramaic', 'Japanese_-hiragana-',
        'Anglo-Saxon_Futhorc', 'N_Ko', 'Grantha', 'Tifinagh',
        'Blackfoot_-Canadian_Aboriginal_Syllabics-', 'Greek', 'Hebrew', 'Latin'
    ]

    # dbs = ['Alphabet_of_the_Magi']
    # dbs = ['Futurama']
    # dbs = ['Latin']
    # dbs = ['Braille']
    # dbs = ['Blackfoot_-Canadian_Aboriginal_Syllabics-', 'Gujarati', 'Syriac_-Estrangelo-']
    dbs = ['Futurama']  #, 'Braille']
    # dbs = ['Cyrillic', 'Futurama', 'Braille']
    if config.DEBUG:
        dbs = ['Braille']
    #dbs = ['Braille']

    traj.f_add_parameter_to_group("simulation", 'database', dbs)

    # Innerloop simulator
    grad_desc = OPTIMIZER == GRADDESC
    optimizee = OmniglotOptimizee(traj, 1234, grad_desc)

    # Prepare optimizee for jube runs
    JUBE_runner.prepare_optimizee(optimizee, paths.root_dir_path)

    _, dict_spec = dict_to_list(optimizee.create_individual(),
                                get_dict_spec=True)
    # step_size = np.asarray(
    #     [config.ATTR_STEPS[k] for (k, spec, length) in dict_spec])
    step_size = tuple(
        [config.ATTR_STEPS[k] for (k, spec, length) in dict_spec])

    fit_weights = [
        1.0,
    ]  # 0.1]

    optimizer_seed = config.SEED
    if OPTIMIZER == GRADDESC:
        n_random_steps = 100
        n_iteration = 1000

        parameters = RMSPropParameters(learning_rate=0.0001,
                                       exploration_step_size=step_size,
                                       n_random_steps=n_random_steps,
                                       momentum_decay=0.5,
                                       n_iteration=n_iteration,
                                       stop_criterion=np.inf,
                                       seed=optimizer_seed)

        optimizer = GradientDescentOptimizer(
            traj,
            optimizee_create_individual=optimizee.create_individual,
            optimizee_fitness_weights=fit_weights,
            parameters=parameters,
            optimizee_bounding_func=optimizee.bounding_func)

    elif OPTIMIZER == EVOSTRAT:
        parameters = EvolutionStrategiesParameters(
            learning_rate=0.0001,
            noise_std=step_size,
            mirrored_sampling_enabled=True,
            fitness_shaping_enabled=True,
            pop_size=50,  # couples
            n_iteration=1000,
            stop_criterion=np.inf,
            seed=optimizer_seed)

        optimizer = EvolutionStrategiesOptimizer(
            traj,
            optimizee_create_individual=optimizee.create_individual,
            optimizee_fitness_weights=fit_weights,
            parameters=parameters,
            optimizee_bounding_func=optimizee.bounding_func)
    else:
        num_generations = 1000
        if ON_JEWELS:
            nodes = 12
            gpus_per_node = 4
            population_size = gpus_per_node * nodes
        else:
            population_size = 20
            #population_size = 5
        # population_size = 5
        p_hof = 0.2 if population_size < 50 else 0.1
        p_bob = 0.2
        # last_trajs = load_last_trajs(os.path.join(
        #    paths.output_dir_path, 'per_gen_trajectories'))
        # last_trajs = load_last_trajs(os.path.join(
        #    paths.root_dir_path, 'trajectories'))
        # if len(last_trajs):
        #     traj.individuals = trajectories_to_individuals(
        #         last_trajs, population_size, optimizee)
        attr_steps = [config.ATTR_STEPS[k[0]] for k in dict_spec]
        parameters = GeneticAlgorithmParameters(
            seed=optimizer_seed,
            popsize=population_size,
            CXPB=0.5,  # probability of mating 2 individuals
            # note: moved from 0.8 to 0.6 mutpb to see if it removes bouncing
            MUTPB=0.7,  # probability of individual to mutate
            NGEN=num_generations,
            indpb=0.1,  # probability of "gene" to mutate
            # number of best individuals to mate
            tournsize=population_size,
            matepar=0.5,  # how much to mix two genes when mating
            # standard deviations for normal distribution
            mutpar=attr_steps,
        )

        optimizer = GeneticAlgorithmOptimizer(
            traj,
            optimizee_create_individual=optimizee.create_individual,
            optimizee_fitness_weights=fit_weights,
            parameters=parameters,
            optimizee_bounding_func=optimizee.bounding_func,
            percent_hall_of_fame=p_hof,
            percent_elite=p_bob,
        )

    # Add post processing
    env.add_postprocessing(optimizer.post_process)

    # Run the simulation with all parameter combinations
    env.run(optimizee.simulate)

    # Outerloop optimizer end
    optimizer.end(traj)

    # Finally disable logging and close all log-files
    env.disable_logging()
예제 #9
0
    def post_process(self, traj, fitnesses_results):
        """
        See :meth:`~l2l.optimizers.optimizer.Optimizer.post_process`
        """

        n_iteration, stop_criterion, learning_rate = \
            traj.n_iteration, traj.stop_criterion, traj.learning_rate
        noise_std, fitness_shaping_enabled = \
            traj.noise_std, traj.fitness_shaping_enabled

        weighted_fitness_list = []
        # *********************************************************************
        # Storing run-information in the trajectory
        # Reading fitnesses and performing distribution update
        # *********************************************************************
        for run_index, fitness in fitnesses_results:
            # We need to convert the current run index into an ind_idx
            # (index of individual within one generation)
            traj.v_idx = run_index
            ind_index = traj.par.ind_idx

            traj.f_add_result('$set.$.individual', self.eval_pop[ind_index])
            traj.f_add_result('$set.$.fitness', fitness)

            weighted_fitness_list.append(
                np.dot(fitness, self.optimizee_fitness_weights))
        traj.v_idx = -1  # set trajectory back to default

        weighted_fitness_list = np.array(weighted_fitness_list).ravel()
        # NOTE: It is necessary to clear the finesses_results to clear the data
        # in the reference, and del
        # ^ is used to make sure it's not used in the rest of this function
        fitnesses_results.clear()
        del fitnesses_results

        # Last fitness is for the previous `current_individual_arr`
        weighted_fitness_list = weighted_fitness_list[:-1]
        current_individual_fitness = weighted_fitness_list[-1]

        # Performs descending arg-sort of weighted fitness
        fitness_sorting_indices = list(
            reversed(np.argsort(weighted_fitness_list)))

        # Sorting the data according to fitness
        sorted_population = self.eval_pop_arr[fitness_sorting_indices]
        sorted_fitness = np.asarray(
            weighted_fitness_list)[fitness_sorting_indices]
        sorted_perturbations = self.current_perturbations[
            fitness_sorting_indices]

        self.best_individual_in_run = sorted_population[0]
        self.best_fitness_in_run = sorted_fitness[0]

        logger.info("-- End of generation %d --", self.g)
        logger.info("  Evaluated %d individuals",
                    len(weighted_fitness_list) + 1)
        logger.info('  Best Fitness: %.4f', self.best_fitness_in_run)
        logger.info('  Average Fitness: %.4f', np.mean(sorted_fitness))

        # *********************************************************************
        # Storing Generation Parameters / Results in the trajectory
        # *********************************************************************
        # These entries correspond to the generation that has been simulated
        # prior to this post-processing run
        # Documentation of algorithm parameters for the current generation
        #
        # generation          - The index of the evaluated generation
        # best_fitness_in_run - The highest fitness among the individuals in the
        #                       evaluated generation
        # pop_size            - Population size
        generation_result_dict = {
            'generation': self.g,
            'best_fitness_in_run': self.best_fitness_in_run,
            'current_individual_fitness': current_individual_fitness,
            'average_fitness_in_run': np.mean(sorted_fitness),
            'pop_size': self.pop_size
        }

        generation_name = 'generation_{}'.format(self.g)
        traj.results.generation_params.f_add_result_group(generation_name)
        traj.results.generation_params.f_add_result(
            generation_name + '.algorithm_params',
            generation_result_dict,
            comment="These are the parameters that correspond to the "
            "algorithm. Look at the source code for "
            "`EvolutionStrategiesOptimizer::post_process()` "
            "for comments documenting these parameters")

        if fitness_shaping_enabled:
            sorted_utilities = []
            n_individuals = len(sorted_fitness)
            for i in range(n_individuals):
                u = max(0., np.log((n_individuals / 2) + 1) - np.log(i + 1))
                sorted_utilities.append(u)
            sorted_utilities = np.array(sorted_utilities)
            sorted_utilities /= np.sum(sorted_utilities)
            sorted_utilities -= (1. / n_individuals)
            # assert np.sum(sorted_utilities) == 0., \
            # "Sum of utilities is not 0, but %.4f" % np.sum(sorted_utilities)
            fitnesses_to_fit = sorted_utilities
        else:
            fitnesses_to_fit = sorted_fitness

        assert len(fitnesses_to_fit) == len(sorted_perturbations)

        sum_fits = np.sum(
            [f * e for f, e in zip(fitnesses_to_fit, sorted_perturbations)],
            axis=0)
        weight = len(fitnesses_to_fit) * np.asarray(noise_std)**2
        self.current_individual_arr += learning_rate * (sum_fits / weight)

        # *********************************************************************
        # Create the next generation by sampling the inferred distribution
        # *********************************************************************
        # Note that this is only done in case the evaluated run is not the
        # last run
        self.eval_pop.clear()

        # check if to stop
        max_g = n_iteration - 1
        if self.g < max_g and self.best_fitness_in_run < stop_criterion:
            self.current_perturbations = self._get_perturbations(traj)
            perturbated = \
                self.current_individual_arr + self.current_perturbations
            current_eval_pop_arr = perturbated.tolist()

            self.eval_pop[:] = [
                list_to_dict(ind, self.optimizee_individual_dict_spec)
                for ind in current_eval_pop_arr
            ]
            self.eval_pop.append(
                list_to_dict(self.current_individual_arr,
                             self.optimizee_individual_dict_spec))

            # Bounding function has to be applied AFTER the individual has been
            # converted to a dict
            if self.optimizee_bounding_func is not None:
                self.eval_pop[:] = [
                    self.optimizee_bounding_func(ind) for ind in self.eval_pop
                ]

            self.eval_pop_arr[:] = np.asarray(
                [dict_to_list(ind) for ind in self.eval_pop])

            self.g += 1  # Update generation counter
            self._expand_trajectory(traj)
예제 #10
0
    def post_process(self, traj, fitnesses_results):
        """
        See :meth:`~l2l.optimizers.optimizer.Optimizer.post_process`
        """
        old_eval_pop = self.eval_pop.copy()
        self.eval_pop.clear()

        logger.info("  Evaluating %i individuals" % len(fitnesses_results))

        assert len(fitnesses_results) - 1 == traj.n_random_steps

        # We need to collect the directions of the random steps along with
        # the fitness evaluated there
        fitnesses = np.zeros(traj.n_random_steps)
        dx = np.zeros((traj.n_random_steps, len(self.current_individual)))
        weighted_fitness_list = []

        for i, (run_index, fitness) in enumerate(fitnesses_results):
            # We need to convert the current run index into an ind_idx
            # (index of individual within one generation
            traj.v_idx = run_index
            ind_index = traj.par.ind_idx

            individual = old_eval_pop[ind_index]

            traj.f_add_result('$set.$.individual', individual)
            traj.f_add_result('$set.$.fitness', fitness)

            weighted_fitness = np.dot(fitness, self.optimizee_fitness_weights)
            weighted_fitness_list.append(weighted_fitness)

            # The last element of the list is the evaluation of the individual
            # obtained via gradient descent
            if i == len(fitnesses_results) - 1:
                self.current_fitness = weighted_fitness
            else:
                fitnesses[i] = weighted_fitness
                dx[i] = np.array(
                    dict_to_list(individual)) - self.current_individual
        traj.v_idx = -1  # set the trajectory back to default

        # Performs descending arg-sort of weighted fitness
        fitness_sorting_indices = list(
            reversed(np.argsort(weighted_fitness_list)))
        old_eval_pop_as_array = np.array(
            [dict_to_list(x) for x in old_eval_pop])

        # Sorting the data according to fitness
        sorted_population = old_eval_pop_as_array[fitness_sorting_indices]
        sorted_fitness = np.asarray(
            weighted_fitness_list)[fitness_sorting_indices]

        logger.info("-- End of generation %d --", self.g)
        logger.info("  Evaluated %d individuals", len(fitnesses_results))
        logger.info('  Average Fitness: %.4f', np.mean(sorted_fitness))
        logger.info("  Current fitness is %.2f", self.current_fitness)
        logger.info('  Best Fitness: %.4f', sorted_fitness[0])
        logger.info("  Best individual is %s", sorted_population[0])

        curr_ind_dict = list_to_dict(self.current_individual,
                                     self.optimizee_individual_dict_spec)

        generation_result_dict = {
            'generation': self.g,
            'current_fitness': self.current_fitness,
            'best_fitness_in_run': sorted_fitness[0],
            'average_fitness_in_run': np.mean(sorted_fitness),
            'current_individual': curr_ind_dict,
        }

        generation_name = 'generation_{}'.format(self.g)
        traj.results.generation_params.f_add_result_group(generation_name)
        traj.results.generation_params.f_add_result(
            generation_name + '.algorithm_params', generation_result_dict)

        logger.info("-- End of iteration {}, current fitness is {} --".format(
            self.g, self.current_fitness))

        max_g = traj.n_iteration - 1
        if self.g < max_g and traj.stop_criterion > self.current_fitness:
            # Create new individual using the appropriate gradient descent
            self.update_function(
                traj,
                np.dot(np.linalg.pinv(dx), fitnesses - self.current_fitness))

            current_individual_dict = list_to_dict(
                list(self.current_individual),
                self.optimizee_individual_dict_spec)
            if self.optimizee_bounding_func is not None:
                current_individual_dict = self.optimizee_bounding_func(
                    current_individual_dict)
            self.current_individual = np.array(
                dict_to_list(current_individual_dict))

            # Explore the neighbourhood in the parameter space of the
            # current individual
            new_individual_list = [
                list_to_dict(
                    self.current_individual +
                    self.random_state.normal(0.0, traj.exploration_step_size,
                                             self.current_individual.size),
                    self.optimizee_individual_dict_spec)
                for _ in range(traj.n_random_steps)
            ]
            if self.optimizee_bounding_func is not None:
                new_individual_list = [
                    self.optimizee_bounding_func(ind)
                    for ind in new_individual_list
                ]
            new_individual_list.append(current_individual_dict)

            fitnesses_results.clear()
            self.eval_pop = new_individual_list
            self.g += 1  # Update generation counter
            self._expand_trajectory(traj)
예제 #11
0
    def __init__(self,
                 traj,
                 optimizee_create_individual,
                 optimizee_fitness_weights,
                 parameters,
                 optimizee_bounding_func=None):
        super().__init__(
            traj,
            optimizee_create_individual=optimizee_create_individual,
            optimizee_fitness_weights=optimizee_fitness_weights,
            parameters=parameters,
            optimizee_bounding_func=optimizee_bounding_func)

        self.optimizee_bounding_func = optimizee_bounding_func

        _, self.optimizee_individual_dict_spec = dict_to_list(
            self.optimizee_create_individual(), get_dict_spec=True)

        exploration_step_size = parameters.exploration_step_size
        if isinstance(exploration_step_size, dict):
            exploration_step_size = dict_to_list(exploration_step_size)

        traj.f_add_parameter('learning_rate',
                             parameters.learning_rate,
                             comment='Value of learning rate')
        traj.f_add_parameter('exploration_step_size',
                             exploration_step_size,
                             comment='Standard deviation of the random steps')
        traj.f_add_parameter('n_random_steps',
                             parameters.n_random_steps,
                             comment='Amount of random steps taken for '
                             'calculating the gradient')
        traj.f_add_parameter('n_iteration',
                             parameters.n_iteration,
                             comment='Number of iteration to perform')
        traj.f_add_parameter('stop_criterion',
                             parameters.stop_criterion,
                             comment='Stopping criterion parameter')
        traj.f_add_parameter('seed',
                             np.uint32(parameters.seed),
                             comment='Optimizer random seed')

        self.random_state = np.random.RandomState(seed=traj.par.seed)

        # Note that this array stores individuals as an np.array of floats as
        # opposed to Individual-Dicts
        # This is because this array is used within the context of the
        # gradient descent algorithm and thus needs to handle the optimizee
        # individuals as vectors
        self.current_individual = np.array(
            dict_to_list(self.optimizee_create_individual()))

        # Depending on the algorithm used, initialize the necessary variables
        self.update_function = None
        if type(parameters) is ClassicGDParameters:
            self.init_classic_gd(parameters, traj)
        elif type(parameters) is StochasticGDParameters:
            self.init_stochastic_gd(parameters, traj)
        elif type(parameters) is AdamParameters:
            self.so_moment = 0.0
            self.delta = 0.0
            self.init_adam(parameters, traj)
        elif type(parameters) is RMSPropParameters:
            self.so_moment = 0.0
            self.fo_moment = 0.0
            self.init_rmsprop(parameters, traj)
        else:
            raise Exception(
                'Class of the provided "parameters" argument is not among '
                'the supported types')

        # Added a generation-wise parameter logging
        traj.results.f_add_result_group('generation_params',
                                        comment='This contains the optimizer '
                                        'parameters that are common '
                                        'across a generation')

        # Explore the neighbourhood in the parameter space of current
        # individual
        new_individual_list = [
            list_to_dict((self.current_individual + self.random_state.normal(
                0.0, exploration_step_size,
                self.current_individual.size)).tolist(),
                         self.optimizee_individual_dict_spec)
            for _ in range(parameters.n_random_steps)
        ]

        # Also add the current individual to determine it's fitness
        new_individual_list.append(
            list_to_dict(list(self.current_individual),
                         self.optimizee_individual_dict_spec))

        if optimizee_bounding_func is not None:
            new_individual_list = [
                self.optimizee_bounding_func(ind)
                for ind in new_individual_list
            ]

        # Storing the fitness of the current individual
        self.current_fitness = -np.Inf
        self.g = 0

        self.eval_pop = new_individual_list
        self._expand_trajectory(traj)
예제 #12
0
파일: optimizer.py 프로젝트: chanokin/L2L
    def __init__(self,
                 traj,
                 optimizee_create_individual,
                 optimizee_fitness_weights,
                 parameters,
                 optimizee_bounding_func=None):
        super().__init__(
            traj,
            optimizee_create_individual=optimizee_create_individual,
            optimizee_fitness_weights=optimizee_fitness_weights,
            parameters=parameters,
            optimizee_bounding_func=optimizee_bounding_func)

        self.best_individual = None
        self.best_fitness = None

        sample_individual = self.optimizee_create_individual()

        # Generate parameter dictionary based on optimizee_param_grid
        self.param_list = {}
        _, optimizee_individual_param_spec = dict_to_list(sample_individual,
                                                          get_dict_spec=True)
        self.optimizee_individual_dict_spec = optimizee_individual_param_spec

        optimizee_param_grid = parameters.param_grid
        # Assert validity of optimizee_param_grid
        assert set(sample_individual.keys()) == set(optimizee_param_grid.keys()), \
            "The Parameters of optimizee_param_grid don't match those of the optimizee individual"

        for param_name, param_type, param_length in optimizee_individual_param_spec:
            param_lower_bound, param_upper_bound, param_n_steps = optimizee_param_grid[
                param_name]
            if param_type == DictEntryType.Scalar:
                self.param_list[param_name] = np.linspace(
                    param_lower_bound, param_upper_bound, param_n_steps)
            elif param_type == DictEntryType.Sequence:
                curr_param_list = np.linspace(param_lower_bound,
                                              param_upper_bound, param_n_steps)
                curr_param_list = np.meshgrid(*([curr_param_list] *
                                                param_length),
                                              indexing='ij')
                curr_param_list = [x.ravel() for x in curr_param_list]
                curr_param_list = np.stack(curr_param_list, axis=-1)
                self.param_list[param_name] = curr_param_list

        self.param_list = cartesian_product(
            self.param_list, tuple(sorted(optimizee_param_grid.keys())))

        # Adding the bounds information to the trajectory
        traj.f_add_parameter_group('grid_spec')
        for param_name, param_grid_spec in optimizee_param_grid.items():
            traj.f_add_parameter_to_group('grid_spec',
                                          param_name + '.lower_bound',
                                          param_grid_spec[0])
            traj.f_add_parameter_to_group('grid_spec',
                                          param_name + '.upper_bound',
                                          param_grid_spec[1])
            traj.f_add_parameter_to_group('grid_spec', param_name + '.step',
                                          param_grid_spec[2])

        # Expanding the trajectory
        self.param_list = {('individual.' + key): value
                           for key, value in self.param_list.items()}
        k0 = list(self.param_list.keys())[0]
        self.param_list['generation'] = [0]
        self.param_list['ind_idx'] = np.arange(len(self.param_list[k0]))

        traj.f_expand(self.param_list)
        traj.par['n_iteration'] = 1
        #: The current generation number
        self.g = 0
        #: The population (i.e. list of individuals) to be evaluated at the next iteration
        self.eval_pop = None  # self.param_list
        # Storing the fitness of the current individual
        self.current_fitness = -np.Inf
        self.traj = traj
예제 #13
0
 def spawn():
     x = self.optimizee_create_individual()
     return dict_to_list(self.optimizee_bounding_func(x))
예제 #14
0
    def __init__(self,
                 traj,
                 optimizee_create_individual,
                 optimizee_fitness_weights,
                 parameters,
                 optimizee_bounding_func=None):

        super().__init__(
            traj,
            optimizee_create_individual=optimizee_create_individual,
            optimizee_fitness_weights=optimizee_fitness_weights,
            parameters=parameters,
            optimizee_bounding_func=optimizee_bounding_func)

        self.optimizee_bounding_func = optimizee_bounding_func

        # If a parameter is set to `None`, use default value as described in Wierstra et al. (2014)
        if parameters.learning_rate_mu is None:
            learning_rate_mu = 1.
        else:
            learning_rate_mu = parameters.learning_rate_mu

        if parameters.learning_rate_sigma is None:
            learning_rate_sigma = (3 + np.log(len(parameters.mu))) / (
                5. * np.sqrt(len(parameters.mu)))
        else:
            learning_rate_sigma = parameters.learning_rate_sigma

        if parameters.pop_size is None:
            pop_size = 4 + int(np.floor(3 * np.log(len(parameters.mu))))
        else:
            pop_size = parameters.pop_size

        if pop_size < 1:
            raise ValueError("pop_size needs to be greater than 0")

        # The following parameters are recorded
        traj.f_add_parameter('learning_rate_mu',
                             learning_rate_mu,
                             comment='Learning rate mu')
        traj.f_add_parameter('learning_rate_sigma',
                             learning_rate_sigma,
                             comment='Learning rate mu')
        traj.f_add_parameter('mu',
                             parameters.mu,
                             comment='Initial mean of search distribution')
        traj.f_add_parameter(
            'sigma',
            parameters.sigma,
            comment='Initial standard deviation of search distribution')
        traj.f_add_parameter('mirrored_sampling_enabled',
                             parameters.mirrored_sampling_enabled,
                             comment='Flag to enable mirrored sampling')
        traj.f_add_parameter('fitness_shaping_enabled',
                             parameters.fitness_shaping_enabled,
                             comment='Flag to enable fitness shaping')
        traj.f_add_parameter(
            'pop_size',
            pop_size,
            comment='Number of minimal individuals simulated in each run')
        traj.f_add_parameter('n_iteration',
                             parameters.n_iteration,
                             comment='Number of iterations to run')
        traj.f_add_parameter(
            'stop_criterion',
            parameters.stop_criterion,
            comment='Stop if best individual reaches this fitness')
        traj.f_add_parameter(
            'seed',
            np.uint32(parameters.seed),
            comment='Seed used for random number generation in optimizer')

        self.random_state = np.random.RandomState(traj.parameters.seed)

        self.current_individual_arr, self.optimizee_individual_dict_spec = dict_to_list(
            self.optimizee_create_individual(), get_dict_spec=True)

        traj.f_add_derived_parameter(
            'dimension',
            self.current_individual_arr.shape,
            comment='The dimension of the parameter space of the optimizee')

        # Added a generation-wise parameter logging
        traj.results.f_add_result_group(
            'generation_params',
            comment='This contains the optimizer parameters that are'
            ' common across a generation')

        # The following parameters are recorded as generation parameters i.e. once per generation
        self.g = 0  # the current generation
        self.pop_size = pop_size  # Population size is dynamic in FACE
        self.best_fitness_in_run = -np.inf
        self.best_individual_in_run = None

        # Set initial parameters of search distribution
        self.mu = traj.mu
        self.sigma = traj.sigma

        # Generate initial distribution
        self.current_perturbations = self._get_perturbations(traj)
        current_eval_pop_arr = (
            self.mu + self.sigma * self.current_perturbations).tolist()

        self.eval_pop = [
            list_to_dict(ind, self.optimizee_individual_dict_spec)
            for ind in current_eval_pop_arr
        ]

        # Bounding function has to be applied AFTER the individual has been converted to a dict
        if optimizee_bounding_func is not None:
            self.eval_pop = [
                self.optimizee_bounding_func(ind) for ind in self.eval_pop
            ]

        self.eval_pop_arr = np.array(
            [dict_to_list(ind) for ind in self.eval_pop])

        self._expand_trajectory(traj)
예제 #15
0
    def post_process(self, traj, fitnesses_results):
        """
        See :meth:`~l2l.optimizers.optimizer.Optimizer.post_process`
        """

        n_iteration, stop_criterion, fitness_shaping_enabled = \
            traj.n_iteration, traj.stop_criterion, traj.fitness_shaping_enabled

        weighted_fitness_list = []
        # **************************************************************************************************************
        # Storing run-information in the trajectory
        # Reading fitnesses and performing distribution update
        # **************************************************************************************************************
        for run_index, fitness in fitnesses_results:
            # We need to convert the current run index into an ind_idx
            # (index of individual within one generation)
            traj.v_idx = run_index
            ind_index = traj.par.ind_idx

            traj.f_add_result('$set.$.individual', self.eval_pop[ind_index])
            traj.f_add_result('$set.$.fitness', fitness)

            weighted_fitness_list.append(
                np.dot(fitness, self.optimizee_fitness_weights))
        traj.v_idx = -1  # set trajectory back to default

        weighted_fitness_list = np.array(weighted_fitness_list).ravel()
        # NOTE: It is necessary to clear the finesses_results to clear the data in the reference, and del
        # is used to make sure it's not used in the rest of this function
        fitnesses_results.clear()
        del fitnesses_results

        # Last fitness is for the previous `current_individual_arr`
        weighted_fitness_list = weighted_fitness_list[:-1]
        current_individual_fitness = weighted_fitness_list[-1]

        # Performs descending arg-sort of weighted fitness
        fitness_sorting_indices = list(
            reversed(np.argsort(weighted_fitness_list)))

        # Sorting the data according to fitness
        sorted_population = self.eval_pop_arr[fitness_sorting_indices]
        sorted_fitness = np.asarray(
            weighted_fitness_list)[fitness_sorting_indices]
        sorted_perturbations = self.current_perturbations[
            fitness_sorting_indices]

        self.best_individual_in_run = sorted_population[0]
        self.best_fitness_in_run = sorted_fitness[0]

        logger.info("-- End of generation %d --", self.g)
        logger.info("  Evaluated %d individuals",
                    len(weighted_fitness_list) + 1)
        logger.info('  Best Fitness: %.4f', self.best_fitness_in_run)
        logger.info('  Average Fitness: %.4f', np.mean(sorted_fitness))

        # **************************************************************************************************************
        # Storing Generation Parameters / Results in the trajectory
        # **************************************************************************************************************
        # These entries correspond to the generation that has been simulated prior to this post-processing run

        # Documentation of algorithm parameters for the current generation
        #
        # generation          - The index of the evaluated generation
        # best_fitness_in_run - The highest fitness among the individuals in the
        #                       evaluated generation
        # pop_size            - Population size
        generation_result_dict = {
            'generation': self.g,
            'best_fitness_in_run': self.best_fitness_in_run,
            'current_individual_fitness': current_individual_fitness,
            'average_fitness_in_run': np.mean(sorted_fitness),
            'pop_size': self.pop_size
        }

        generation_name = 'generation_{}'.format(self.g)
        traj.results.generation_params.f_add_result_group(generation_name)
        traj.results.generation_params.f_add_result(
            generation_name + '.algorithm_params',
            generation_result_dict,
            comment="These are the parameters that correspond to the algorithm. "
            "Look at the source code for `EvolutionStrategiesOptimizer::post_process()` "
            "for comments documenting these parameters")

        traj.results.generation_params.f_add_result(
            generation_name + '.distribution_params', {
                'mu': self.mu.copy(),
                'sigma': self.sigma.copy()
            },
            comment=
            "These are the parameters of the distribution that underlies the"
            " currently evaluated generation")

        if fitness_shaping_enabled:
            fitnesses_to_fit = self._compute_utility(sorted_fitness)
        else:
            fitnesses_to_fit = sorted_fitness

        assert len(fitnesses_to_fit) == len(sorted_perturbations)

        # **************************************************************************************************************
        # Update the parameters of the search distribution using the natural gradient in natural coordinates
        # **************************************************************************************************************
        self.mu += traj.learning_rate_mu * traj.sigma * np.dot(
            fitnesses_to_fit, sorted_perturbations)
        self.sigma *= np.exp(
            traj.learning_rate_sigma / 2. *
            np.dot(fitnesses_to_fit, sorted_perturbations**2 - 1.))

        # **************************************************************************************************************
        # Create the next generation by sampling the inferred distribution
        # **************************************************************************************************************
        # Note that this is only done in case the evaluated run is not the last run

        self.eval_pop.clear()

        # check if to stop
        if self.g < n_iteration - 1 and self.best_fitness_in_run < stop_criterion:
            self.current_perturbations = self._get_perturbations(traj)
            current_eval_pop_arr = (
                self.mu + self.sigma * self.current_perturbations).tolist()

            self.eval_pop = [
                list_to_dict(ind, self.optimizee_individual_dict_spec)
                for ind in current_eval_pop_arr
            ]

            # Bounding function has to be applied AFTER the individual has been converted to a dict
            if self.optimizee_bounding_func is not None:
                self.eval_pop = [
                    self.optimizee_bounding_func(ind) for ind in self.eval_pop
                ]

            self.eval_pop_arr = np.array(
                [dict_to_list(ind) for ind in self.eval_pop])

            self.g += 1  # Update generation counter
            self._expand_trajectory(traj)
예제 #16
0
    def post_process(self, traj, fitnesses_results):
        """
        See :meth:`~l2l.optimizers.optimizer.Optimizer.post_process`
        """

        n_elite, n_iteration, smoothing, temp_decay, min_pop_size, max_pop_size = \
            traj.n_elite, traj.n_iteration, traj.smoothing, traj.temp_decay, traj.min_pop_size, traj.max_pop_size
        stop_criterion, n_expand = traj.stop_criterion, traj.n_expand

        weighted_fitness_list = []
        # **************************************************************************************************************
        # Storing run-information in the trajectory
        # Reading fitnesses and performing distribution update
        # **************************************************************************************************************
        for run_index, fitness in fitnesses_results:
            # We need to convert the current run index into an ind_idx
            # (index of individual within one generation)
            traj.v_idx = run_index
            ind_index = traj.par.ind_idx

            traj.f_add_result('$set.$.individual', self.eval_pop[ind_index])
            traj.f_add_result('$set.$.fitness', fitness)

            weighted_fitness_list.append(
                np.dot(fitness, self.optimizee_fitness_weights))
        traj.v_idx = -1  # set trajectory back to default

        # Performs descending arg-sort of weighted fitness
        fitness_sorting_indices = list(
            reversed(np.argsort(weighted_fitness_list)))

        generation_name = 'generation_{}'.format(self.g)

        # Sorting the data according to fitness
        sorted_population = self.eval_pop_asarray[fitness_sorting_indices]
        sorted_fitess = np.asarray(
            weighted_fitness_list)[fitness_sorting_indices]

        # Elite individuals are with performance better than or equal to the (1-rho) quantile.
        # See original describtion of cross entropy for optimization
        elite_individuals = sorted_population[:n_elite]

        previous_best_fitness = self.best_fitness_in_run
        self.best_individual_in_run = sorted_population[0]
        self.best_fitness_in_run = sorted_fitess[0]
        previous_gamma = self.gamma
        self.gamma = sorted_fitess[n_elite - 1]

        logger.info("-- End of generation %d --", self.g)
        logger.info("  Evaluated %d individuals", len(fitnesses_results))
        logger.info('  Best Fitness: %.4f', self.best_fitness_in_run)
        logger.debug('  Calculated gamma: %.4f', self.gamma)

        # **************************************************************************************************************
        # Storing Generation Parameters / Results in the trajectory
        # **************************************************************************************************************
        # These entries correspond to the generation that has been simulated prior to this post-processing run

        traj.results.generation_params.f_add_result(
            generation_name + '.g',
            self.g,
            comment='The index of the evaluated generation')
        traj.results.generation_params.f_add_result(
            generation_name + '.gamma',
            self.gamma,
            comment='The fitness threshold inferred from the evaluated '
            'generation (This is used in sampling the next generation')
        traj.results.generation_params.f_add_result(
            generation_name + '.T',
            self.T,
            comment='Temperature used to select non-elite elements among the'
            'individuals of the evaluated generation')
        traj.results.generation_params.f_add_result(
            generation_name + '.best_fitness_in_run',
            self.best_fitness_in_run,
            comment='The highest fitness among the individuals in the '
            'evaluated generation')
        traj.results.generation_params.f_add_result(generation_name +
                                                    '.pop_size',
                                                    self.pop_size,
                                                    comment='Population size')

        # Check stopping
        if self.g >= n_iteration or self.best_fitness_in_run >= stop_criterion:
            return

        expand = True
        if self.best_fitness_in_run > previous_best_fitness or self.gamma > previous_gamma:
            # shrink population size
            self.pop_size = (self.pop_size + min_pop_size) // 2
            # new distribution fit
            individuals_to_be_fitted = elite_individuals

            # Temperature dependent sampling of non elite individuals
            if temp_decay > 0:
                # Keeping non-elite samples with certain probability dependent on temperature (like Simulated Annealing)
                non_elite_selection_probs = np.clip(np.exp(
                    (weighted_fitness_list[n_elite:] - self.gamma) / self.T),
                                                    amin=0.0,
                                                    a_max=1.0)
                non_elite_selected_indices = self.random_state.binomial(
                    1, p=non_elite_selection_probs)
                non_elite_eval_pop_asarray = sorted_population[n_elite:][
                    non_elite_selected_indices]
                individuals_to_be_fitted = np.concatenate(
                    (elite_individuals, non_elite_eval_pop_asarray))

            # Fitting New distribution parameters.
            self.distribution_results = self.current_distribution.fit(
                individuals_to_be_fitted, smoothing)
        elif self.pop_size + n_expand <= max_pop_size:
            # Increase pop size by one, resample, FACE part
            logger.info('  FACE increase population size by %d', n_expand)
            self.pop_size += n_expand
        else:
            # Stop algorithm
            expand = False
            logger.warning('  Possibly diverged')

        # Add the results of the distribution fitting to the trajectory
        for parameter_key, parameter_value in self.distribution_results.items(
        ):
            traj.results.generation_params.f_add_result(
                generation_name + '.' + parameter_key, parameter_value)

        # **************************************************************************************************************
        # Create the next generation by sampling the inferred distribution
        # **************************************************************************************************************
        # Note that this is only done in case the evaluated run is not the last run
        fitnesses_results.clear()
        self.eval_pop.clear()
        if expand:
            # Sample from the constructed distribution
            self.eval_pop_asarray = self.current_distribution.sample(
                self.pop_size)
            self.eval_pop = [
                list_to_dict(ind_asarray, self.optimizee_individual_dict_spec)
                for ind_asarray in self.eval_pop_asarray
            ]
            # Clip to boundaries
            if self.optimizee_bounding_func is not None:
                self.eval_pop = [
                    self.optimizee_bounding_func(individual)
                    for individual in self.eval_pop
                ]
                self.eval_pop_asarray = np.array(
                    [dict_to_list(x) for x in self.eval_pop])
            self.g += 1  # Update generation counter
            self.T *= temp_decay
            self._expand_trajectory(traj)
예제 #17
0
    def __init__(self,
                 traj,
                 optimizee_create_individual,
                 optimizee_fitness_weights,
                 parameters,
                 optimizee_bounding_func=None):

        super().__init__(
            traj,
            optimizee_create_individual=optimizee_create_individual,
            optimizee_fitness_weights=optimizee_fitness_weights,
            parameters=parameters,
            optimizee_bounding_func=optimizee_bounding_func)

        self.optimizee_bounding_func = optimizee_bounding_func

        if parameters.min_pop_size < 1:
            raise Exception("min_pop_size needs to be greater than 0")
        if parameters.max_pop_size < parameters.min_pop_size:
            raise Exception(
                "max_pop_size needs to be greater or equal to min_pop_size")
        if parameters.n_elite > parameters.min_pop_size:
            raise Exception("n_elite exceeds min_pop_size")
        if parameters.temp_decay < 0 or parameters.temp_decay > 1:
            raise Exception("temp_decay not in range")
        if parameters.smoothing >= 1 or parameters.smoothing < 0:
            raise Exception("smoothing has to be in interval [0, 1)")
        if parameters.seed is None:
            raise Exception("The 'seed' must be set")

        # The following parameters are recorded
        traj.f_add_parameter(
            'min_pop_size',
            parameters.min_pop_size,
            comment='Number of minimal individuals simulated in each run')
        traj.f_add_parameter('max_pop_size',
                             parameters.max_pop_size,
                             comment='Maximal individuals in population')
        traj.f_add_parameter(
            'n_elite',
            parameters.n_elite,
            comment='Number of individuals to be considered as elite')
        traj.f_add_parameter('n_iteration',
                             parameters.n_iteration,
                             comment='Number of iterations to run')
        traj.f_add_parameter(
            'n_expand',
            parameters.n_expand,
            comment='Expanding of population size in case of FACE')
        traj.f_add_parameter(
            'stop_criterion',
            parameters.stop_criterion,
            comment='Stop if best individual reaches this fitness')
        traj.f_add_parameter('smoothing',
                             parameters.smoothing,
                             comment='Weight of old parameters in smoothing')
        traj.f_add_parameter('temp_decay',
                             parameters.temp_decay,
                             comment='Decay factor for temperature')
        traj.f_add_parameter('seed',
                             np.uint32(parameters.seed),
                             comment='Random seed used by optimizer')

        self.random_state = np.random.RandomState(seed=traj.par.seed)
        temp_indiv, self.optimizee_individual_dict_spec = dict_to_list(
            self.optimizee_create_individual(), get_dict_spec=True)
        traj.f_add_derived_parameter(
            'dimension',
            len(temp_indiv),
            comment='The dimension of the parameter space of the optimizee')

        # Added a generation-wise parameter logging
        traj.results.f_add_result_group(
            'generation_params',
            comment='This contains the optimizer parameters that are'
            ' common across a generation')

        # The following parameters are recorded as generation parameters i.e. once per generation
        self.g = 0  # the current generation
        # This is the value above which the samples are considered elite in the
        # current generation
        self.gamma = -np.inf
        self.T = 1  # This is the temperature used to filter evaluated samples in this run
        self.pop_size = parameters.min_pop_size  # Population size is dynamic in FACE
        self.best_fitness_in_run = -np.inf

        # The first iteration does not pick the values out of the Gaussian distribution. It picks randomly
        # (or at-least as randomly as optimizee_create_individual creates individuals)

        # Note that this array stores individuals as an np.array of floats as opposed to Individual-Dicts
        # This is because this array is used within the context of the cross entropy algorithm and
        # Thus needs to handle the optimizee individuals as vectors
        current_eval_pop = [
            self.optimizee_create_individual()
            for _ in range(parameters.min_pop_size)
        ]

        if optimizee_bounding_func is not None:
            current_eval_pop = [
                self.optimizee_bounding_func(ind) for ind in current_eval_pop
            ]

        self.eval_pop = current_eval_pop
        self.eval_pop_asarray = np.array(
            [dict_to_list(x) for x in self.eval_pop])

        # Max Likelihood
        self.current_distribution = parameters.distribution
        self.current_distribution.init_random_state(self.random_state)
        self.current_distribution.fit(self.eval_pop_asarray)

        self._expand_trajectory(traj)
예제 #18
0
파일: optimizer.py 프로젝트: w-klijn/L2L
    def __init__(self, traj,
                 optimizee_create_individual,
                 optimizee_fitness_weights,
                 parameters,
                 optimizee_bounding_func=None):
        super().__init__(traj,
                         optimizee_create_individual=optimizee_create_individual,
                         optimizee_fitness_weights=optimizee_fitness_weights,
                         parameters=parameters, optimizee_bounding_func=optimizee_bounding_func)

        self.optimizee_bounding_func = optimizee_bounding_func
        
        # The following parameters are recorded
        traj.f_add_parameter('n_parallel_runs', parameters.n_parallel_runs,
                             comment='Number of parallel simulated annealing runs / Size of Population')
        traj.f_add_parameter('noisy_step', parameters.noisy_step, comment='Size of the random step')
        traj.f_add_parameter('n_iteration', parameters.n_iteration, comment='Number of iteration to perform')
        traj.f_add_parameter('stop_criterion', parameters.stop_criterion, comment='Stopping criterion parameter')
        traj.f_add_parameter('seed', parameters.seed, comment='Seed for RNG')
        
        cooling_schedules_string = ''
        bounds_list = []
        decay_list = []
        schedules_list = []
        for i in range(0,traj.n_parallel_runs):
            bounds_list.append(str(parameters.temperature_bounds[i,:]))
            bounds_list.append(' ')
            decay_list.append(str(parameters.decay_parameters[i]))
            decay_list.append(' ')
            schedules_list.append(str(parameters.cooling_schedules[i]))
            schedules_list.append(' ')
        temperature_bounds_string = ''.join(bounds_list)
        decay_parameters_string = ''.join(decay_list)
        cooling_schedules_string = ''.join(schedules_list)
        
        traj.f_add_parameter('temperature_bounds', temperature_bounds_string,
                             comment='The max and min temperature of the respective schedule')
        traj.f_add_parameter('decay_parameters', decay_parameters_string,
                             comment='The one parameter, most schedules need')
        traj.f_add_parameter('cooling_schedules', cooling_schedules_string,
                             comment='The used cooling schedule')

        _, self.optimizee_individual_dict_spec = dict_to_list(self.optimizee_create_individual(), get_dict_spec=True)

        # Note that this array stores individuals as an np.array of floats as opposed to Individual-Dicts
        # This is because this array is used within the context of the simulated annealing algorithm and
        # Thus needs to handle the optimizee individuals as vectors
        self.current_individual_list = [np.array(dict_to_list(self.optimizee_create_individual()))
                                        for _ in range(parameters.n_parallel_runs)]

        traj.f_add_result('fitnesses', [], comment='Fitnesses of all individuals')

        self.T_all = parameters.temperature_bounds[:,0]  # Initialize temperature
        self.T = 1
        self.g = 0  # the current generation
        self.cooling_schedules = parameters.cooling_schedules
        self.decay_parameters = parameters.decay_parameters
        self.temperature_bounds = parameters.temperature_bounds
        # Keep track of current fitness value to decide whether we want the next individual to be accepted or not
        self.current_fitness_value_list = [-np.Inf] * parameters.n_parallel_runs

        new_individual_list = [
            list_to_dict(ind_as_list + np.random.normal(0.0, parameters.noisy_step, ind_as_list.size) * traj.noisy_step,
                         self.optimizee_individual_dict_spec)
            for ind_as_list in self.current_individual_list
        ]
        if optimizee_bounding_func is not None:
            new_individual_list = [self.optimizee_bounding_func(ind) for ind in new_individual_list]

        self.eval_pop = new_individual_list
        self._expand_trajectory(traj)
        
        #initialize container for the indices of the parallel runs
        self.parallel_indices = []
        for i in range(0,traj.n_parallel_runs):
            self.parallel_indices.append(i)
        
        self.available_cooling_schedules = AvailableCoolingSchedules
        
        # assert if all cooling schedules are among the known cooling schedules
        schedule_known = True  # start off as True - if any schdule is unknown gets False
        for i in range(np.size(self.cooling_schedules)):
            schedule_known = schedule_known and self.cooling_schedules[i] in AvailableCoolingSchedules
        
        assert schedule_known, print("Warning: Unknown cooling schedule")
예제 #19
0
    def __init__(self,
                 traj,
                 optimizee_create_individual,
                 optimizee_fitness_weights,
                 parameters,
                 optimizee_bounding_func=None):

        super().__init__(
            traj,
            optimizee_create_individual=optimizee_create_individual,
            optimizee_fitness_weights=optimizee_fitness_weights,
            parameters=parameters,
            optimizee_bounding_func=optimizee_bounding_func)

        self.optimizee_bounding_func = optimizee_bounding_func

        if parameters.pop_size < 1:
            raise Exception("pop_size needs to be greater than 0")

        # The following parameters are recorded
        traj.f_add_parameter('learning_rate',
                             parameters.learning_rate,
                             comment='Learning rate')
        traj.f_add_parameter('noise_std',
                             parameters.noise_std,
                             comment='Standard deviation of noise')
        traj.f_add_parameter('mirrored_sampling_enabled',
                             parameters.mirrored_sampling_enabled,
                             comment='Flag to enable mirrored sampling')
        traj.f_add_parameter('fitness_shaping_enabled',
                             parameters.fitness_shaping_enabled,
                             comment='Flag to enable fitness shaping')
        traj.f_add_parameter(
            'pop_size',
            parameters.pop_size,
            comment='Number of minimal individuals simulated in each run')
        traj.f_add_parameter('n_iteration',
                             parameters.n_iteration,
                             comment='Number of iterations to run')
        traj.f_add_parameter(
            'stop_criterion',
            parameters.stop_criterion,
            comment='Stop if best individual reaches this fitness')
        traj.f_add_parameter(
            'seed',
            np.uint32(parameters.seed),
            comment='Seed used for random number generation in optimizer')

        self.random_state = np.random.RandomState(traj.parameters.seed)

        self.current_individual_arr, self.optimizee_individual_dict_spec = \
            dict_to_list(
                self.optimizee_create_individual(), get_dict_spec=True)

        noise_std_shape = np.array(parameters.noise_std).shape
        ind_shape = self.current_individual_arr.shape
        assert noise_std_shape == () or noise_std_shape == ind_shape

        traj.f_add_derived_parameter(
            'dimension',
            self.current_individual_arr.shape,
            comment='The dimension of the parameter space of the optimizee')

        # Added a generation-wise parameter logging
        traj.results.f_add_result_group(
            'generation_params',
            comment='This contains the optimizer parameters that are'
            ' common across a generation')

        # The following parameters are recorded as generation parameters
        # i.e. once per generation
        self.g = 0  # the current generation
        # Population size is dynamic in FACE
        self.pop_size = parameters.pop_size
        self.best_fitness_in_run = -np.inf
        self.best_individual_in_run = None

        # The first iteration does not pick the values out of the Gaussian
        # distribution. It picks randomly (or at-least
        # as randomly as optimizee_create_individual creates individuals)

        # Note that this array stores individuals as an np.array of floats as
        # opposed to Individual-Dicts.
        # This is because this array is used within the context of the cross
        # entropy algorithm and thus needs to handle the optimizee individuals
        # as vectors
        self.current_perturbations = self._get_perturbations(traj)
        current_eval_pop_arr = (self.current_individual_arr +
                                self.current_perturbations).tolist()

        self.eval_pop = [
            list_to_dict(ind, self.optimizee_individual_dict_spec)
            for ind in current_eval_pop_arr
        ]
        self.eval_pop.append(
            list_to_dict(self.current_individual_arr,
                         self.optimizee_individual_dict_spec))

        # Bounding function has to be applied AFTER the individual has been
        # converted to a dict
        if optimizee_bounding_func is not None:
            self.eval_pop[:] = [
                self.optimizee_bounding_func(ind) for ind in self.eval_pop
            ]

        self.eval_pop_arr = np.array(
            [dict_to_list(ind) for ind in self.eval_pop])

        self._expand_trajectory(traj)
예제 #20
0
파일: optimizer.py 프로젝트: w-klijn/L2L
    def post_process(self, traj, fitnesses_results):
        """
        See :meth:`~l2l.optimizers.optimizer.Optimizer.post_process`
        """
        noisy_step, n_iteration, stop_criterion = \
            traj.noisy_step, traj.n_iteration, traj.stop_criterion
        cooling_schedules = self.cooling_schedules
        decay_parameters = self.decay_parameters
        temperature_bounds = self.temperature_bounds
        old_eval_pop = self.eval_pop.copy()
        self.eval_pop.clear()
        temperature = self.T_all
        for i in range(0,traj.n_parallel_runs):
            self.T_all[self.parallel_indices[i]] = self.cooling(temperature[self.parallel_indices[i]], cooling_schedules[self.parallel_indices[i]], decay_parameters[self.parallel_indices[i]], temperature_bounds[self.parallel_indices[i],:], n_iteration)
        logger.info("  Evaluating %i individuals" % len(fitnesses_results))
  
        assert len(fitnesses_results) == traj.n_parallel_runs
        weighted_fitness_list = []
        for i, (run_index, fitness) in enumerate(fitnesses_results):
            
            self.T = self.T_all[self.parallel_indices[i]]

            weighted_fitness = sum(f * w for f, w in zip(fitness, self.optimizee_fitness_weights))
            weighted_fitness_list.append(weighted_fitness)

            # We need to convert the current run index into an ind_idx
            # (index of individual within one generation)
            traj.v_idx = run_index
            ind_index = traj.par.ind_idx
            individual = old_eval_pop[ind_index]

            # Accept or reject the new solution
            current_fitness_value_i = self.current_fitness_value_list[i]
            r = np.random.rand()
            p = np.exp((weighted_fitness - current_fitness_value_i) / self.T)

            # Accept
            if r < p or weighted_fitness >= current_fitness_value_i:
                self.current_fitness_value_list[i] = weighted_fitness
                self.current_individual_list[i] = np.array(dict_to_list(individual))

            traj.f_add_result('$set.$.individual', individual)
            # Watchout! if weighted fitness is a tuple/np array it should be converted to a list first here
            traj.f_add_result('$set.$.fitness', weighted_fitness)

            current_individual = self.current_individual_list[i]
            new_individual = list_to_dict(current_individual + np.random.randn(current_individual.size) * noisy_step * self.T,
                                          self.optimizee_individual_dict_spec)
            if self.optimizee_bounding_func is not None:
                new_individual = self.optimizee_bounding_func(new_individual)

            logger.debug("Current best fitness for individual %d is %.2f. New individual is %s", 
                         i, self.current_fitness_value_list[i], new_individual)
            self.eval_pop.append(new_individual)
            
        # the parallel tempering swapping starts here
        for i in range(0,traj.n_parallel_runs):
            
            #make a random choice from all the other parallel runs
            compare_indices = []
            for j in range(0,traj.n_parallel_runs):
                compare_indices.append(i)
            compare_indices.remove(i)
            random_choice = random.choice(compare_indices)
            
            #random variable with unit distribution betwwen 0 and 1
            random_variable = np.random.rand() 
            
            #swap if criterion is met
            if (self.metropolis_hasting(self.current_fitness_value_list[i], self.current_fitness_value_list[random_choice], self.T_all[i], self.T_all[random_choice]) > random_variable):
                temp = self.parallel_indices[i]
                self.parallel_indices[i] = self.parallel_indices[random_choice]
                self.parallel_indices[random_choice] = temp
                
        logger.debug("Current best fitness within population is %.2f", max(self.current_fitness_value_list))

        traj.v_idx = -1  # set the trajectory back to default
        logger.info("-- End of generation {} --".format(self.g))

        # ------- Create the next generation by crossover and mutation -------- #
        # not necessary for the last generation
        if self.g < n_iteration - 1 and stop_criterion > max(self.current_fitness_value_list):
            fitnesses_results.clear()
            self.g += 1  # Update generation counter
            self._expand_trajectory(traj)