Exemplo n.º 1
0
    def _one_iter(self, start_point: Configuration,
                  *args) -> Tuple[float, Configuration]:

        incumbent = start_point
        # Compute the acquisition value of the incumbent
        acq_val_incumbent = self.acquisition_function([incumbent], *args)[0]

        local_search_steps = 0
        neighbors_looked_at = 0
        time_n = []
        while True:

            local_search_steps += 1
            if local_search_steps % 1000 == 0:
                self.logger.warning(
                    "Local search took already %d iterations. Is it maybe "
                    "stuck in a infinite loop?", local_search_steps)

            # Get neighborhood of the current incumbent
            # by randomly drawing configurations
            changed_inc = False

            # Get one exchange neighborhood returns an iterator (in contrast of
            # the previously returned list).
            all_neighbors = get_one_exchange_neighbourhood(
                incumbent, seed=self.rng.randint(MAXINT))

            for neighbor in all_neighbors:
                s_time = time.time()
                acq_val = self.acquisition_function([neighbor], *args)
                neighbors_looked_at += 1
                time_n.append(time.time() - s_time)

                if acq_val > acq_val_incumbent + self.epsilon:
                    self.logger.debug("Switch to one of the neighbors")
                    incumbent = neighbor
                    acq_val_incumbent = acq_val
                    changed_inc = True
                    break

            if (not changed_inc) or \
                    (self.max_iterations is not None and
                             local_search_steps == self.max_iterations):
                self.logger.debug(
                    "Local search took %d steps and looked at %d "
                    "configurations. Computing the acquisition "
                    "value for one configuration took %f seconds"
                    " on average.", local_search_steps, neighbors_looked_at,
                    np.mean(time_n))
                break

        return acq_val_incumbent, incumbent
Exemplo n.º 2
0
    def _one_iter(self, start_generation: [Configuration],
                  *args) -> [Tuple[float, Configuration]]:

        # if we have too little to start with
        if self.generation_size > len(start_generation):
            start_generation = start_generation + \
                               self.config_space.sample_configuration(size=self.generation_size - len(start_generation))

        ## Breed phase:
        population = []

        # Add parents:
        population += [
            start_generation[i] for i in range(len(start_generation))
        ]

        # Generate new individuals with crossover:
        for i in range(0, len(start_generation) - 1):
            for j in range(i + 1, len(start_generation)):
                if (self.rng.standard_normal() > self.crossover_probability):
                    population += self.crossover(start_generation[i],
                                                 start_generation[j])

        ## Mutation phase (produces new individuals too):
        for incumbent in start_generation:
            neighbours = get_one_exchange_neighbourhood(
                incumbent, seed=self.rng.randint(MAXINT))
            population += neighbours

        # Make population contain only unique individuals:
        arrays = list(map(lambda x: x.get_array(), population))
        indices = np.unique(arrays, return_index=True, axis=0)
        population = [population[ind] for ind in indices[1][::-1]]

        # #Selection phase:
        survivals = []
        sorted = self._sort_configs_by_acq_value(population)

        elite_num = int(self.generation_size * self.elite_rate)
        for i in range(0, elite_num):
            individual = sorted[i]
            survivals.append(individual)

        return survivals
