Ejemplo n.º 1
0
def test_slhd():
    for i in range(10, 12):  # To test even and odd
        slhd = SymmetricLatinHypercube(dim=3, num_pts=i)
        X = slhd.generate_points()
        assert isinstance(slhd, ExperimentalDesign)
        assert np.all(X.shape == (i, 3))
        assert slhd.num_pts == i
        assert slhd.dim == 3
Ejemplo n.º 2
0
def test_slhd_round():
    num_pts = 10
    dim = 3
    lb = np.array([1, 2, 3])
    ub = np.array([3, 4, 5])
    int_var = np.array([1])

    np.random.seed(0)
    slhd = SymmetricLatinHypercube(dim=dim, num_pts=num_pts)
    X = slhd.generate_points(lb=lb, ub=ub, int_var=int_var)
    assert np.all(np.round(X[:, 1] == X[:, 1]))  # Should be integers
    assert np.all(np.max(X, axis=0) == ub)
    assert np.all(np.min(X, axis=0) == lb)
Ejemplo n.º 3
0
    def run(self):
        """
        Run the optimization
        @return: Nothing
        """

        self.problem = VoltageOptimizationProblem(
            self.circuit,
            self.options,
            self.max_iter,
            callback=self.progress_signal.emit)

        # # (1) Optimization problem
        # # print(data.info)
        #
        # # (2) Experimental design
        # # Use a symmetric Latin hypercube with 2d + 1 samples
        # exp_des = SymmetricLatinHypercube(dim=self.problem.dim, npts=2 * self.problem.dim + 1)
        #
        # # (3) Surrogate model
        # # Use a cubic RBF interpolant with a linear tail
        # surrogate = RBFInterpolant(kernel=CubicKernel, tail=LinearTail, maxp=self.max_eval)
        #
        # # (4) Adaptive sampling
        # # Use DYCORS with 100d candidate points
        # adapt_samp = CandidateDYCORS(data=self, numcand=100 * self.dim)
        #
        # # Use the serial controller (uses only one thread)
        # controller = SerialController(self.objfunction)
        #
        # # (5) Use the sychronous strategy without non-bound constraints
        # strategy = SyncStrategyNoConstraints(worker_id=0,
        #                                      data=self,
        #                                      maxeval=self.max_eval,
        #                                      nsamples=1,
        #                                      exp_design=exp_des,
        #                                      response_surface=surrogate,
        #                                      sampling_method=adapt_samp)
        #
        # controller.strategy = strategy
        #
        # # Run the optimization strategy
        # result = controller.run()
        #
        # # Print the final result
        # print('Best value found: {0}'.format(result.value))
        # print('Best solution found: {0}'.format(np.array_str(result.params[0], max_line_width=np.inf, precision=5,
        #                                                      suppress_small=True)))

        num_threads = 4

        surrogate_model = GPRegressor(dim=self.problem.dim)
        sampler = SymmetricLatinHypercube(dim=self.problem.dim,
                                          num_pts=2 * (self.problem.dim + 1))

        # Create a strategy and a controller
        controller = ThreadController()
        controller.strategy = SRBFStrategy(max_evals=self.max_iter,
                                           opt_prob=self.problem,
                                           exp_design=sampler,
                                           surrogate=surrogate_model,
                                           asynchronous=True,
                                           batch_size=num_threads)

        print("Number of threads: {}".format(num_threads))
        print("Maximum number of evaluations: {}".format(self.max_iter))
        print("Strategy: {}".format(controller.strategy.__class__.__name__))
        print("Experimental design: {}".format(sampler.__class__.__name__))
        print("Surrogate: {}".format(surrogate_model.__class__.__name__))

        # Launch the threads and give them access to the objective function
        for _ in range(num_threads):
            worker = BasicWorkerThread(controller, self.problem.eval)
            controller.launch_worker(worker)

        # Run the optimization strategy
        result = controller.run()

        print('Best value found: {0}'.format(result.value))
        print('Best solution found: {0}\n'.format(
            np.array_str(result.params[0],
                         max_line_width=np.inf,
                         precision=4,
                         suppress_small=True)))

        self.solution = result.params[0]

        # Extract function values from the controller
        self.optimization_values = np.array(
            [o.value for o in controller.fevals])

        # send the finnish signal
        self.progress_signal.emit(0.0)
        self.progress_text.emit('Done!')
        self.done_signal.emit()