Exemplo n.º 3
0
    def maximize(self, start_point, *args):
        """
        Starts a local search from the given startpoint and quits
        if either the max number of steps is reached or no neighbor
        with an higher improvement was found.

        Parameters:
        ----------

        start_point:  np.array(1, D):
            The point from where the local search starts
        *args :
            Additional parameters that will be passed to the
            acquisition function

        Returns:
        -------

        incumbent np.array(1, D):
            The best found configuration
        acq_val_incumbent np.array(1,1) :
            The acquisition value of the incumbent

        """
        incumbent = start_point
        # Compute the acquisition value of the incumbent
        incumbent_array = convert_configurations_to_array([incumbent])
        acq_val_incumbent = self.acquisition_function(incumbent_array, *args)

        local_search_steps = 0
        neighbors_looked_at = 0
        time_n = []
        while True:

            local_search_steps += 1
            if local_search_steps % 1000 == 0:
                self.logger.warn(
                    "Local search took already %d iterations."
                    "Is it maybe stuck in a infinite loop?",
                    local_search_steps)

            # Get neighborhood of the current incumbent
            # by randomly drawing configurations
            changed_inc = False

            # Get one exchange neighborhood returns an iterator (in contrast of
            # the previously returned list).
            all_neighbors = get_one_exchange_neighbourhood(
                incumbent, seed=self.rng.seed())

            for neighbor in all_neighbors:
                s_time = time.time()
                neighbor_array_ = convert_configurations_to_array([neighbor])

                acq_val = self.acquisition_function(neighbor_array_, *args)

                neighbors_looked_at += 1

                time_n.append(time.time() - s_time)

                if acq_val > acq_val_incumbent + self.epsilon:
                    self.logger.debug("Switch to one of the neighbors")
                    incumbent = neighbor
                    acq_val_incumbent = acq_val
                    changed_inc = True
                    break

            if (not changed_inc) or (self.max_iterations != None
                                     and local_search_steps
                                     == self.max_iterations):
                self.logger.debug(
                    "Local search took %d steps and looked at %d configurations. "
                    "Computing the acquisition value for one "
                    "configuration took %f seconds on average.",
                    local_search_steps, neighbors_looked_at, np.mean(time_n))
                break

        return incumbent, acq_val_incumbent
Exemplo n.º 4
0
    def local_search(self, start_point: Configuration):
        """Starts a local search from the given startpoint and quits
        if either the max number of steps is reached or no neighbor
        with an higher improvement was found.

        Parameters:
        ----------

        start_point: Configuration
            The point from where the local search starts

        Returns:
        -------
        incumbent: Configuration
            The best found configuration
        """

        self.intensifier.minR = self.fast_race_minR  # be aggressive here!
        self.intensifier.Adaptive_Capping_Slackfactor = self.fast_race_adaptive_capping_factor

        incumbent = start_point

        local_search_steps = 0
        neighbors_looked_at = 0
        time_n = []
        while True:

            local_search_steps += 1

            # Get neighborhood of the current incumbent
            # by randomly drawing configurations
            changed_inc = False

            # Get one exchange neighborhood returns an iterator (in contrast of
            # the previously returned list).
            all_neighbors = list(
                get_one_exchange_neighbourhood(incumbent,
                                               seed=self.rng.seed()))

            acq_val = self.acquisition_func(all_neighbors)

            sorted_neighbors = sorted(zip(all_neighbors, acq_val),
                                      key=lambda x: x[1],
                                      reverse=True)
            prev_incumbent = incumbent

            for neighbor in all_neighbors[:self.max_neighbors]:
                neighbors_looked_at += 1

                neighbor.origin = "SLS"
                self.logger.debug("Intensify")
                incumbent, inc_perf = self.intensifier.intensify(
                    challengers=[neighbor],
                    incumbent=incumbent,
                    run_history=self.runhistory,
                    aggregate_func=self.aggregate_func,
                    time_bound=0.01,
                    log_traj=False)

                # first improvement SLS
                if incumbent != prev_incumbent:
                    changed_inc = True
                    break

            if not changed_inc:
                self.logger.info(
                    "Local search took %d steps and looked at %d configurations."
                    % (local_search_steps, neighbors_looked_at))
                break

        return incumbent
Exemplo n.º 5
0
    def run(self):
        """Runs the Bayesian optimization loop

        Returns
        ----------
        incumbent: np.array(1, H)
            The best found configuration
        """
        self.stats.start_timing()
        try:
            self.incumbent = self.initial_design.run()
        except FirstRunCrashedException as err:
            if self.scenario.abort_on_first_run_crash:
                raise

        # Main loop
        iteration = 1
        while True:
            if self.scenario.shared_model:
                pSMAC.read(run_history=self.runhistory,
                           output_dirs=self.scenario.input_psmac_dirs,
                           configuration_space=self.config_space,
                           logger=self.logger)

            # model training
            self.logger.info("Model Training")
            X, Y = self.rh2EPM.transform(self.runhistory)
            self.model.train(X, Y)
            self.acquisition_func.update(model=self.model,
                                         eta=self.runhistory.get_cost(
                                             self.incumbent))

            if iteration == 1:
                start_point = self.incumbent
            else:
                # Restart?
                if self.rng.rand() < self.restart_prob:
                    self.logger.info("Restart Search")
                    start_point = self.scenario.cs.sample_configuration()
                else:
                    # pertubate inc
                    self.logger.info("Pertubate Incumbent")
                    start_point = self.incumbent
                    for _ in range(self.pertubation_steps):
                        start_point = random.choice(
                            list(
                                get_one_exchange_neighbourhood(
                                    start_point, seed=self.rng.seed())))

            # SLS
            self.logger.info("SLS")
            local_inc = self.local_search(start_point=start_point)

            # decide global inc
            self.logger.info("Race local incumbent against global incumbent")
            # don't be too aggressive here
            self.intensifier.minR = self.slow_race_minR
            self.intensifier.Adaptive_Capping_Slackfactor = self.slow_race_adaptive_capping_factor
            # log traj
            self.incumbent, inc_perf = self.intensifier.intensify(
                challengers=[local_inc],
                incumbent=self.incumbent,
                run_history=self.runhistory,
                aggregate_func=self.aggregate_func,
                time_bound=0.01,
                log_traj=True)
            if self.incumbent == local_inc:
                self.logger.info("Changed global incumbent!")

            if self.scenario.shared_model:
                pSMAC.write(run_history=self.runhistory,
                            output_directory=self.stats.output_dir,
                            num_run=self.num_run)

            iteration += 1

            self.logger.debug("Remaining budget: %f (wallclock), "
                              "%f (ta costs), %f (target runs)" %
                              (self.stats.get_remaing_time_budget(),
                               self.stats.get_remaining_ta_budget(),
                               self.stats.get_remaining_ta_runs()))

            if self.stats.is_budget_exhausted():
                break

            self.stats.print_stats(debug_out=True)

        return self.incumbent