Ejemplo n.º 4
0
    def optimize(self):
        """Method used to run the Genetic algorithm

        :return: Returns the best individual and its function value
        :rtype: numpy.array, float
        """
        #  Initialize population
        if isinstance(self.start, np.ndarray):
            if self.start.shape[0] != self.nindividuals or \
                    self.start.shape[1] != self.nvariables:
                raise ValueError("Initial population has incorrect size")
            if any(np.min(self.start, axis=0) >= self.lower_boundary) or \
                    any(np.max(self.start, axis=0) <= self.upper_boundary):
                raise ValueError("Initial population is outside the domain")
            population = self.start
        elif self.start == "SLHD":
            exp_des = SymmetricLatinHypercube(
                self.nvariables, self.nindividuals)
            population = self.lower_boundary + exp_des.generate_points() * \
                (self.upper_boundary - self.lower_boundary)
        elif self.start == "LHD":
            exp_des = LatinHypercube(self.nvariables, self.nindividuals)
            population = self.lower_boundary + exp_des.generate_points() * \
                (self.upper_boundary - self.lower_boundary)
        elif self.start == "Random":
            population = self.lower_boundary + np.random.rand(
                self.nindividuals, self.nvariables) *\
                (self.upper_boundary - self.lower_boundary)
        else:
            raise ValueError("Unknown argument for initial population")

        new_population = []
        #  Round positions
        if len(self.integer_variables) > 0:
            new_population = np.copy(population)
            population[:, self.integer_variables] = np.round(
                population[:, self.integer_variables])
            for i in self.integer_variables:
                ind = np.where(population[:, i] < self.lower_boundary[i])
                population[ind, i] += 1
                ind = np.where(population[:, i] > self.upper_boundary[i])
                population[ind, i] -= 1

        #  Evaluate all individuals
        function_values = self.function(population)
        if len(function_values.shape) == 2:
            function_values = np.squeeze(np.asarray(function_values))

        # Save the best individual
        ind = np.argmin(function_values)
        best_individual = np.copy(population[ind, :])
        best_value = function_values[ind]

        if len(self.integer_variables) > 0:
            population = new_population

        # Main loop
        for _ in range(self.ngenerations):
            # Do tournament selection to select the parents
            competitors = np.random.randint(
                0, self.nindividuals,
                (self.nindividuals, self.tournament_size))
            ind = np.argmin(function_values[competitors], axis=1)
            winner_indices = np.zeros(self.nindividuals, dtype=int)
            for i in range(self.tournament_size):  # This loop is short
                winner_indices[np.where(ind == i)] = \
                    competitors[np.where(ind == i), i]

            parent1 = population[
                winner_indices[0:self.nindividuals//2], :]
            parent2 = population[
                winner_indices[self.nindividuals//2:self.nindividuals], :]

            # Averaging Crossover
            cross = np.where(np.random.rand(
                self.nindividuals//2) < self.p_cross)[0]
            nn = len(cross)  # Number of crossovers
            alpha = np.random.rand(nn, 1)

            # Create the new chromosomes
            parent1_new = np.multiply(alpha, parent1[cross, :]) + \
                np.multiply(1 - alpha, parent2[cross, :])
            parent2_new = np.multiply(alpha, parent2[cross, :]) + \
                np.multiply(1 - alpha, parent1[cross, :])
            parent1[cross, :] = parent1_new
            parent2[cross, :] = parent2_new
            population = np.concatenate((parent1, parent2))

            # Apply mutation
            scale_factors = self.sigma * (
                self.upper_boundary - self.lower_boundary)  # Scale
            perturbation = np.random.randn(
                self.nindividuals, self.nvariables)  # Generate perturbations
            perturbation = np.multiply(
                perturbation, scale_factors)  # Scale accordingly
            perturbation = np.multiply(perturbation, (
                np.random.rand(self.nindividuals,
                               self.nvariables) < self.p_mutation))

            population += perturbation  # Add perturbation
            population = np.maximum(np.reshape(
                self.lower_boundary, (1, self.nvariables)), population)
            population = np.minimum(np.reshape(
                self.upper_boundary, (1, self.nvariables)), population)

            # Round chromosomes
            new_population = []
            if len(self.integer_variables) > 0:
                new_population = np.copy(population)
                population = round_vars(population, self.integer_variables,
                                        self.lower_boundary,
                                        self.upper_boundary)

            # Keep the best individual
            population[0, :] = best_individual

            #  Evaluate all individuals
            function_values = self.function(population)
            if len(function_values.shape) == 2:
                function_values = np.squeeze(np.asarray(function_values))

            # Save the best individual
            ind = np.argmin(function_values)
            best_individual = np.copy(population[ind, :])
            best_value = function_values[ind]

            # Use the positions that are not rounded
            if len(self.integer_variables) > 0:
                population = new_population

        return best_individual, best_value
Ejemplo n.º 5
0
    def pysot_cube(objective,
                   n_trials,
                   n_dim,
                   with_count=False,
                   method=None,
                   design=None):
        """ Minimize
        :param objective:
        :param n_trials:
        :param n_dim:
        :param with_count:
        :return:
        """
        logging.getLogger('pySOT').setLevel(logging.ERROR)

        num_threads = 1
        asynchronous = True

        max_evals = n_trials
        gp = GenericProblem(dim=n_dim, objective=objective)

        if design == 'latin':
            exp_design = LatinHypercube(dim=n_dim, num_pts=2 * (n_dim + 1))
        elif design == 'symmetric':
            exp_design = SymmetricLatinHypercube(dim=n_dim,
                                                 num_pts=2 * (n_dim + 1))
        elif design == 'factorial':
            exp_design = TwoFactorial(dim=n_dim)
        else:
            raise ValueError('design should be latin, symmetric or factorial')

        # Create a strategy and a controller
        #  SRBFStrategy, EIStrategy, DYCORSStrategy,RandomStrategy, LCBStrategy
        controller = ThreadController()
        if method.lower() == 'srbf':
            surrogate = RBFInterpolant(dim=n_dim,
                                       lb=np.array([0.0] * n_dim),
                                       ub=np.array([1.0] * n_dim),
                                       kernel=CubicKernel(),
                                       tail=LinearTail(n_dim))
            controller.strategy = SRBFStrategy(max_evals=max_evals,
                                               opt_prob=gp,
                                               exp_design=exp_design,
                                               surrogate=surrogate,
                                               asynchronous=asynchronous)
        elif method.lower() == 'ei':
            surrogate = GPRegressor(dim=n_dim,
                                    lb=np.array([0.0] * n_dim),
                                    ub=np.array([1.0] * n_dim))
            controller.strategy = EIStrategy(max_evals=max_evals,
                                             opt_prob=gp,
                                             exp_design=exp_design,
                                             surrogate=surrogate,
                                             asynchronous=asynchronous)
        elif method.lower() == 'dycors':
            surrogate = RBFInterpolant(dim=n_dim,
                                       lb=np.array([0.0] * n_dim),
                                       ub=np.array([1.0] * n_dim),
                                       kernel=CubicKernel(),
                                       tail=LinearTail(n_dim))
            controller.strategy = DYCORSStrategy(max_evals=max_evals,
                                                 opt_prob=gp,
                                                 exp_design=exp_design,
                                                 surrogate=surrogate,
                                                 asynchronous=asynchronous)
        elif method.lower() == 'lcb':
            surrogate = GPRegressor(dim=n_dim,
                                    lb=np.array([0.0] * n_dim),
                                    ub=np.array([1.0] * n_dim))
            controller.strategy = LCBStrategy(max_evals=max_evals,
                                              opt_prob=gp,
                                              exp_design=exp_design,
                                              surrogate=surrogate,
                                              asynchronous=asynchronous)
        elif method.lower() == 'random':
            controller.strategy = RandomStrategy(max_evals=max_evals,
                                                 opt_prob=gp)
        else:
            raise ValueError("Didn't recognize method passed to pysot")

        # Launch the threads and give them access to the objective function
        for _ in range(num_threads):
            worker = BasicWorkerThread(controller, gp.eval)
            controller.launch_worker(worker)

        # Run the optimization strategy
        result = controller.run()
        best_x = result.params[0].tolist()
        return (result.value, best_x,
                gp.feval_count) if with_count else (result.value, best_x)
Ejemplo n.º 6
0
Archivo: utils.py Proyecto: dme65/pySOT
    def optimize(self):
        """Method used to run the Genetic algorithm

        :return: Returns the best individual and its function value
        :rtype: numpy.array, float
        """
        #  Initialize population
        if isinstance(self.start, np.ndarray):
            if self.start.shape[0] != self.nindividuals or \
                    self.start.shape[1] != self.nvariables:
                raise ValueError("Initial population has incorrect size")
            if any(np.min(self.start, axis=0) >= self.lower_boundary) or \
                    any(np.max(self.start, axis=0) <= self.upper_boundary):
                raise ValueError("Initial population is outside the domain")
            population = self.start
        elif self.start == "SLHD":
            from pySOT.experimental_design import SymmetricLatinHypercube
            exp_des = SymmetricLatinHypercube(
                self.nvariables, self.nindividuals)
            population = self.lower_boundary + exp_des.generate_points() * \
                (self.upper_boundary - self.lower_boundary)
        elif self.start == "LHD":
            from pySOT.experimental_design import LatinHypercube
            exp_des = LatinHypercube(self.nvariables, self.nindividuals)
            population = self.lower_boundary + exp_des.generate_points() * \
                (self.upper_boundary - self.lower_boundary)
        elif self.start == "Random":
            population = self.lower_boundary + np.random.rand(
                self.nindividuals, self.nvariables) *\
                (self.upper_boundary - self.lower_boundary)
        else:
            raise ValueError("Unknown argument for initial population")

        new_population = []
        #  Round positions
        if len(self.integer_variables) > 0:
            new_population = np.copy(population)
            population[:, self.integer_variables] = np.round(
                population[:, self.integer_variables])
            for i in self.integer_variables:
                ind = np.where(population[:, i] < self.lower_boundary[i])
                population[ind, i] += 1
                ind = np.where(population[:, i] > self.upper_boundary[i])
                population[ind, i] -= 1

        #  Evaluate all individuals
        function_values = self.function(population)
        if len(function_values.shape) == 2:
            function_values = np.squeeze(np.asarray(function_values))

        # Save the best individual
        ind = np.argmin(function_values)
        best_individual = np.copy(population[ind, :])
        best_value = function_values[ind]

        if len(self.integer_variables) > 0:
            population = new_population

        # Main loop
        for _ in range(self.ngenerations):
            # Do tournament selection to select the parents
            competitors = np.random.randint(
                0, self.nindividuals,
                (self.nindividuals, self.tournament_size))
            ind = np.argmin(function_values[competitors], axis=1)
            winner_indices = np.zeros(self.nindividuals, dtype=int)
            for i in range(self.tournament_size):  # This loop is short
                winner_indices[np.where(ind == i)] = \
                    competitors[np.where(ind == i), i]

            parent1 = population[
                winner_indices[0:self.nindividuals//2], :]
            parent2 = population[
                winner_indices[self.nindividuals//2:self.nindividuals], :]

            # Averaging Crossover
            cross = np.where(np.random.rand(
                self.nindividuals//2) < self.p_cross)[0]
            nn = len(cross)  # Number of crossovers
            alpha = np.random.rand(nn, 1)

            # Create the new chromosomes
            parent1_new = np.multiply(alpha, parent1[cross, :]) + \
                np.multiply(1 - alpha, parent2[cross, :])
            parent2_new = np.multiply(alpha, parent2[cross, :]) + \
                np.multiply(1 - alpha, parent1[cross, :])
            parent1[cross, :] = parent1_new
            parent2[cross, :] = parent2_new
            population = np.concatenate((parent1, parent2))

            # Apply mutation
            scale_factors = self.sigma * (
                self.upper_boundary - self.lower_boundary)  # Scale
            perturbation = np.random.randn(
                self.nindividuals, self.nvariables)  # Generate perturbations
            perturbation = np.multiply(
                perturbation, scale_factors)  # Scale accordingly
            perturbation = np.multiply(perturbation, (
                np.random.rand(self.nindividuals,
                               self.nvariables) < self.p_mutation))

            population += perturbation  # Add perturbation
            population = np.maximum(np.reshape(
                self.lower_boundary, (1, self.nvariables)), population)
            population = np.minimum(np.reshape(
                self.upper_boundary, (1, self.nvariables)), population)

            # Round chromosomes
            new_population = []
            if len(self.integer_variables) > 0:
                new_population = np.copy(population)
                population = round_vars(population, self.integer_variables,
                                        self.lower_boundary,
                                        self.upper_boundary)

            # Keep the best individual
            population[0, :] = best_individual

            #  Evaluate all individuals
            function_values = self.function(population)
            if len(function_values.shape) == 2:
                function_values = np.squeeze(np.asarray(function_values))

            # Save the best individual
            ind = np.argmin(function_values)
            best_individual = np.copy(population[ind, :])
            best_value = function_values[ind]

            # Use the positions that are not rounded
            if len(self.integer_variables) > 0:
                population = new_population

        return best_individual, best_value
Ejemplo n.º 7
0
    def __init__(self,
                 worker_id,
                 data,
                 response_surface,
                 maxeval,
                 nsamples,
                 exp_design=None,
                 sampling_method=None,
                 archiving_method=None,
                 extra=None,
                 extra_vals=None,
                 store_sim=False):

        # Check stopping criterion
        self.start_time = time.time()
        if maxeval < 0:  # Time budget
            self.maxeval = np.inf
            self.time_budget = np.abs(maxeval)
        else:
            self.maxeval = maxeval
            self.time_budget = np.inf

        # Import problem information
        self.worker_id = worker_id
        self.data = data
        self.fhat = []
        if response_surface is None:
            for i in range(self.data.nobj):
                self.fhat.append(
                    RBFInterpolant(kernel=CubicKernel,
                                   tail=LinearTail,
                                   maxp=maxeval))  #MOPLS ONLY
        else:
            for i in range(self.data.nobj):
                response_surface.reset()  # Just to be sure!
                self.fhat.append(deepcopy(response_surface))  #MOPLS ONLY

        self.ncenters = nsamples
        self.nsamples = 1
        self.numinit = None
        self.extra = extra
        self.extra_vals = extra_vals
        self.store_sim = store_sim

        # Default to generate sampling points using Symmetric Latin Hypercube
        self.design = exp_design
        if self.design is None:
            if self.data.dim > 50:
                self.design = LatinHypercube(data.dim, data.dim + 1)
            else:
                self.design = SymmetricLatinHypercube(data.dim,
                                                      2 * (data.dim + 1))

        self.xrange = np.asarray(data.xup - data.xlow)

        # algorithm parameters
        self.sigma_min = 0.005
        self.sigma_max = 0.2
        self.sigma_init = 0.2

        self.failtol = max(5, data.dim)
        self.failcount = 0
        self.contol = 5
        self.numeval = 0
        self.status = 0
        self.sigma = 0
        self.resubmitter = RetryStrategy()
        self.xbest = None
        self.fbest = None
        self.fbest_old = None
        self.improvement_prev = 1

        # population of centers and long-term archive
        self.nd_archives = []
        self.new_pop = []
        self.sim_res = []
        if archiving_method is None:
            self.memory_archive = NonDominatedArchive(200)
        else:
            self.memory_archive = archiving_method
        self.evals = []
        self.maxfit = min(200, 20 * self.data.dim)
        self.d_thresh = 1.0

        # Set up search procedures and initialize
        self.sampling = sampling_method
        if self.sampling is None:
            self.sampling = EvolutionaryAlgorithm(data)

        self.check_input()

        # Start with first experimental design
        self.sample_initial()
Ejemplo n.º 8
0
class MoSyncStrategyNoConstraints(BaseStrategy):
    """Parallel Multi-Objective synchronous optimization strategy without non-bound constraints. (GOMORS)

    This class implements the GOMORS Framework
    described by Akhtar and Shoemaker (2016).  After the initial experimental
    design (which is embarrassingly parallel), the optimization
    proceeds in phases.  During each phase, we allow nsamples
    simultaneous function evaluations.  We insist that these
    evaluations run to completion -- if one fails for whatever reason,
    we will resubmit it.  Samples are drawn randomly from a multi-rule
    selection strategy that includes i) Global Evolutionary / Candidate
    search with three selection rules a) Hypervolume, b) Max-min Decision
    Space Distance and c) Max-min Objective Space Distance, and,
     ii) Neighborhood Evolutionary / Candidate Search with hv selection.

    :param worker_id: Start ID in a multi-start setting
    :type worker_id: int
    :param data: Problem parameter data structure
    :type data: Object
    :param response_surface: Surrogate model object
    :type response_surface: Object
    :param maxeval: Stopping criterion. If positive, this is an
                    evaluation budget. If negative, this is a time
                    budget in seconds.
    :type maxeval: int
    :param nsamples: Number of simultaneous fevals allowed
    :type nsamples: int
    :param exp_design: Experimental design
    :type exp_design: Object
    :param sampling_method: Sampling method for finding
        points to evaluate
    :type sampling_method: Object
    :param extra: Points to be added to the experimental design
    :type extra: numpy.array
    :param extra_vals: Values of the points in extra (if known). Use nan for values that are not known.
    :type extra_vals: numpy.array
    """
    def __init__(self,
                 worker_id,
                 data,
                 response_surface,
                 maxeval,
                 nsamples,
                 exp_design=None,
                 sampling_method=None,
                 archiving_method=None,
                 extra=None,
                 extra_vals=None,
                 store_sim=False):

        # Check stopping criterion
        self.start_time = time.time()
        if maxeval < 0:  # Time budget
            self.maxeval = np.inf
            self.time_budget = np.abs(maxeval)
        else:
            self.maxeval = maxeval
            self.time_budget = np.inf

        # Import problem information
        self.worker_id = worker_id
        self.data = data
        self.fhat = []
        if response_surface is None:
            for i in range(self.data.nobj):
                self.fhat.append(
                    RBFInterpolant(kernel=CubicKernel,
                                   tail=LinearTail,
                                   maxp=maxeval))  #MOPLS ONLY
        else:
            for i in range(self.data.nobj):
                response_surface.reset()  # Just to be sure!
                self.fhat.append(deepcopy(response_surface))  #MOPLS ONLY

        self.ncenters = nsamples
        self.nsamples = 1
        self.numinit = None
        self.extra = extra
        self.extra_vals = extra_vals
        self.store_sim = store_sim

        # Default to generate sampling points using Symmetric Latin Hypercube
        self.design = exp_design
        if self.design is None:
            if self.data.dim > 50:
                self.design = LatinHypercube(data.dim, data.dim + 1)
            else:
                self.design = SymmetricLatinHypercube(data.dim,
                                                      2 * (data.dim + 1))

        self.xrange = np.asarray(data.xup - data.xlow)

        # algorithm parameters
        self.sigma_min = 0.005
        self.sigma_max = 0.2
        self.sigma_init = 0.2

        self.failtol = max(5, data.dim)
        self.failcount = 0
        self.contol = 5
        self.numeval = 0
        self.status = 0
        self.sigma = 0
        self.resubmitter = RetryStrategy()
        self.xbest = None
        self.fbest = None
        self.fbest_old = None
        self.improvement_prev = 1

        # population of centers and long-term archive
        self.nd_archives = []
        self.new_pop = []
        self.sim_res = []
        if archiving_method is None:
            self.memory_archive = NonDominatedArchive(200)
        else:
            self.memory_archive = archiving_method
        self.evals = []
        self.maxfit = min(200, 20 * self.data.dim)
        self.d_thresh = 1.0

        # Set up search procedures and initialize
        self.sampling = sampling_method
        if self.sampling is None:
            self.sampling = EvolutionaryAlgorithm(data)

        self.check_input()

        # Start with first experimental design
        self.sample_initial()

    def check_input(self):
        """Checks that the inputs are correct"""

        self.check_common()
        if hasattr(self.data, "eval_ineq_constraints"):
            raise ValueError(
                "Optimization problem has constraints,\n"
                "SyncStrategyNoConstraints can't handle constraints")
        if hasattr(self.data, "eval_eq_constraints"):
            raise ValueError(
                "Optimization problem has constraints,\n"
                "SyncStrategyNoConstraints can't handle constraints")

    def check_common(self):
        """Checks that the inputs are correct"""

        # Check evaluation budget
        if self.extra is None:
            if self.maxeval < self.design.npts:
                raise ValueError(
                    "Experimental design is larger than the evaluation budget")
        else:
            # Check the number of unknown extra points
            if self.extra_vals is None:  # All extra point are unknown
                nextra = self.extra.shape[0]
            else:  # We know the values at some extra points so count how many we don't know
                nextra = np.sum(np.isinf(self.extra_vals[0])) + np.sum(
                    np.isnan(self.extra_vals[0]))

            if self.maxeval < self.design.npts + nextra:
                raise ValueError("Experimental design + extra points "
                                 "exceeds the evaluation budget")

        # Check dimensionality
        if self.design.dim != self.data.dim:
            raise ValueError("Experimental design and optimization "
                             "problem have different dimensions")
        if self.extra is not None:
            if self.data.dim != self.extra.shape[1]:
                raise ValueError("Extra point and optimization problem "
                                 "have different dimensions")
            if self.extra_vals is not None:
                if self.extra.shape[0] != len(self.extra_vals):
                    raise ValueError("Extra point values has the wrong length")

        # Check that the optimization problem makes sense
        check_opt_prob(self.data)

    def proj_fun(self, x):
        """Projects a set of points onto the feasible region

        :param x: Points, of size npts x dim
        :type x: numpy.array
        :return: Projected points
        :rtype: numpy.array
        """

        x = np.atleast_2d(x)
        return round_vars(self.data, x)

    def log_completion(self, record):
        """Record a completed evaluation to the log.

        :param record: Record of the function evaluation
        :type record: Object
        """

        xstr = np.array_str(record.params[0],
                            max_line_width=np.inf,
                            precision=5,
                            suppress_small=True)
        if self.store_sim is True:
            fstr = np.array_str(record.value[0],
                                max_line_width=np.inf,
                                precision=5,
                                suppress_small=True)
        else:
            fstr = np.array_str(record.value,
                                max_line_width=np.inf,
                                precision=5,
                                suppress_small=True)

        if record.feasible:
            logger.info("{} {} @ {}".format("True", fstr, xstr))
        else:
            logger.info("{} {} @ {}".format("False", fstr, xstr))

    def sample_initial(self):
        """Generate and queue an initial experimental design."""

        for fhat in self.fhat:
            fhat.reset()  #MOPLS Only
        self.sigma = self.sigma_init
        self.failcount = 0
        self.xbest = None
        self.fbest_old = None
        self.fbest = None
        for fhat in self.fhat:
            fhat.reset()  #MOPLS Only

        start_sample = self.design.generate_points()
        assert start_sample.shape[1] == self.data.dim, \
            "Dimension mismatch between problem and experimental design"
        start_sample = from_unit_box(start_sample, self.data)

        if self.extra is not None:
            # We know the values if this is a restart, so add the points to the surrogate
            if self.numeval > 0:
                for i in range(len(self.extra_vals)):
                    xx = self.proj_fun(np.copy(self.extra[i, :]))
                    for j in range(self.data.nobj):
                        self.fhat[j].add_point(np.ravel(xx),
                                               self.extra_vals[i, j])
            else:  # Check if we know the values of the points
                if self.extra_vals is None:
                    self.extra_vals = np.nan * np.ones(
                        (self.extra.shape[0], self.data.nobj))

                for i in range(len(self.extra_vals)):
                    xx = self.proj_fun(np.copy(self.extra[i, :]))
                    if np.isnan(self.extra_vals[i, 0]) or np.isinf(
                            self.extra_vals[i, 0]):  # We don't know this value
                        proposal = self.propose_eval(np.ravel(xx))
                        proposal.extra_point_id = i  # Decorate the proposal
                        self.resubmitter.rput(proposal)
                    else:  # We know this value
                        for j in range(self.data.nobj):
                            self.fhat[j].add_point(np.ravel(xx),
                                                   self.extra_vals[i, j])
                        # 2 - Generate a Memory Record of the New Evaluation
                        srec = MemoryRecord(np.copy(np.ravel(xx)),
                                            self.extra_vals[i, :],
                                            self.sigma_init)
                        self.new_pop.append(srec)
                        self.evals.append(srec)

        # Evaluate the experimental design
        for j in range(min(start_sample.shape[0],
                           self.maxeval - self.numeval)):
            start_sample[j, :] = self.proj_fun(
                start_sample[j, :])  # Project onto feasible region
            proposal = self.propose_eval(np.copy(start_sample[j, :]))
            self.resubmitter.rput(proposal)

        if self.extra is not None:
            sample_init = np.vstack((start_sample, self.extra))
        else:
            sample_init = start_sample

        sample_prev = np.copy(sample_init)

        if self.numeval == 0:
            logger.info("=== Start ===")
        elif self.status < self.contol:
            logger.info("=== Connected Start ===")
            print('Connected Restart # ' + str(self.status + 1) + ' initiated')
            # Step 1 - Update connected restart count
            self.status += 1
            # Step 2 - Obtain xvals and fvals of ND points
            front = self.memory_archive.contents
            fvals = [rec.fx for rec in front]
            fvals = np.asarray(fvals)
            xvals = [rec.x for rec in front]
            xvals = np.asarray(xvals)
            # Step 3 - Add ND points to the surrogate
            npts, nobj = fvals.shape
            for i in range(npts):
                for j in range(nobj):
                    self.fhat[j].add_point(xvals[i, :], fvals[i, j])
            # Step 4 -  Add points to the set of previously evaluated points for sampling strategy
            all_xvals = [rec.x for rec in self.evals]
            sample_prev = np.vstack((sample_prev, all_xvals))
        else:
            # Step 4 - Store the Front in a separate archive
            front = self.memory_archive.contents
            self.nd_archives.append(front)
            self.status = 0
            if len(self.nd_archives) == 2:
                logger.info("=== Global Connected Restart ===")
                print('GLOBAL Restart Initiated')
                prev_front = self.nd_archives[0]
                for rec in prev_front:
                    self.memory_archive.add(rec)
                # Step 2 - Obtain xvals and fvals of ND points
                front = self.memory_archive.contents
                fvals = [rec.fx for rec in front]
                fvals = np.asarray(fvals)
                xvals = [rec.x for rec in front]
                xvals = np.asarray(xvals)
                # Step 3 - Add ND points to the surrogate
                npts, nobj = fvals.shape
                for i in range(npts):
                    for j in range(nobj):
                        self.fhat[j].add_point(xvals[i, :], fvals[i, j])
                # Step 4 -  Add points to the set of previously evaluated points for sampling strategy
                all_xvals = [rec.x for rec in self.evals]
                sample_prev = np.vstack((sample_prev, all_xvals))
                self.nd_archives = []
                self.failtol = self.failtol * 2
            else:
                logger.info("=== Independent Restart ===")
                print('INDEPENDENT Restart Initiated')
                self.memory_archive.reset()  #GOMORS only
                self.new_pop = []  #GOMORS Only
                all_xvals = [rec.x for rec in self.evals]
                sample_prev = np.vstack((sample_prev, all_xvals))

        self.sampling.init(sample_init, self.fhat, self.maxeval - self.numeval,
                           sample_prev)

        if self.numinit is None:
            self.numinit = start_sample.shape[0]

        print('Initialization completed successfully')

    def update_archives(self):
        """Update the Tabu list, Tabu Tenure, memory archive and non-dominated front.
        """

        # Step 3 - Add newly Evaluated Points to Memory Archive and update ND_Archives list
        nimprovements = 0
        for rec in self.new_pop:
            self.memory_archive.add(rec)
            nimprovements += self.memory_archive.improvement
        self.new_pop = []
        self.memory_archive.compute_fitness()

        # Step 3b - Adjust failure_count if needed
        if nimprovements == 0:
            print('No Improvement Registered')
            if self.improvement_prev == 0:
                self.failcount += 1
                print('No Improvement - Failure count is: ' +
                      str(self.failcount))
            self.improvement_prev = 0
        else:
            print("Number of Improvements: " + str(nimprovements))
            self.improvement_prev = 1

    def sample_adapt(self):
        """Generate and queue samples from the search strategy"""

        # # Step 1 - Add Newly Evaluated Points to Memory Archive
        self.update_archives()
        front = self.memory_archive.contents
        fvals = [rec.fx for rec in front]
        fvals = np.asarray(fvals)
        xvals = [rec.x for rec in front]
        xvals = np.asarray(xvals)

        fitness = [rec.fitness for rec in front]
        if fitness[0] == POSITIVE_INFINITY:
            idx = random.randint(0, len(fitness) - 1)
        else:
            fitness = np.asarray(fitness)
            idx = np.argmax(fitness)
        self.xbest = xvals[idx, :]
        self.fbest = fvals[idx, :]

        #self.interactive_plotting(fvals)
        print('NUMBER OF EVALUATIONS COMPLETED: ' + str(self.numeval))
        start = time.clock()
        new_points, new_fhvals, fhvals_nd = self.sampling.make_points(
            npts=1,
            xbest=self.xbest,
            xfront=xvals,
            front=fvals,
            proj_fun=self.proj_fun)

        #print(new_points)
        end = time.clock()
        totalTime = end - start
        print('CANDIDATE SELECTION TIME: ' + str(totalTime))

        #self.interactive_plotting(fvals, new_fhvals, fhvals_nd)
        self.save_plot(self.numeval)

        nsamples = 4
        for i in range(nsamples):
            proposal = self.propose_eval(np.copy(np.ravel(new_points[i, :])))
            self.resubmitter.rput(proposal)

    def start_batch(self):
        """Generate and queue a new batch of points"""
        if self.failcount > self.failtol:
            self.sample_initial()
        else:
            self.sample_adapt()

    def propose_action(self):
        """Propose an action
        """
        if self.numeval >= self.maxeval:
            # Save results to Array and Terminate
            X = np.zeros((self.maxeval, self.data.dim + self.data.nobj))
            all_xvals = [rec.x for rec in self.evals]
            all_xvals = np.asarray(all_xvals)
            X[:, 0:self.data.dim] = all_xvals[0:self.maxeval, :]
            all_fvals = [rec.fx for rec in self.evals]
            all_fvals = np.asarray(all_fvals)
            X[:, self.data.dim:self.data.dim +
              self.data.nobj] = all_fvals[0:self.maxeval, :]
            np.savetxt('final.txt', X)
            return self.propose_terminate()
        elif self.resubmitter.num_eval_outstanding == 0:
            # UPDATE MEMORY ARCHIVE
            self.start_batch()
        return self.resubmitter.get()

    def on_complete(self, record):
        """Handle completed function evaluation.

        When a function evaluation is completed we need to ask the constraint
        handler if the function value should be modified which is the case for
        say a penalty method. We also need to print the information to the
        logfile, update the best value found so far and notify the GUI that
        an evaluation has completed.

        :param record: Evaluation record
        """
        self.numeval += 1
        record.worker_id = self.worker_id
        record.worker_numeval = self.numeval
        record.feasible = True
        self.log_completion(record)

        if self.store_sim is True:
            obj_val = np.copy(record.value[0])
            self.sim_res.append(np.copy(record.value[1]))
            np.savetxt('final_simulations.txt', np.asarray(self.sim_res))
        else:
            obj_val = np.copy(record.value)

        # 1 - Update Response Surface Model
        i = 0
        for fhat in self.fhat:
            fhat.add_point(np.copy(record.params[0]), obj_val[i])
            i += 1

        # 2 - Generate a Memory Record of the New Evaluation
        srec = MemoryRecord(np.copy(record.params[0]), obj_val,
                            self.sigma_init)
        self.new_pop.append(srec)
        self.evals.append(srec)

    def interactive_plotting(self, fvals, sel_fhvals, new_fhvals_nd):
        """"If interactive plotting is on,
        """
        maxgen = (self.maxeval - self.numinit) / (self.nsamples *
                                                  self.ncenters)
        curgen = (self.numeval - self.numinit) / (self.nsamples *
                                                  self.ncenters) + 1

        plt.show()
        #plt.plot(self.data.pf[:,0], self.data.pf[:,1], 'g')
        all_fvals = [rec.fx for rec in self.evals]
        all_fvals = np.asarray(all_fvals)
        plt.plot(all_fvals[:, 0], all_fvals[:, 1], 'k+')
        plt.plot(fvals[:, 0], fvals[:, 1], 'b*')
        plt.plot(self.fbest[0], self.fbest[1], 'y>')
        plt.plot(new_fhvals_nd[:, 0], new_fhvals_nd[:, 1], 'ro')
        plt.plot(sel_fhvals[:, 0], sel_fhvals[:, 1], 'cd')
        plt.draw()
        if curgen < maxgen:
            plt.pause(0.001)
        else:
            plt.show()

    def save_plot(self, i):
        """"If interactive plotting is on,
        """
        #plt.figure(i)
        title = 'Number of Evals Completed: ' + str(i)
        front = self.memory_archive.contents
        fvals = [rec.fx for rec in front]
        fvals = np.asarray(fvals)
        maxgen = (self.maxeval - self.numinit) / (self.nsamples *
                                                  self.ncenters)
        curgen = (self.numeval - self.numinit) / (self.nsamples *
                                                  self.ncenters) + 1
        if self.data.pf is not None:
            plt.plot(self.data.pf[:, 0], self.data.pf[:, 1], 'g')
        all_fvals = [rec.fx for rec in self.evals]
        all_fvals = np.asarray(all_fvals)
        plt.plot(all_fvals[:, 0], all_fvals[:, 1], 'k+')
        if fvals.ndim > 1:
            plt.plot(fvals[:, 0], fvals[:, 1], 'b*')
        plt.title(title)
        plt.draw()
        plt.savefig('Final')
        plt.clf()

        all_xvals = [rec.x for rec in self.evals]
        all_xvals = np.asarray(all_xvals)
        npts = all_xvals.shape[0]
        X = np.zeros((npts, self.data.dim + self.data.nobj))
        X[:, 0:self.data.dim] = all_xvals
        X[:, self.data.dim:self.data.dim + self.data.nobj] = all_fvals
        np.savetxt('final.txt', X)

        if self.data.pf is not None:
            plt.plot(self.data.pf[:, 0], self.data.pf[:, 1], 'g')
        if fvals.ndim > 1:
            plt.plot(fvals[:, 0], fvals[:, 1], 'b*')
            plt.title(title)
            plt.draw()
            plt.savefig('Final_front')
            plt.clf()
Ejemplo n.º 9
0
Archivo: pysot.py Proyecto: evhub/bbopt
    def setup_backend(
        self,
        params,
        strategy="SRBF",
        surrogate="RBF",
        design=None,
    ):
        self.opt_problem = BBoptOptimizationProblem(params)

        design_kwargs = dict(dim=self.opt_problem.dim)
        _coconut_case_match_to_1 = design
        _coconut_case_match_check_1 = False
        if _coconut_case_match_to_1 is None:
            _coconut_case_match_check_1 = True
        if _coconut_case_match_check_1:
            self.exp_design = EmptyExperimentalDesign(**design_kwargs)
        if not _coconut_case_match_check_1:
            if _coconut_case_match_to_1 == "latin_hypercube":
                _coconut_case_match_check_1 = True
            if _coconut_case_match_check_1:
                self.exp_design = LatinHypercube(num_pts=2 *
                                                 (self.opt_problem.dim + 1),
                                                 **design_kwargs)
        if not _coconut_case_match_check_1:
            if _coconut_case_match_to_1 == "symmetric_latin_hypercube":
                _coconut_case_match_check_1 = True
            if _coconut_case_match_check_1:
                self.exp_design = SymmetricLatinHypercube(
                    num_pts=2 * (self.opt_problem.dim + 1), **design_kwargs)
        if not _coconut_case_match_check_1:
            if _coconut_case_match_to_1 == "two_factorial":
                _coconut_case_match_check_1 = True
            if _coconut_case_match_check_1:
                self.exp_design = TwoFactorial(**design_kwargs)
        if not _coconut_case_match_check_1:
            _coconut_match_set_name_design_cls = _coconut_sentinel
            _coconut_match_set_name_design_cls = _coconut_case_match_to_1
            _coconut_case_match_check_1 = True
            if _coconut_case_match_check_1:
                if _coconut_match_set_name_design_cls is not _coconut_sentinel:
                    design_cls = _coconut_case_match_to_1
            if _coconut_case_match_check_1 and not (callable(design_cls)):
                _coconut_case_match_check_1 = False
            if _coconut_case_match_check_1:
                self.exp_design = design_cls(**design_kwargs)
        if not _coconut_case_match_check_1:
            raise TypeError(
                "unknown experimental design {_coconut_format_0!r}".format(
                    _coconut_format_0=(design)))

        surrogate_kwargs = dict(dim=self.opt_problem.dim,
                                lb=self.opt_problem.lb,
                                ub=self.opt_problem.ub)
        _coconut_case_match_to_2 = surrogate
        _coconut_case_match_check_2 = False
        if _coconut_case_match_to_2 == "RBF":
            _coconut_case_match_check_2 = True
        if _coconut_case_match_check_2:
            self.surrogate = RBFInterpolant(
                kernel=LinearKernel() if design is None else CubicKernel(),
                tail=ConstantTail(self.opt_problem.dim)
                if design is None else LinearTail(self.opt_problem.dim),
                **surrogate_kwargs)
        if not _coconut_case_match_check_2:
            if _coconut_case_match_to_2 == "GP":
                _coconut_case_match_check_2 = True
            if _coconut_case_match_check_2:
                self.surrogate = GPRegressor(**surrogate_kwargs)
        if not _coconut_case_match_check_2:
            _coconut_match_set_name_surrogate_cls = _coconut_sentinel
            _coconut_match_set_name_surrogate_cls = _coconut_case_match_to_2
            _coconut_case_match_check_2 = True
            if _coconut_case_match_check_2:
                if _coconut_match_set_name_surrogate_cls is not _coconut_sentinel:
                    surrogate_cls = _coconut_case_match_to_2
            if _coconut_case_match_check_2 and not (callable(surrogate_cls)):
                _coconut_case_match_check_2 = False
            if _coconut_case_match_check_2:
                self.surrogate = surrogate_cls(**surrogate_kwargs)
        if not _coconut_case_match_check_2:
            raise TypeError("unknown surrogate {_coconut_format_0!r}".format(
                _coconut_format_0=(surrogate)))

        strategy_kwargs = dict(max_evals=sys.maxsize,
                               opt_prob=self.opt_problem,
                               exp_design=self.exp_design,
                               surrogate=self.surrogate,
                               asynchronous=True,
                               batch_size=1)
        _coconut_case_match_to_3 = strategy
        _coconut_case_match_check_3 = False
        if _coconut_case_match_to_3 == "SRBF":
            _coconut_case_match_check_3 = True
        if _coconut_case_match_check_3:
            self.strategy = SRBFStrategy(**strategy_kwargs)
        if not _coconut_case_match_check_3:
            if _coconut_case_match_to_3 == "EI":
                _coconut_case_match_check_3 = True
            if _coconut_case_match_check_3:
                self.strategy = EIStrategy(**strategy_kwargs)
        if not _coconut_case_match_check_3:
            if _coconut_case_match_to_3 == "DYCORS":
                _coconut_case_match_check_3 = True
            if _coconut_case_match_check_3:
                self.strategy = DYCORSStrategy(**strategy_kwargs)
        if not _coconut_case_match_check_3:
            if _coconut_case_match_to_3 == "LCB":
                _coconut_case_match_check_3 = True
            if _coconut_case_match_check_3:
                self.strategy = LCBStrategy(**strategy_kwargs)
        if not _coconut_case_match_check_3:
            _coconut_match_set_name_strategy_cls = _coconut_sentinel
            _coconut_match_set_name_strategy_cls = _coconut_case_match_to_3
            _coconut_case_match_check_3 = True
            if _coconut_case_match_check_3:
                if _coconut_match_set_name_strategy_cls is not _coconut_sentinel:
                    strategy_cls = _coconut_case_match_to_3
            if _coconut_case_match_check_3 and not (callable(strategy_cls)):
                _coconut_case_match_check_3 = False
            if _coconut_case_match_check_3:
                self.strategy = strategy_cls(**strategy_kwargs)
        if not _coconut_case_match_check_3:
            raise TypeError("unknown strategy {_coconut_format_0!r}".format(
                _coconut_format_0=(strategy)))
Ejemplo n.º 10
0
def example_extra_vals():
    if not os.path.exists("./logfiles"):
        os.makedirs("logfiles")
    if os.path.exists("./logfiles/example_extra_vals.log"):
        os.remove("./logfiles/example_extra_vals.log")
    logging.basicConfig(filename="./logfiles/example_extra_vals.log",
                        level=logging.INFO)

    num_threads = 4
    max_evals = 500

    ackley = Ackley(dim=10)
    num_extra = 10
    extra = np.random.uniform(ackley.lb, ackley.ub, (num_extra, ackley.dim))
    extra_vals = np.nan * np.ones((num_extra, 1))
    for i in range(num_extra):  # Evaluate every second point
        if i % 2 == 0:
            extra_vals[i] = ackley.eval(extra[i, :])

    rbf = RBFInterpolant(dim=ackley.dim,
                         lb=ackley.lb,
                         ub=ackley.ub,
                         kernel=CubicKernel(),
                         tail=LinearTail(ackley.dim))
    slhd = SymmetricLatinHypercube(dim=ackley.dim,
                                   num_pts=2 * (ackley.dim + 1))

    # Create a strategy and a controller
    controller = ThreadController()
    controller.strategy = SRBFStrategy(
        max_evals=max_evals,
        opt_prob=ackley,
        exp_design=slhd,
        surrogate=rbf,
        asynchronous=True,
        batch_size=num_threads,
        extra_points=extra,
        extra_vals=extra_vals,
    )

    print("Number of threads: {}".format(num_threads))
    print("Maximum number of evaluations: {}".format(max_evals))
    print("Strategy: {}".format(controller.strategy.__class__.__name__))
    print("Experimental design: {}".format(slhd.__class__.__name__))
    print("Surrogate: {}".format(rbf.__class__.__name__))

    # Append the known function values to the POAP database since
    # POAP won't evaluate these points
    for i in range(len(extra_vals)):
        if not np.isnan(extra_vals[i]):
            record = EvalRecord(params=(np.ravel(extra[i, :]), ),
                                status="completed")
            record.value = extra_vals[i]
            record.feasible = True
            controller.fevals.append(record)

    # Launch the threads and give them access to the objective function
    for _ in range(num_threads):
        worker = BasicWorkerThread(controller, ackley.eval)
        controller.launch_worker(worker)

    # Run the optimization strategy
    result = controller.run()

    print("Best value found: {0}".format(result.value))
    print("Best solution found: {0}\n".format(
        np.array_str(result.params[0],
                     max_line_width=np.inf,
                     precision=5,
                     suppress_small=True)))
Ejemplo n.º 11
0
    def fit(self, X, y=None, **kwargs):
        """Run training with cross validation.

        :param X: training data
        :param **: parameters to be passed to GridSearchCV
        """

        # wrap for pySOT
        class Target(OptimizationProblem):
            def __init__(self, outer):
                self.outer = outer
                param_def = outer.param_def
                self.lb = np.array([param['lb'] for param in param_def])
                self.ub = np.array([param['ub'] for param in param_def])
                self.dim = len(param_def)
                self.int_var = np.array([
                    idx for idx, param in enumerate(param_def)
                    if param['integer']
                ])
                self.cont_var = np.array([
                    idx for idx, param in enumerate(param_def)
                    if idx not in self.int_var
                ])

            def eval_(self, x):
                print('Eval {0} ...'.format(x))
                param_def = self.outer.param_def
                outer = self.outer
                # prepare parameters grid for gridsearchcv
                param_grid = ({
                    param['name']:
                    [int(x[idx]) if param['integer'] else x[idx]]
                    for idx, param in enumerate(param_def)
                })
                # create gridsearchcv to evaluate the cv
                gs = GridSearchCV(outer.estimator,
                                  param_grid,
                                  refit=False,
                                  **outer.kwargs)
                # never refit during iteration, refit at the end
                gs.fit(X, y=y, **kwargs)
                gs_score = gs.best_score_
                # gridsearchcv score is better when greater
                if not outer.best_score_ or gs_score > outer.best_score_:
                    outer.best_score_ = gs_score
                    outer.best_params_ = gs.best_params_
                # also record history
                outer.params_history_.append(x)
                outer.score_history_.append(gs_score)
                print('Eval {0} => {1}'.format(x, gs_score))
                # pySOT score is the lower the better, so return the negated
                return -gs_score

        # pySOT routine
        # TODO: make this configurable
        target = Target(self)
        rbf = SurrogateUnitBox(RBFInterpolant(dim=target.dim,
                                              kernel=CubicKernel(),
                                              tail=LinearTail(target.dim)),
                               lb=target.lb,
                               ub=target.ub)
        slhd = SymmetricLatinHypercube(dim=target.dim,
                                       num_pts=2 * (target.dim + 1))

        # Create a strategy and a controller
        controller = SerialController(objective=target.eval_)
        controller.strategy = SRBFStrategy(max_evals=self.n_iter,
                                           batch_size=1,
                                           opt_prob=target,
                                           exp_design=slhd,
                                           surrogate=rbf,
                                           asynchronous=False)

        print('Maximum number of evaluations: {0}'.format(self.n_iter))
        print('Strategy: {0}'.format(controller.strategy.__class__.__name__))
        print('Experimental design: {0}'.format(slhd.__class__.__name__))
        print('Surrogate: {0}'.format(rbf.__class__.__name__))

        # Run the optimization strategy
        result = controller.run()

        print('Best value found: {0}'.format(result.value))
        print('Best solution found: {0}\n'.format(
            np.array_str(result.params[0],
                         max_line_width=np.inf,
                         precision=5,
                         suppress_small=True)))
Ejemplo n.º 12
0
class SyncStrategyNoConstraints(BaseStrategy):
    """Parallel synchronous optimization strategy without non-bound constraints.

    This class implements the parallel synchronous SRBF strategy
    described by Regis and Shoemaker.  After the initial experimental
    design (which is embarrassingly parallel), the optimization
    proceeds in phases.  During each phase, we allow nsamples
    simultaneous function evaluations.  We insist that these
    evaluations run to completion -- if one fails for whatever reason,
    we will resubmit it.  Samples are drawn randomly from around the
    current best point, and are sorted according to a merit function
    based on distance to other sample points and predicted function
    values according to the response surface.  After several
    successive significant improvements, we increase the sampling
    radius; after several failures to improve the function value, we
    decrease the sampling radius.  We restart once the sampling radius
    decreases below a threshold.

    :param worker_id: Start ID in a multi-start setting
    :type worker_id: int
    :param data: Problem parameter data structure
    :type data: Object
    :param response_surface: Surrogate model object
    :type response_surface: Object
    :param maxeval: Stopping criterion. If positive, this is an 
                    evaluation budget. If negative, this is a time
                    budget in seconds.
    :type maxeval: int
    :param nsamples: Number of simultaneous fevals allowed
    :type nsamples: int
    :param exp_design: Experimental design
    :type exp_design: Object
    :param sampling_method: Sampling method for finding
        points to evaluate
    :type sampling_method: Object
    :param extra: Points to be added to the experimental design
    :type extra: numpy.array
    :param extra_vals: Values of the points in extra (if known). Use nan for values that are not known.
    :type extra_vals: numpy.array
    """

    def __init__(self, worker_id, data, response_surface, maxeval, nsamples,
                 exp_design=None, sampling_method=None, extra=None, extra_vals=None):

        # Check stopping criterion
        self.start_time = time.time()
        if maxeval < 0:  # Time budget
            self.maxeval = np.inf
            self.time_budget = np.abs(maxeval)
        else:
            self.maxeval = maxeval
            self.time_budget = np.inf

        # Import problem information
        self.worker_id = worker_id
        self.data = data
        self.fhat = response_surface
        if self.fhat is None:
            self.fhat = RBFInterpolant(kernel=CubicKernel, tail=LinearTail, maxp=maxeval)
        self.fhat.reset()  # Just to be sure!

        self.nsamples = nsamples
        self.extra = extra
        self.extra_vals = extra_vals

        # Default to generate sampling points using Symmetric Latin Hypercube
        self.design = exp_design
        if self.design is None:
            if self.data.dim > 50:
                self.design = LatinHypercube(data.dim, data.dim+1)
            else:
                self.design = SymmetricLatinHypercube(data.dim, 2*(data.dim+1))

        self.xrange = np.asarray(data.xup - data.xlow)

        # algorithm parameters
        self.sigma_min = 0.005
        self.sigma_max = 0.2
        self.sigma_init = 0.2

        self.failtol = max(5, data.dim)
        self.succtol = 3

        self.numeval = 0
        self.status = 0
        self.sigma = 0
        self.resubmitter = RetryStrategy()
        self.xbest = None
        self.fbest = np.inf
        self.fbest_old = None

        # Set up search procedures and initialize
        self.sampling = sampling_method
        if self.sampling is None:
            self.sampling = CandidateDYCORS(data)

        self.check_input()

        # Start with first experimental design
        self.sample_initial()

    def check_input(self):
        """Checks that the inputs are correct"""

        self.check_common()
        if hasattr(self.data, "eval_ineq_constraints"):
            raise ValueError("Optimization problem has constraints,\n"
                             "SyncStrategyNoConstraints can't handle constraints")
        if hasattr(self.data, "eval_eq_constraints"):
            raise ValueError("Optimization problem has constraints,\n"
                             "SyncStrategyNoConstraints can't handle constraints")

    def check_common(self):
        """Checks that the inputs are correct"""

        # Check evaluation budget
        if self.extra is None:
            if self.maxeval < self.design.npts:
                raise ValueError("Experimental design is larger than the evaluation budget")
        else:
            # Check the number of unknown extra points
            if self.extra_vals is None:  # All extra point are unknown
                nextra = self.extra.shape[0]
            else:  # We know the values at some extra points so count how many we don't know
                nextra = np.sum(np.isinf(self.extra_vals)) + np.sum(np.isnan(self.extra_vals))

            if self.maxeval < self.design.npts + nextra:
                raise ValueError("Experimental design + extra points "
                                 "exceeds the evaluation budget")

        # Check dimensionality
        if self.design.dim != self.data.dim:
            raise ValueError("Experimental design and optimization "
                             "problem have different dimensions")
        if self.extra is not None:
            if self.data.dim != self.extra.shape[1]:
                raise ValueError("Extra point and optimization problem "
                                 "have different dimensions")
            if self.extra_vals is not None:
                if self.extra.shape[0] != len(self.extra_vals):
                    raise ValueError("Extra point values has the wrong length")

        # Check that the optimization problem makes sense
        check_opt_prob(self.data)

    def proj_fun(self, x):
        """Projects a set of points onto the feasible region

        :param x: Points, of size npts x dim
        :type x: numpy.array
        :return: Projected points
        :rtype: numpy.array
        """

        x = np.atleast_2d(x)
        return round_vars(self.data, x)

    def log_completion(self, record):
        """Record a completed evaluation to the log.

        :param record: Record of the function evaluation
        :type record: Object
        """

        xstr = np.array_str(record.params[0], max_line_width=np.inf,
                            precision=5, suppress_small=True)
        if record.feasible:
            logger.info("{} {:.3e} @ {}".format("True", record.value, xstr))
        else:
            logger.info("{} {:.3e} @ {}".format("False", record.value, xstr))

    def adjust_step(self):
        """Adjust the sampling radius sigma.

        After succtol successful steps, we cut the sampling radius;
        after failtol failed steps, we double the sampling radius.
        """

        # Initialize if this is the first adaptive step
        if self.fbest_old is None:
            self.fbest_old = self.fbest
            return

        # Check if we succeeded at significant improvement
        if self.fbest < self.fbest_old - 1e-3 * math.fabs(self.fbest_old):
            self.status = max(1, self.status + 1)
        else:
            self.status = min(-1, self.status - 1)
        self.fbest_old = self.fbest

        # Check if step needs adjusting
        if self.status <= -self.failtol:
            self.status = 0
            self.sigma /= 2
            logger.info("Reducing sigma")
        if self.status >= self.succtol:
            self.status = 0
            self.sigma = min([2.0 * self.sigma, self.sigma_max])
            logger.info("Increasing sigma")

    def sample_initial(self):
        """Generate and queue an initial experimental design."""

        if self.numeval == 0:
            logger.info("=== Start ===")
        else:
            logger.info("=== Restart ===")
        self.fhat.reset()
        self.sigma = self.sigma_init
        self.status = 0
        self.xbest = None
        self.fbest_old = None
        self.fbest = np.inf
        self.fhat.reset()

        start_sample = self.design.generate_points()
        assert start_sample.shape[1] == self.data.dim, \
            "Dimension mismatch between problem and experimental design"
        start_sample = from_unit_box(start_sample, self.data)

        if self.extra is not None:
            # We know the values if this is a restart, so add the points to the surrogate
            if self.numeval > 0:
                for i in range(len(self.extra_vals)):
                    xx = self.proj_fun(np.copy(self.extra[i, :]))
                    self.fhat.add_point(np.ravel(xx), self.extra_vals[i])
            else:  # Check if we know the values of the points
                if self.extra_vals is None:
                    self.extra_vals = np.nan * np.ones((self.extra.shape[0], 1))

                for i in range(len(self.extra_vals)):
                    xx = self.proj_fun(np.copy(self.extra[i, :]))
                    if np.isnan(self.extra_vals[i]) or np.isinf(self.extra_vals[i]):  # We don't know this value
                        proposal = self.propose_eval(np.ravel(xx))
                        proposal.extra_point_id = i  # Decorate the proposal
                        self.resubmitter.rput(proposal)
                    else:  # We know this value
                        self.fhat.add_point(np.ravel(xx), self.extra_vals[i])

        # Evaluate the experimental design
        for j in range(min(start_sample.shape[0], self.maxeval - self.numeval)):
            start_sample[j, :] = self.proj_fun(start_sample[j, :])  # Project onto feasible region
            proposal = self.propose_eval(np.copy(start_sample[j, :]))
            self.resubmitter.rput(proposal)

        if self.extra is not None:
            self.sampling.init(np.vstack((start_sample, self.extra)), self.fhat, self.maxeval - self.numeval)
        else:
            self.sampling.init(start_sample, self.fhat, self.maxeval - self.numeval)

    def sample_adapt(self):
        """Generate and queue samples from the search strategy"""

        self.adjust_step()
        nsamples = min(self.nsamples, self.maxeval - self.numeval)
        new_points = self.sampling.make_points(npts=nsamples, xbest=np.copy(self.xbest), sigma=self.sigma,
                                               proj_fun=self.proj_fun)
        for i in range(nsamples):
            proposal = self.propose_eval(np.copy(np.ravel(new_points[i, :])))
            self.resubmitter.rput(proposal)

    def start_batch(self):
        """Generate and queue a new batch of points"""

        if self.sigma < self.sigma_min:
            self.sample_initial()
        else:
            self.sample_adapt()

    def propose_action(self):
        """Propose an action"""

        current_time = time.time()
        if self.numeval >= self.maxeval or (current_time - self.start_time) >= self.time_budget:
            return self.propose_terminate()
        elif self.resubmitter.num_eval_outstanding == 0:
            self.start_batch()
        return self.resubmitter.get()

    def on_reply_accept(self, proposal):
        # Transfer the decorations
        if hasattr(proposal, 'extra_point_id'):
            proposal.record.extra_point_id = proposal.extra_point_id

    def on_complete(self, record):
        """Handle completed function evaluation.

        When a function evaluation is completed we need to ask the constraint
        handler if the function value should be modified which is the case for
        say a penalty method. We also need to print the information to the
        logfile, update the best value found so far and notify the GUI that
        an evaluation has completed.

        :param record: Evaluation record
        :type record: Object
        """

        # Check for extra_point decorator
        if hasattr(record, 'extra_point_id'):
            self.extra_vals[record.extra_point_id] = record.value

        self.numeval += 1
        record.worker_id = self.worker_id
        record.worker_numeval = self.numeval
        record.feasible = True
        self.log_completion(record)
        self.fhat.add_point(np.copy(record.params[0]), record.value)
        if record.value < self.fbest:
            self.xbest = np.copy(record.params[0])
            self.fbest = record.value