Exemplo n.º 6
0
    def _do_search(
        self,
        start_points: List[Configuration],
    ) -> List[Tuple[float, Configuration]]:

        # Gather data strucuture for starting points
        if isinstance(start_points, Configuration):
            start_points = [start_points]
        candidates = start_points
        # Compute the acquisition value of the candidates
        num_candidates = len(candidates)
        acq_val_candidates = self.acquisition_function(candidates)
        if num_candidates == 1:
            acq_val_candidates = [acq_val_candidates[0][0]]
        else:
            acq_val_candidates = [a[0] for a in acq_val_candidates]

        # Set up additional variables required to do vectorized local search:
        # whether the i-th local search is still running
        active = [True] * num_candidates
        # number of plateau walks of the i-th local search. Reaching the maximum number is the stopping criterion of
        # the local search.
        n_no_plateau_walk = [0] * num_candidates
        # tracking the number of steps for logging purposes
        local_search_steps = [0] * num_candidates
        # tracking the number of neighbors looked at for logging purposes
        neighbors_looked_at = [0] * num_candidates
        # tracking the number of neighbors generated for logging purposse
        neighbors_generated = [0] * num_candidates
        # how many neighbors were obtained for the i-th local search. Important to map the individual acquisition
        # function values to the correct local search run
        obtain_n = [self.vectorization_min_obtain] * num_candidates
        # Tracking the time it takes to compute the acquisition function
        times = []

        # Set up the neighborhood generators
        neighborhood_iterators = []
        for i, inc in enumerate(candidates):
            neighborhood_iterators.append(
                get_one_exchange_neighbourhood(inc,
                                               seed=self.rng.randint(
                                                   low=0, high=100000)))
            local_search_steps[i] += 1
        # Keeping track of configurations with equal acquisition value for plateau walking
        neighbors_w_equal_acq = [[] for _ in range(num_candidates)
                                 ]  # type: List[List[Configuration]]

        num_iters = 0
        while np.any(active):

            num_iters += 1
            # Whether the i-th local search improved. When a new neighborhood is generated, this is used to determine
            # whether a step was made (improvement) or not (iterator exhausted)
            improved = [False] * num_candidates
            # Used to request a new neighborhood for the candidates of the i-th local search
            new_neighborhood = [False] * num_candidates

            # gather all neighbors
            neighbors = []
            for i, neighborhood_iterator in enumerate(neighborhood_iterators):
                if active[i]:
                    neighbors_for_i = []
                    for j in range(obtain_n[i]):
                        try:
                            n = next(neighborhood_iterator)
                            neighbors_generated[i] += 1
                            neighbors_for_i.append(n)
                        except StopIteration:
                            obtain_n[i] = len(neighbors_for_i)
                            new_neighborhood[i] = True
                            break
                    neighbors.extend(neighbors_for_i)

            if len(neighbors) != 0:
                start_time = time.time()
                acq_val = self.acquisition_function(neighbors)
                end_time = time.time()
                times.append(end_time - start_time)
                if np.ndim(acq_val.shape) == 0:
                    acq_val = [acq_val]

                # Comparing the acquisition function of the neighbors with the acquisition value of the candidate
                acq_index = 0
                # Iterating the all i local searches
                for i in range(num_candidates):
                    if not active[i]:
                        continue
                    # And for each local search we know how many neighbors we obtained
                    for j in range(obtain_n[i]):
                        # The next line is only true if there was an improvement and we basically need to iterate to
                        # the i+1-th local search
                        if improved[i]:
                            acq_index += 1
                        else:
                            neighbors_looked_at[i] += 1

                            # Found a better configuration
                            if acq_val[acq_index] > acq_val_candidates[i]:
                                self.logger.debug(
                                    "Local search %d: Switch to one of the neighbors (after %d configurations).",
                                    i,
                                    neighbors_looked_at[i],
                                )
                                candidates[i] = neighbors[acq_index]
                                acq_val_candidates[i] = acq_val[acq_index]
                                new_neighborhood[i] = True
                                improved[i] = True
                                local_search_steps[i] += 1
                                neighbors_w_equal_acq[i] = []
                                obtain_n[i] = 1
                            # Found an equally well performing configuration, keeping it for plateau walking
                            elif acq_val[acq_index] == acq_val_candidates[i]:
                                neighbors_w_equal_acq[i].append(
                                    neighbors[acq_index])

                            acq_index += 1

            # Now we check whether we need to create new neighborhoods and whether we need to increase the number of
            # plateau walks for one of the local searches. Also disables local searches if the number of plateau walks
            # is reached (and all being switched off is the termination criterion).
            for i in range(num_candidates):
                if not active[i]:
                    continue
                if obtain_n[i] == 0 or improved[i]:
                    obtain_n[i] = 2
                else:
                    obtain_n[i] = obtain_n[i] * 2
                    obtain_n[i] = min(obtain_n[i],
                                      self.vectorization_max_obtain)
                if new_neighborhood[i]:
                    if not improved[i] and n_no_plateau_walk[
                            i] < self.n_steps_plateau_walk:
                        if len(neighbors_w_equal_acq[i]) != 0:
                            candidates[i] = neighbors_w_equal_acq[i][0]
                            neighbors_w_equal_acq[i] = []
                        n_no_plateau_walk[i] += 1
                    if n_no_plateau_walk[i] >= self.n_steps_plateau_walk:
                        active[i] = False
                        continue

                    neighborhood_iterators[i] = get_one_exchange_neighbourhood(
                        candidates[i],
                        seed=self.rng.randint(low=0, high=100000),
                    )

        self.logger.debug(
            "Local searches took %s steps and looked at %s configurations. Computing the acquisition function in "
            "vectorized for took %f seconds on average.",
            local_search_steps,
            neighbors_looked_at,
            np.mean(times),
        )

        return [(a, i) for a, i in zip(acq_val_candidates, candidates)]