def __init__(self, model, model_c,space, objective, constraint, acquisition, evaluator, X_init ,ref_point=None, expensive=False,Y_init=None, C_init=None, cost = None, normalize_Y = False, model_update_interval = 1, deterministic=True,true_preference = 0.5): self.true_preference = true_preference self.model_c = model_c self.model = model self.space = space self.objective = objective self.constraint = constraint self.acquisition = acquisition self.utility = acquisition.utility self.evaluator = evaluator self.normalize_Y = normalize_Y self.model_update_interval = model_update_interval self.X = X_init self.Y = Y_init self.C = C_init self.ref_point = ref_point self.deterministic = deterministic self.cost = CostModel(cost) self.model_parameters_iterations = None self.expensive = expensive try: if acquisition.name == "Constrained_Thompson_Sampling": self.sample_from_acq = True self.tag_last_evaluation = False else: self.sample_from_acq = False self.tag_last_evaluation = True except: print("name of acquisition function wasnt provided") self.sample_from_acq = False self.tag_last_evaluation = False
def __init__(self, model, space, objective, acquisition, evaluator, X_init, Y_init=None, cost=None, normalize_Y=False, model_update_interval=1, expectation_utility=None): self.model = model self.space = space self.objective = objective self.acquisition = acquisition self.utility = acquisition.utility self.expectation_utility = expectation_utility self.evaluator = evaluator self.X = X_init self.Y = Y_init self.normalize_Y = normalize_Y self.cost = CostModel(cost) self.model_update_interval = model_update_interval self.historical_optimal_values = [] self.historical_time = [] self.n_attributes = self.model.output_dim self.n_hyps_samples = min(10, self.model.number_of_hyps_samples()) self.n_parameter_samples = 10 self.full_parameter_support = self.utility.parameter_dist.use_full_support self.evaluation_optimizer = GPyOpt.optimization.GeneralOptimizer( optimizer='lbfgs', space=space) # self.context = None self.current_argmax = np.atleast_2d(X_init[0, :])
def setUp(self): kwargs = { 'n_samples': 1000, 'n_burnin': 100, 'subsample_interval': 5, 'step_size': 1e-1, 'leapfrog_steps': 20, 'optimize_restarts': 10, 'num_inducing': 15, 'acquisition_transformation': 'softplus', 'acquisition_jitter': 0.02, 'acquisition_weight': 2.5, 'acquisition_transformation': 'softplus' } ## --- Defaults for some of the tests self.space = Design_space(space=[{ 'name': 'var1', 'type': 'continuous', 'domain': (-10, 10), 'dimensionality': 2 }]) self.cost = CostModel(None) self.arguments_manager = ArgumentsManager(kwargs) self.model = self.arguments_manager.model_creator(model_type='GP', exact_feval=True, space=self.space) self.acquisition_optimizer = AcquisitionOptimizer(self.space) self.acquisition = self.arguments_manager.acquisition_creator( 'EI', self.model, self.space, self.acquisition_optimizer, self.cost)
def __init__(self, model, space, acquisition, evaluator, X_init, Y_init=None, cost=None, normalize_Y=True, model_update_interval=1, de_duplication=False, initial_design_numdata=2): self.model = model self.space = space self.acquisition = acquisition self.evaluator = evaluator self.normalize_Y = normalize_Y self.model_update_interval = model_update_interval self.X = X_init self.Y = Y_init self.cost = CostModel(cost) self.normalization_type = 'stats' ## not added in the API self.de_duplication = de_duplication self.initial_design_numdata = initial_design_numdata self.context = None self.num_acquisitions = 0 self.sample_for_pred = 50 self.prev_m = self.prev_v = np.inf * np.ones( (self.sample_for_pred, self.sample_for_pred, self.sample_for_pred)) self.X_for_pred = self.X_for_prediction()
def __init__(self, model, model_c,space, objective, constraint, acquisition, evaluator, X_init , Y_init=None, C_init=None, cost = None, normalize_Y = False, model_update_interval = 1, true_preference = 0.5): self.true_preference = true_preference self.model_c = model_c self.model = model self.space = space self.objective = objective self.constraint = constraint self.acquisition = acquisition self.utility = acquisition.utility self.evaluator = evaluator self.normalize_Y = normalize_Y self.model_update_interval = model_update_interval self.X = X_init self.Y = Y_init self.C = C_init self.cost = CostModel(cost) self.model_parameters_iterations = None
class BO(object): """ Runner of the multi-attribute Bayesian optimization loop. This class wraps the optimization loop around the different handlers. :param model: GPyOpt model class. :param space: GPyOpt space class. :param objective: GPyOpt objective class. :param acquisition: GPyOpt acquisition class. :param evaluator: GPyOpt evaluator class. :param X_init: 2d numpy array containing the initial inputs (one per row) of the model. :param Y_init: 2d numpy array containing the initial outputs (one per row) of the model. :param cost: GPyOpt cost class (default, none). :param normalize_Y: whether to normalize the outputs before performing any optimization (default, True). :param model_update_interval: interval of collected observations after which the model is updated (default, 1). :param de_duplication: GPyOpt DuplicateManager class. Avoids re-evaluating the objective at previous, pending or infeasible locations (default, False). """ def __init__(self, model, model_c, space, objective, constraint, acquisition, evaluator, X_init, ref_point=None, expensive=False, Y_init=None, C_init=None, cost=None, normalize_Y=False, model_update_interval=1, deterministic=True, true_preference=0.5): self.true_preference = true_preference self.model_c = model_c self.model = model self.space = space self.objective = objective self.constraint = constraint self.acquisition = acquisition self.utility = acquisition.utility self.evaluator = evaluator self.normalize_Y = normalize_Y self.model_update_interval = model_update_interval self.X = X_init self.Y = Y_init self.C = C_init self.ref_point = ref_point self.deterministic = deterministic self.cost = CostModel(cost) self.model_parameters_iterations = None self.expensive = expensive try: if acquisition.name == "Constrained_Thompson_Sampling": self.sample_from_acq = True self.tag_last_evaluation = False else: self.sample_from_acq = False self.tag_last_evaluation = True except: print("name of acquisition function wasnt provided") self.sample_from_acq = False self.tag_last_evaluation = False def suggest_next_locations(self, context=None, pending_X=None, ignored_X=None): """ Run a single optimization step and return the next locations to evaluate the objective. Number of suggested locations equals to batch_size. :param context: fixes specified variables to a particular context (values) for the optimization run (default, None). :param pending_X: matrix of input configurations that are in a pending state (i.e., do not have an evaluation yet) (default, None). :param ignored_X: matrix of input configurations that the user black-lists, i.e., those configurations will not be suggested again (default, None). """ self.model_parameters_iterations = None self.num_acquisitions = 0 self.context = context self._update_model(self.normalization_type) suggested_locations = self._compute_next_evaluations( pending_zipped_X=pending_X, ignored_zipped_X=ignored_X) return suggested_locations # def _value_so_far(self): # """ # Computes E_n[U(f(x_max))|f], where U is the utility function, f is the true underlying ojective function and x_max = argmax E_n[U(f(x))|U]. See # function _marginal_max_value_so_far below. # """ # # output = 0 # support = self.utility.parameter_dist.support # utility_dist = self.utility.parameter_dist.prob_dist # # a = np.reshape(self.objective.evaluate(self._marginal_max_value_so_far())[0],(self.objective.output_dim,)) # # output += self.utility.eval_func(support[i],a)*utility_dist[i] # #print(output) # return output # # # def _marginal_max_value_so_far(self): # """ # Computes argmax E_n[U(f(x))|U] (The abuse of notation can be misleading; note that the expectation is with # respect to the posterior distribution on f after n evaluations) # """ # # def val_func(X): # X = np.atleast_2d(X) # muX = self.model.posterior_mean(X) # return muX # # def val_func_with_gradient(X): # X = np.atleast_2d(X) # muX = self.model.posterior_mean(X) # dmu_dX = self.model.posterior_mean_gradient(X) # valX = np.reshape( muX, (X.shape[0],1)) # dval_dX = dmu_dX # return -valX, -dval_dX # # # argmax = self.acquisition.optimizer.optimize_inner_func(f=val_func, f_df=val_func_with_gradient)[0] # return argmax # # def run_optimization(self, max_iter=1, max_time=np.inf, rep=None, last_step_evaluator=None, eps=1e-8, context=None, verbosity=False, path=None, evaluations_file=None): """ Runs Bayesian Optimization for a number 'max_iter' of iterations (after the initial exploration data) :param max_iter: exploration horizon, or number of acquisitions. If nothing is provided optimizes the current acquisition. :param max_time: maximum exploration horizon in seconds. :param eps: minimum distance between two consecutive x's to keep running the model. :param context: fixes specified variables to a particular context (values) for the optimization run (default, None). :param verbosity: flag to print the optimization results after each iteration (default, False). :param evaluations_file: filename of the file where the evaluated points and corresponding evaluations are saved (default, None). """ self.last_step_evaluator = last_step_evaluator if self.objective is None: raise InvalidConfigError( "Cannot run the optimization loop without the objective function" ) # --- Save the options to print and save the results self.verbosity = verbosity self.evaluations_file = evaluations_file self.context = context self.path = path self.rep = rep # --- Setting up stop conditions self.eps = eps if (max_iter is None) and (max_time is None): self.max_iter = 0 self.max_time = np.inf elif (max_iter is None) and (max_time is not None): self.max_iter = np.inf self.max_time = max_time elif (max_iter is not None) and (max_time is None): self.max_iter = max_iter self.max_time = np.inf else: self.max_iter = max_iter self.max_time = max_time # --- Initial function evaluation and model fitting if self.X is not None and self.Y is None: self.Y, cost_values = self.objective.evaluate(self.X) if self.constraint is not None: self.C, cost_values = self.constraint.evaluate(self.X) if self.cost.cost_type == 'evaluation_time': self.cost.update_cost_model(self.X, cost_values) #self.model.updateModel(self.X,self.Y) # --- Initialize iterations and running time self.time_zero = time.time() self.cum_time = 0 self.num_acquisitions = 0 self.suggested_sample = self.X self.Y_new = self.Y self.Opportunity_Cost = {"Hypervolume": np.array([])} value_so_far = [] # --- Initialize time cost of the evaluations print("MAIN LOOP STARTS") self.true_best_stats = { "true_best": [], "mean_gp": [], "std gp": [], "pf": [], "mu_pf": [], "var_pf": [], "residual_noise": [] } self._update_model() while (self.max_iter > self.num_acquisitions): self.optimize_final_evaluation() print("maKG optimizer") start = time.time() self.suggested_sample = self._compute_next_evaluations() print("self.suggested_sample", self.suggested_sample) if verbosity: if self.constraint is not None: self.verbosity_plot_2D_constrained() else: self.verbosity_plot_2D_unconstrained() # self.suggested_sample = np.array([[0.1,0.0]]) finish = time.time() print("time optimisation point X", finish - start) self.X = np.vstack((self.X, self.suggested_sample)) # --- Evaluate *f* in X, augment Y and update cost function (if needed) self.evaluate_objective() self._update_model() # --- Update current evaluation time and function evaluations self.cum_time = time.time() - self.time_zero self.num_acquisitions += 1 print("optimize_final_evaluation") print("num acquired samples Main Alg: ", self.num_acquisitions) print("self.X, self.Y, self.C , self.Opportunity_Cost", self.X, self.Y, self.C, self.Opportunity_Cost) return self.X, self.Y, self.C, self.Opportunity_Cost # --- Print the desired result in files #if self.evaluations_file is not None: #self.save_evaluations(self.evaluations_file) #file = open('test_file.txt','w') #np.savetxt('test_file.txt',value_so_far) def verbosity_plot_1D(self): ####plots print("generating plots") design_plot = np.linspace(0, 5, 100)[:, None] # precision = [] # for i in range(20): # kg_f = -self.acquisition._compute_acq(design_plot) # precision.append(np.array(kg_f).reshape(-1)) # print("mean precision", np.mean(precision, axis=0), "std precision", np.std(precision, axis=0), "max precision", np.max(precision, axis=0), "min precision",np.min(precision, axis=0)) ac_f = self.expected_improvement(design_plot) Y, _ = self.objective.evaluate(design_plot) C, _ = self.constraint.evaluate(design_plot) pf = self.probability_feasibility_multi_gp(design_plot, self.model_c).reshape( -1, 1) mu_f = self.model.predict(design_plot)[0] bool_C = np.product(np.concatenate(C, axis=1) < 0, axis=1) func_val = Y * bool_C.reshape(-1, 1) kg_f = -self.acquisition._compute_acq(design_plot) design_plot = design_plot.reshape(-1) fig, axs = plt.subplots(3, 2) axs[0, 0].set_title('True Function') axs[0, 0].plot(design_plot, np.array(func_val).reshape(-1)) axs[0, 0].scatter(self.X, self.Y, color="red", label="sampled") # suggested_sample_value , _= self.objective.evaluate(self.suggested_sample) # axs[0, 0].scatter(self.suggested_sample, suggested_sample_value, marker="x", color="red", # label="suggested") axs[0, 0].legend() axs[0, 1].set_title('approximation Acqu Function') axs[0, 1].plot(design_plot, np.array(ac_f).reshape(-1)) axs[0, 1].legend() axs[1, 0].set_title("mu and pf separetely ") axs[1, 0].plot(design_plot, np.array(mu_f).reshape(-1), label="mu") axs[1, 0].plot(design_plot, np.array(pf).reshape(-1), label="pf") axs[1, 0].legend() axs[1, 1].set_title("mu pf") axs[1, 1].plot(design_plot, np.array(mu_f).reshape(-1) * np.array(pf).reshape(-1)) axs[1, 1].legend() axs[2, 1].set_title('approximation kg Function') axs[2, 1].plot(design_plot, np.array(kg_f).reshape(-1)) axs[2, 1].legend() plt.show() def verbosity_plot_2D_unconstrained(self): ####plots print("generating plots") design_plot = initial_design('random', self.space, 10000) # precision = [] # for i in range(20): # kg_f = -self.acquisition._compute_acq(design_plot) # precision.append(np.array(kg_f).reshape(-1)) # print("mean precision", np.mean(precision, axis=0), "std precision", np.std(precision, axis=0), "max precision", np.max(precision, axis=0), "min precision",np.min(precision, axis=0)) func_val, _ = self.objective.evaluate(design_plot) func_val = np.concatenate(func_val, axis=1) mu_f = self.model.posterior_mean(design_plot) var_f = self.model.posterior_variance(design_plot, noise=False) mu_predicted_best = self.model.posterior_mean(self.suggested_sample) HVI = self.acquisition._compute_acq(design_plot) fig, axs = plt.subplots(2, 2) axs[0, 0].set_title('True PF Function') axs[0, 0].scatter(func_val[:, 0], func_val[:, 1]) print("self.suggested_sample", self.suggested_sample) axs[0, 1].set_title("GP(X)") axs[0, 1].scatter( design_plot[:, 0], design_plot[:, 1], c=np.array(mu_f).reshape(-1)) #,c= np.array(HVI).reshape(-1)) axs[0, 1].scatter(self.suggested_sample[:, 0], self.suggested_sample[:, 1], color="magenta") axs[0, 1].legend() print("self.suggested_sample", self.suggested_sample) axs[1, 0].set_title("Var[GP(X)]") axs[1, 0].scatter( design_plot[:, 0], design_plot[:, 1], c=np.array(var_f).reshape(-1)) #,c= np.array(HVI).reshape(-1)) axs[1, 0].scatter(self.suggested_sample[:, 0], self.suggested_sample[:, 1], color="magenta") axs[1, 0].legend() axs[1, 1].set_title("acq(X)") axs[1, 1].scatter( design_plot[:, 0], design_plot[:, 1], c=np.array(HVI).reshape(-1)) #,c= np.array(HVI).reshape(-1)) axs[1, 1].scatter(self.suggested_sample[:, 0], self.suggested_sample[:, 1], color="magenta") axs[1, 1].legend() # axs[1, 1].set_title("mu pf") # axs[1, 1].scatter(design_plot[:,0],design_plot[:,1],c= np.array(mu_f).reshape(-1) * np.array(pf).reshape(-1)) # axs[1, 1].legend() # # axs[1, 0].set_title('Opportunity Cost') # axs[1, 0].plot(range(len(self.Opportunity_Cost)), self.Opportunity_Cost) # axs[1, 0].set_yscale("log") # axs[1, 0].legend() # axs[1, 1].set_title('True PF Function with sampled points') # axs[1, 1].scatter(func_val[:, 0], func_val[:, 1]) # axs[1, 1].scatter(self.Y[0],self.Y[1], color="red", label="sampled") # import os # folder = "IMAGES" # subfolder = "new_branin" # cwd = os.getcwd() # print("cwd", cwd) # time_taken = time.time() # path = cwd + "/" + folder + "/" + subfolder + '/im_' +str(time_taken) +str(self.X.shape[0]) + '.pdf' # if os.path.isdir(cwd + "/" + folder + "/" + subfolder) == False: # os.makedirs(cwd + "/" + folder + "/" + subfolder) # plt.savefig(path) plt.show() def verbosity_plot_2D_constrained(self): ####plots print("generating plots") design_plot = initial_design('random', self.space, 1000) # precision = [] # for i in range(20): # kg_f = -self.acquisition._compute_acq(design_plot) # precision.append(np.array(kg_f).reshape(-1)) # print("mean precision", np.mean(precision, axis=0), "std precision", np.std(precision, axis=0), "max precision", np.max(precision, axis=0), "min precision",np.min(precision, axis=0)) # self.acquisition._gradient_sanity_check_2D(f=self.acquisition._compute_acq, grad_f = self.acquisition.acquisition_Gradients, x_value = self.suggested_sample, delta=1e-4) Y, _ = self.objective.evaluate(design_plot) Y = np.concatenate(Y, axis=1) C, _ = self.constraint.evaluate(design_plot) pf = self.probability_feasibility_multi_gp(design_plot, self.model_c).reshape( -1, 1) mu_f = self.model.posterior_mean(design_plot) bool_C = np.product(np.concatenate(C, axis=1) < 0, axis=1) bool_C = np.array(bool_C, dtype=bool) func_val = Y[bool_C] mu_predicted_best = self.model.posterior_mean(self.suggested_sample) # mu_predicted_final_best = self.model.posterior_mean(self.suggested_final_evaluation) feasable_mu_index = np.array(pf > 0.51, dtype=bool).reshape(-1) # HVI = self.acquisition._compute_acq(design_plot[feasable_mu_index ]) # kg_f = -self.acquisition._compute_acq(design_plot) # HVI_optimiser = self.acquisition._compute_acq(self.suggested_sample) # print("optimiser best", HVI_optimiser, "discretisation best", np.max(np.array(HVI).reshape(-1))) # print("x", design_plot[np.argmax(np.array(HVI).reshape(-1))]) # x_suggested_discretisation = design_plot[np.argmax(np.array(HVI).reshape(-1))] # print("ac with grad info optimised", self.acquisition._compute_acq_withGradients(self.suggested_sample), "ac info optimised",self.acquisition._compute_acq(self.suggested_sample)) # print("best discretisation ac", self.acquisition._compute_acq(x_suggested_discretisation )) # print("best discretisation ac with gradients", self.acquisition._compute_acq_withGradients(x_suggested_discretisation)) # print("mu predicted best opt", mu_predicted_best[0], mu_predicted_best[1]) # print("mu predicted best discretisation", mu_f[0][np.argmax(np.array(HVI).reshape(-1))], mu_f[1][np.argmax(np.array(HVI).reshape(-1))]) fig, axs = plt.subplots(2, 2) axs[0, 0].set_title('True PF Function') axs[0, 0].scatter(func_val[:, 0], func_val[:, 1]) axs[0, 1].set_title("HVI") axs[0, 1].scatter(mu_f[0], mu_f[1], color="green") # , c=np.array(HVI).reshape(-1)) axs[0, 1].scatter(mu_f[0][feasable_mu_index], mu_f[1][feasable_mu_index], color="blue") #, c=np.array(HVI).reshape(-1)) axs[0, 1].scatter(mu_predicted_best[0], mu_predicted_best[1], color="red", label="optimiser best") # axs[0, 1].scatter(mu_predicted_final_best[0], mu_predicted_final_best[1], color="red", label="optimiser final best") #axs[0, 1].scatter(mu_f[0][np.argmax(np.array(HVI).reshape(-1))], mu_f[1][np.argmax(np.array(HVI).reshape(-1))], color="red",label="discretisation best") axs[0, 1].legend() axs[1, 0].set_title('Opportunity Cost') axs[1, 0].plot(range(len(self.Opportunity_Cost["Hypervolume"])), self.Opportunity_Cost["Hypervolume"]) axs[1, 0].set_yscale("log") axs[1, 0].legend() Y_reccomended, _ = self.objective.evaluate(self.suggested_sample) # Y_reccomended = np.concatenate(Y_reccomended, axis=1) axs[1, 1].set_title('True PF Function with sampled points') axs[1, 1].scatter(func_val[:, 0], func_val[:, 1]) axs[1, 1].scatter(Y_reccomended[0], Y_reccomended[1], color="red", label="sampled") axs[1, 1].scatter(self.Y[0], self.Y[1], color="green") # import os # folder = "IMAGES" # subfolder = "new_branin" # cwd = os.getcwd() # print("cwd", cwd) # time_taken = time.time() # path = cwd + "/" + folder + "/" + subfolder + '/im_' +str(time_taken) +str(self.X.shape[0]) + '.pdf' # if os.path.isdir(cwd + "/" + folder + "/" + subfolder) == False: # os.makedirs(cwd + "/" + folder + "/" + subfolder) # plt.savefig(path) plt.show() def optimize_final_evaluation(self): if self.last_step_evaluator is None: if self.constraint is None: sampled_Y = self.model.get_Y_values() sampled_Y = np.concatenate(sampled_Y, axis=1).tolist() sampled_hv = hypervolume(sampled_Y) sampled_HV = sampled_hv.compute(ref_point=self.ref_point) #self.Opportunity_Cost.append(sampled_HV) feasable_Y = sampled_Y self.store_results(feasable_Y) else: sampled_Y = self.model.get_Y_values() sampled_Y = np.concatenate(sampled_Y, axis=1) C_true, C_cost_new = self.constraint.evaluate(self.X, true_val=True) feasable_samples = np.product( np.concatenate(C_true, axis=1) < 0, axis=1) feasable_samples = np.array(feasable_samples, dtype=bool) feasable_Y = sampled_Y[feasable_samples] self.store_results(feasable_Y) else: if self.constraint is not None: suggested_sample = self._compute_final_evaluations() self.suggested_final_evaluation = suggested_sample Y_new, cost_new = self.objective.evaluate(suggested_sample) Y_new = np.concatenate(Y_new, axis=1) C_new, C_cost_new = self.constraint.evaluate(suggested_sample) C_new = np.concatenate(C_new, axis=1) sampled_Y = self.model.get_Y_values() sampled_Y = np.concatenate(sampled_Y, axis=1) sampled_Y = np.vstack((sampled_Y, Y_new)) C_true, C_cost_new = self.constraint.evaluate(self.X, true_val=True) C_true = np.concatenate(C_true, axis=1) C_true = np.vstack((C_true, C_new)) feasable_samples = np.product(C_true < 0, axis=1) feasable_samples = np.array(feasable_samples, dtype=bool) feasable_Y = sampled_Y[feasable_samples] self.store_results(feasable_Y) else: suggested_sample = self._compute_final_evaluations() # self.suggested_final_evaluation = suggested_sample # # Y_new, cost_new = self.objective.evaluate(suggested_sample) # Y_new = np.concatenate(Y_new, axis=1) # # X_train = self.model.get_X_values() # sampled_Y , cost_new = self.objective.evaluate(X_train) # sampled_Y = np.concatenate(sampled_Y, axis=1) # sampled_Y = np.vstack((sampled_Y, Y_new)) # # feasable_Y = sampled_Y # self.store_results(feasable_Y) # design_plot = initial_design('random', self.space, 1000) # ac_f = self.expected_improvement(design_plot) # fig, axs = plt.subplots(2, 2) # axs[0, 0].set_title('True Function') # axs[0, 0].scatter(design_plot[:, 0], design_plot[:, 1], c=np.array(ac_f).reshape(-1)) # # if self.tag_last_evaluation: # start = time.time() # self.acquisition.optimizer.context_manager = ContextManager(self.space, self.context) # if self.constraint is None: # out = self.acquisition.optimizer.optimize_inner_func(f=self.expected_improvement_unconstrained, duplicate_manager=None, num_samples=100) # else: # out = self.acquisition.optimizer.optimize_inner_func(f=self.expected_improvement_constrained, # duplicate_manager=None, num_samples=100) # print("out",out) # suggested_sample = self.space.zip_inputs(out[0]) # stop = time.time() # # axs[0, 0].scatter(suggested_sample[:, 0], suggested_sample[:, 1], color="red") # # plt.show() # print("time EI", stop - start) # # print("self.suggested_sample",suggested_sample) # # --- Evaluate *f* in X, augment Y and update cost function (if needed) # # # if self.deterministic: # if self.constraint is None: # func_val , _ = self.objective.evaluate(suggested_sample, true_val=True) # # print("Y",Y) # Y_true, cost_new = self.objective.evaluate(self.X, true_val=True) # # feasable_Y_data = np.array(Y_true).reshape(-1) # Y_aux = np.concatenate((func_val.reshape(-1), np.array(feasable_Y_data).reshape(-1))) # else: # Y, _ = self.objective.evaluate(suggested_sample, true_val=True) # # print("Y",Y) # # C, _ = self.constraint.evaluate(suggested_sample, true_val=True) # bool_C = np.product(np.concatenate(C, axis=1) < 0, axis=1) # func_val = Y * bool_C.reshape(-1, 1) # # Y_true, cost_new = self.objective.evaluate(self.X ,true_val=True) # C_true, C_cost_new = self.constraint.evaluate(self.X ,true_val=True) # # feasable_Y_data = np.array(Y_true).reshape(-1) * np.product(np.concatenate(C_true, axis=1) < 0, axis=1) # # Y_aux = np.concatenate((func_val.reshape(-1), np.array(feasable_Y_data).reshape(-1))) # # if self.expensive: # self.Opportunity_Cost.append(np.array(np.abs(np.max(Y_aux))).reshape(-1)) # else: # self.true_best_value() # optimum = np.max(np.abs(self.true_best_stats["true_best"])) # print("optimum", optimum) # self.Opportunity_Cost.append(optimum - np.array(np.abs(np.max(Y_aux))).reshape(-1)) # # else: # # print("self.X,suggested_sample",self.X,suggested_sample) # # samples = np.concatenate((self.X,suggested_sample)) # # print("samples", samples) # Y= self.model.posterior_mean(samples) # # print("Y",Y) # pf = self.probability_feasibility_multi_gp(samples, model=self.model_c) # # # print("pf", pf) # func_val = np.array(Y).reshape(-1) * np.array(pf).reshape(-1) # # # print("Y", Y, "pf", pf, "func_val", func_val) # suggested_final_sample = samples[np.argmax(func_val)] # suggested_final_sample = np.array(suggested_final_sample).reshape(-1) # suggested_final_sample = np.array(suggested_final_sample).reshape(1,-1) # # print("suggested_final_sample", suggested_final_sample, "val",np.max(func_val) ) # Y_true, _ = self.objective.evaluate(suggested_final_sample, true_val=True) # # print("Y_true", Y_true) # C_true, _ = self.constraint.evaluate(suggested_final_sample, true_val=True) # # print("C_true", C_true) # bool_C_true = np.product(np.concatenate(C_true, axis=1) < 0, axis=1) # func_val_true = Y_true * bool_C_true.reshape(-1, 1) # # print("func_val_true",func_val_true) # # if self.expensive: # self.Opportunity_Cost.append(np.array(np.abs(np.max(func_val_true))).reshape(-1)) # else: # self.true_best_value() # optimum = np.max(np.abs(self.true_best_stats["true_best"])) # # print("optimum", optimum) # self.Opportunity_Cost.append(optimum - np.array(np.abs(np.max(func_val_true))).reshape(-1)) # # print("OC_i", optimum - np.array(np.abs(np.max(func_val_true))).reshape(-1)) # # else: # samples = self.X # # print("samples", samples) # Y = self.model.posterior_mean(samples) # # print("Y",Y) # pf = self.probability_feasibility_multi_gp(samples, model=self.model_c) # # # print("pf", pf) # func_val = np.array(Y).reshape(-1) * np.array(pf).reshape(-1) # # # print("Y", Y, "pf", pf, "func_val", func_val) # suggested_final_sample = samples[np.argmax(func_val)] # suggested_final_sample = np.array(suggested_final_sample).reshape(-1) # suggested_final_sample = np.array(suggested_final_sample).reshape(1, -1) # # print("suggested_final_sample", suggested_final_sample, "val",np.max(func_val) ) # Y_true, _ = self.objective.evaluate(suggested_final_sample, true_val=True) # # print("Y_true", Y_true) # C_true, _ = self.constraint.evaluate(suggested_final_sample, true_val=True) # # print("C_true", C_true) # bool_C_true = np.product(np.concatenate(C_true, axis=1) < 0, axis=1) # func_val_true = Y_true * bool_C_true.reshape(-1, 1) # # if self.expensive: # self.Opportunity_Cost.append(np.array(np.abs(np.max(func_val_true))).reshape(-1)) # else: # self.true_best_value() # optimum = np.max(np.abs(self.true_best_stats["true_best"])) # # print("optimum", optimum) # self.Opportunity_Cost.append(optimum - np.array(np.abs(np.max(func_val_true))).reshape(-1)) # # print("OC_i", optimum - np.array(np.abs(np.max(func_val_true))).reshape(-1)) # def store_results(self, feasable_Y): if len(feasable_Y) >= 1: sampled_hv = hypervolume(-feasable_Y) sampled_HV = 0 #sampled_hv.compute(ref_point=self.ref_point) else: sampled_HV = 0 self.Opportunity_Cost["Hypervolume"] = np.concatenate( (self.Opportunity_Cost["Hypervolume"], np.array(sampled_HV).reshape(-1))) data = self.Opportunity_Cost if self.path is not None: gen_file = pd.DataFrame.from_dict(data) results_folder = "HVI" path = self.path + "/" + results_folder + '/it_' + str( self.rep) + '.csv' if os.path.isdir(self.path + "/" + results_folder) == False: os.makedirs(self.path + "/" + results_folder) gen_file.to_csv(path_or_buf=path) def expected_improvement_unconstrained(self, X, offset=1e-4): ''' Computes the EI at points X based on existing samples X_sample and Y_sample using a Gaussian process surrogate model. Args: X: Points at which EI shall be computed (m x d). X_sample: Sample locations (n x d). Y_sample: Sample values (n x 1). gpr: A GaussianProcessRegressor fitted to samples. xi: Exploitation-exploration trade-off parameter. Returns: Expected improvements at points X. ''' if self.deterministic: #print("DETERMINISTIC LAST STEP") mu = self.model.posterior_mean(X) sigma = self.model.posterior_variance(X, noise=False) sigma = np.sqrt(sigma).reshape(-1, 1) mu = mu.reshape(-1, 1) # Needed for noise-based model, # otherwise use np.max(Y_sample). # See also section 2.4 in [...] func_val = self.Y #* bool_C.reshape(-1, 1) mu_sample_opt = np.max(func_val) - offset with np.errstate(divide='warn'): imp = mu - mu_sample_opt Z = imp / sigma ei = imp * norm.cdf(Z) + sigma * norm.pdf(Z) ei[sigma == 0.0] = 0.0 return -np.array(ei).reshape(-1) else: print("NOISY LAST STEP") mu = self.model.posterior_mean(X) mu = mu.reshape(-1, 1) return -np.array(mu).reshape(-1) def expected_improvement_constrained(self, X, offset=1e-4): ''' Computes the EI at points X based on existing samples X_sample and Y_sample using a Gaussian process surrogate model. Args: X: Points at which EI shall be computed (m x d). X_sample: Sample locations (n x d). Y_sample: Sample values (n x 1). gpr: A GaussianProcessRegressor fitted to samples. xi: Exploitation-exploration trade-off parameter. Returns: Expected improvements at points X. ''' if self.deterministic: #print("DETERMINISTIC LAST STEP") mu = self.model.posterior_mean(X) sigma = self.model.posterior_variance(X, noise=False) sigma = np.sqrt(sigma).reshape(-1, 1) mu = mu.reshape(-1, 1) # Needed for noise-based model, # otherwise use np.max(Y_sample). # See also section 2.4 in [...] bool_C = np.product(np.concatenate(self.C, axis=1) < 0, axis=1) func_val = self.Y * bool_C.reshape(-1, 1) mu_sample_opt = np.max(func_val) - offset with np.errstate(divide='warn'): imp = mu - mu_sample_opt Z = imp / sigma ei = imp * norm.cdf(Z) + sigma * norm.pdf(Z) ei[sigma == 0.0] = 0.0 pf = self.probability_feasibility_multi_gp(X, self.model_c).reshape( -1, 1) return -(ei * pf) else: print("NOISY LAST STEP") mu = self.model.posterior_mean(X) mu = mu.reshape(-1, 1) pf = self.probability_feasibility_multi_gp(X, self.model_c).reshape( -1, 1) return -(mu * pf) def probability_feasibility_multi_gp(self, x, model, mean=None, cov=None, grad=False, l=0): # print("model",model.output) x = np.atleast_2d(x) Fz = [] for m in range(model.output_dim): Fz.append(self.probability_feasibility(x, model.output[m], grad, l)) Fz = np.product(Fz, axis=0) return Fz def probability_feasibility(self, x, model, mean=None, cov=None, grad=False, l=0): model = model.model # kern = model.kern # X = model.X mean = model.posterior_mean(x) var = model.posterior_variance(x, noise=False) # print("mean",mean,"var",var) std = np.sqrt(var).reshape(-1, 1) # print("std",std) aux_var = np.reciprocal(var) mean = mean.reshape(-1, 1) norm_dist = norm(mean, std) fz = norm_dist.pdf(l) Fz = norm_dist.cdf(l) if grad == True: grad_mean, grad_var = model.predictive_gradients(x) grad_std = (1 / 2.0) * grad_var # cov = kern.K(X, X) + np.eye(X.shape[0]) * 1e-3 # L = scipy.linalg.cholesky(cov, lower=True) # u = scipy.linalg.solve(L, np.eye(X.shape[0])) # Ainv = scipy.linalg.solve(L.T, u) dims = range(x.shape[1]) grad_Fz = [] for d in dims: # K1 = np.diag(np.dot(np.dot(kern.dK_dX(x, X, d), Ainv), kern.dK_dX2(X, x, d))) # K2 = np.diag(kern.dK2_dXdX2(x, x, d, d)) # var_grad = K2 - K1 # var_grad = var_grad.reshape(-1, 1) grd_mean_d = grad_mean[:, d].reshape(-1, 1) grd_std_d = grad_std[:, d].reshape(-1, 1) # print("grd_mean ",grd_mean_d, "var_grad ",grd_std_d ) # print("std",std) # print("aux_var", aux_var) # print("fz",fz) grad_Fz.append(fz * aux_var * (mean * grd_std_d - grd_mean_d * std)) grad_Fz = np.stack(grad_Fz, axis=1) return Fz.reshape(-1, 1), grad_Fz[:, :, 0] else: return Fz.reshape(-1, 1) def evaluate_objective(self): """ Evaluates the objective """ print(1) print(self.suggested_sample) self.Y_new, cost_new = self.objective.evaluate(self.suggested_sample) if self.constraint is not None: self.C_new, C_cost_new = self.constraint.evaluate( self.suggested_sample) for k in range(self.constraint.output_dim): self.C[k] = np.vstack((self.C[k], self.C_new[k])) self.cost.update_cost_model(self.suggested_sample, cost_new) for j in range(self.objective.output_dim): print(self.Y_new[j]) self.Y[j] = np.vstack((self.Y[j], self.Y_new[j])) def compute_current_best(self): current_acqX = self.acquisition.current_compute_acq() return current_acqX def _distance_last_evaluations(self): """ Computes the distance between the last two evaluations. """ return np.sqrt( sum((self.X[self.X.shape[0] - 1, :] - self.X[self.X.shape[0] - 2, :])**2)) def _compute_next_evaluations(self, pending_zipped_X=None, ignored_zipped_X=None, re_use=False): """ Computes the location of the new evaluation (optimizes the acquisition in the standard case). :param pending_zipped_X: matrix of input configurations that are in a pending state (i.e., do not have an evaluation yet). :param ignored_zipped_X: matrix of input configurations that the user black-lists, i.e., those configurations will not be suggested again. :return: """ ## --- Update the context if any self.acquisition.optimizer.context_manager = ContextManager( self.space, self.context, ) print("compute next evaluation") if self.sample_from_acq: print("suggest next location given THOMPSON SAMPLING") candidate_points = initial_design('latin', self.space, 2000) aux_var = self.acquisition._compute_acq(candidate_points) else: if self.constraint is not None: aux_var = self.evaluator.compute_batch(duplicate_manager=None, re_use=re_use, constrained=True) else: aux_var = self.evaluator.compute_batch(duplicate_manager=None, re_use=re_use, constrained=False) return self.space.zip_inputs(aux_var[0]) def _compute_final_evaluations(self, pending_zipped_X=None, ignored_zipped_X=None, re_use=False): """ Computes the location of the new evaluation (optimizes the acquisition in the standard case). :param pending_zipped_X: matrix of input configurations that are in a pending state (i.e., do not have an evaluation yet). :param ignored_zipped_X: matrix of input configurations that the user black-lists, i.e., those configurations will not be suggested again. :return: """ ## --- Update the context if any self.acquisition.optimizer.context_manager = ContextManager( self.space, self.context, ) print("compute next evaluation") if self.sample_from_acq: print("suggest next location given THOMPSON SAMPLING") candidate_points = initial_design('latin', self.space, 2000) aux_var = self.acquisition._compute_acq(candidate_points) else: if self.constraint is not None: aux_var = self.last_step_evaluator.compute_batch( duplicate_manager=None, re_use=re_use, constrained=True) else: aux_var = self.last_step_evaluator.compute_batch( duplicate_manager=None, re_use=re_use, constrained=False) return self.space.zip_inputs(aux_var[0]) #return initial_design('random', self.space, 1) def _update_model(self): """ Updates the model (when more than one observation is available) and saves the parameters (if available). """ if (self.num_acquisitions % self.model_update_interval) == 0: ### --- input that goes into the model (is unziped in case there are categorical variables) X_inmodel = self.space.unzip_inputs(self.X) Y_inmodel = list(self.Y) Y_inmodel = -np.concatenate(Y_inmodel, axis=1) scalarisation_rdn = self.utility.sample_parameter( 1) #np.array([[0.9055273, 0.0944727]])# np.array([[0.5,0.5]]) Utility = self.utility.func(parameter=scalarisation_rdn, y=Y_inmodel) Utility = [Utility] if self.constraint is not None: C_inmodel = list(self.C) self.model_c.updateModel(X_inmodel, C_inmodel) self.model.updateModel(X_inmodel, Utility) ### --- Save parameters of the model #self._save_model_parameter_values() def get_evaluations(self): return self.X.copy(), self.Y.copy() def true_best_value(self): from scipy.optimize import minimize X = initial_design('random', self.space, 1000) fval = self.func_val(X) anchor_point = np.array(X[np.argmin(fval)]).reshape(-1) anchor_point = anchor_point.reshape(1, -1) print("anchor_point", anchor_point) best_design = minimize(self.func_val, anchor_point, method='Nelder-Mead', tol=1e-8).x self.true_best_stats["true_best"].append(self.func_val(best_design)) self.true_best_stats["mean_gp"].append( self.model.posterior_mean(best_design)) self.true_best_stats["std gp"].append( self.model.posterior_variance(best_design, noise=False)) self.true_best_stats["pf"].append( self.probability_feasibility_multi_gp(best_design, self.model_c).reshape(-1, 1)) mean = self.model_c.posterior_mean(best_design) var = self.model_c.posterior_variance(best_design, noise=False) residual_noise = self.model_c.posterior_variance(self.X[1], noise=False) self.true_best_stats["mu_pf"].append(mean) self.true_best_stats["var_pf"].append(var) self.true_best_stats["residual_noise"].append(residual_noise) if False: fig, axs = plt.subplots(3, 2) N = len(np.array(self.true_best_stats["std gp"]).reshape(-1)) GAP = np.array( np.abs( np.abs(self.true_best_stats["true_best"]).reshape(-1) - np.abs(self.true_best_stats["mean_gp"]).reshape(-1)) ).reshape(-1) print("GAP len", len(GAP)) print("N", N) axs[0, 0].set_title('GAP') axs[0, 0].plot(range(N), GAP) axs[0, 0].set_yscale("log") axs[0, 1].set_title('VAR') axs[0, 1].plot(range(N), np.array(self.true_best_stats["std gp"]).reshape(-1)) axs[0, 1].set_yscale("log") axs[1, 0].set_title("PF") axs[1, 0].plot(range(N), np.array(self.true_best_stats["pf"]).reshape(-1)) axs[1, 1].set_title("mu_PF") axs[1, 1].plot( range(N), np.abs(np.array(self.true_best_stats["mu_pf"]).reshape(-1))) axs[1, 1].set_yscale("log") axs[2, 1].set_title("std_PF") axs[2, 1].plot( range(N), np.sqrt(np.array(self.true_best_stats["var_pf"]).reshape(-1))) axs[2, 1].set_yscale("log") axs[2, 0].set_title("Irreducible noise") axs[2, 0].plot( range(N), np.sqrt( np.array( self.true_best_stats["residual_noise"]).reshape(-1))) axs[2, 0].set_yscale("log") plt.show() def func_val(self, x): if len(x.shape) == 1: x = x.reshape(1, -1) Y, _ = self.objective.evaluate(x, true_val=True) C, _ = self.constraint.evaluate(x, true_val=True) Y = np.array(Y).reshape(-1) out = Y.reshape(-1) * np.product(np.concatenate(C, axis=1) < 0, axis=1).reshape(-1) out = np.array(out).reshape(-1) return -out
class BO(object): """ Runner of the multi-attribute Bayesian optimization loop. This class wraps the optimization loop around the different handlers. :param model: GPyOpt model class. :param space: GPyOpt space class. :param objective: GPyOpt objective class. :param acquisition: GPyOpt acquisition class. :param evaluator: GPyOpt evaluator class. :param X_init: 2d numpy array containing the initial inputs (one per row) of the model. :param Y_init: 2d numpy array containing the initial outputs (one per row) of the model. :param cost: GPyOpt cost class (default, none). :param normalize_Y: whether to normalize the outputs before performing any optimization (default, True). :param model_update_interval: interval of collected observations after which the model is updated (default, 1). :param de_duplication: GPyOpt DuplicateManager class. Avoids re-evaluating the objective at previous, pending or infeasible locations (default, False). """ def __init__(self, model, model_c,space, objective, constraint, acquisition, evaluator, X_init , Y_init=None, C_init=None, cost = None, normalize_Y = False, model_update_interval = 1, true_preference = 0.5): self.true_preference = true_preference self.model_c = model_c self.model = model self.space = space self.objective = objective self.constraint = constraint self.acquisition = acquisition self.utility = acquisition.utility self.evaluator = evaluator self.normalize_Y = normalize_Y self.model_update_interval = model_update_interval self.X = X_init self.Y = Y_init self.C = C_init self.cost = CostModel(cost) self.model_parameters_iterations = None def suggest_next_locations(self, context = None, pending_X = None, ignored_X = None): """ Run a single optimization step and return the next locations to evaluate the objective. Number of suggested locations equals to batch_size. :param context: fixes specified variables to a particular context (values) for the optimization run (default, None). :param pending_X: matrix of input configurations that are in a pending state (i.e., do not have an evaluation yet) (default, None). :param ignored_X: matrix of input configurations that the user black-lists, i.e., those configurations will not be suggested again (default, None). """ self.model_parameters_iterations = None self.num_acquisitions = 0 self.context = context self._update_model(self.normalization_type) suggested_locations = self._compute_next_evaluations(pending_zipped_X = pending_X, ignored_zipped_X = ignored_X) return suggested_locations # def _value_so_far(self): # """ # Computes E_n[U(f(x_max))|f], where U is the utility function, f is the true underlying ojective function and x_max = argmax E_n[U(f(x))|U]. See # function _marginal_max_value_so_far below. # """ # # output = 0 # support = self.utility.parameter_dist.support # utility_dist = self.utility.parameter_dist.prob_dist # # a = np.reshape(self.objective.evaluate(self._marginal_max_value_so_far())[0],(self.objective.output_dim,)) # # output += self.utility.eval_func(support[i],a)*utility_dist[i] # #print(output) # return output # # # def _marginal_max_value_so_far(self): # """ # Computes argmax E_n[U(f(x))|U] (The abuse of notation can be misleading; note that the expectation is with # respect to the posterior distribution on f after n evaluations) # """ # # def val_func(X): # X = np.atleast_2d(X) # muX = self.model.posterior_mean(X) # return muX # # def val_func_with_gradient(X): # X = np.atleast_2d(X) # muX = self.model.posterior_mean(X) # dmu_dX = self.model.posterior_mean_gradient(X) # valX = np.reshape( muX, (X.shape[0],1)) # dval_dX = dmu_dX # return -valX, -dval_dX # # # argmax = self.acquisition.optimizer.optimize_inner_func(f=val_func, f_df=val_func_with_gradient)[0] # return argmax # # def run_optimization(self, max_iter = 1, max_time = np.inf, eps = 1e-8, context = None, verbosity=False, evaluations_file = None): """ Runs Bayesian Optimization for a number 'max_iter' of iterations (after the initial exploration data) :param max_iter: exploration horizon, or number of acquisitions. If nothing is provided optimizes the current acquisition. :param max_time: maximum exploration horizon in seconds. :param eps: minimum distance between two consecutive x's to keep running the model. :param context: fixes specified variables to a particular context (values) for the optimization run (default, None). :param verbosity: flag to print the optimization results after each iteration (default, False). :param evaluations_file: filename of the file where the evaluated points and corresponding evaluations are saved (default, None). """ if self.objective is None: raise InvalidConfigError("Cannot run the optimization loop without the objective function") # --- Save the options to print and save the results self.verbosity = verbosity self.evaluations_file = evaluations_file self.context = context # --- Setting up stop conditions self.eps = eps if (max_iter is None) and (max_time is None): self.max_iter = 0 self.max_time = np.inf elif (max_iter is None) and (max_time is not None): self.max_iter = np.inf self.max_time = max_time elif (max_iter is not None) and (max_time is None): self.max_iter = max_iter self.max_time = np.inf else: self.max_iter = max_iter self.max_time = max_time # --- Initial function evaluation and model fitting if self.X is not None and self.Y is None: self.Y, cost_values = self.objective.evaluate(self.X) self.C, cost_values = self.constraint.evaluate(self.X) if self.cost.cost_type == 'evaluation_time': self.cost.update_cost_model(self.X, cost_values) #self.model.updateModel(self.X,self.Y) # --- Initialize iterations and running time self.time_zero = time.time() self.cum_time = 0 self.num_acquisitions = 0 self.suggested_sample = self.X self.Y_new = self.Y self.Opportunity_Cost = [] value_so_far = [] # --- Initialize time cost of the evaluations print("MAIN LOOP STARTS") Opportunity_Cost = [] while (self.max_iter > self.num_acquisitions ): # self._update_model() print("maKG optimizer") start = time.time() self.suggested_sample = initial_design('random', self.space, 1) finish = time.time() print("time optimisation point X", finish - start) if verbosity: self.verbosity_plot_2D() print("self.Opportunity_Cost",self.Opportunity_Cost) self.X = np.vstack((self.X,self.suggested_sample)) # --- Evaluate *f* in X, augment Y and update cost function (if needed) self.evaluate_objective() # --- Update current evaluation time and function evaluations self.cum_time = time.time() - self.time_zero self.num_acquisitions += 1 print("optimize_final_evaluation") self.optimize_final_evaluation() print("self.X, self.Y, self.C , self.Opportunity_Cost",self.X, self.Y, self.C , self.Opportunity_Cost) return self.X, self.Y, self.C , self.Opportunity_Cost # --- Print the desired result in files #if self.evaluations_file is not None: #self.save_evaluations(self.evaluations_file) #file = open('test_file.txt','w') #np.savetxt('test_file.txt',value_so_far) def verbosity_plot_1D(self): ####plots print("generating plots") design_plot = np.linspace(0,5,100)[:,None] # precision = [] # for i in range(20): # kg_f = -self.acquisition._compute_acq(design_plot) # precision.append(np.array(kg_f).reshape(-1)) # print("mean precision", np.mean(precision, axis=0), "std precision", np.std(precision, axis=0), "max precision", np.max(precision, axis=0), "min precision",np.min(precision, axis=0)) ac_f = self.expected_improvement(design_plot) Y, _ = self.objective.evaluate(design_plot) C, _ = self.constraint.evaluate(design_plot) pf = self.probability_feasibility_multi_gp(design_plot, self.model_c).reshape(-1, 1) mu_f = self.model.predict(design_plot)[0] bool_C = np.product(np.concatenate(C, axis=1) < 0, axis=1) func_val = Y * bool_C.reshape(-1, 1) kg_f = -self.acquisition._compute_acq(design_plot) design_plot = design_plot.reshape(-1) fig, axs = plt.subplots(3, 2) axs[0, 0].set_title('True Function') axs[0, 0].plot(design_plot, np.array(func_val).reshape(-1)) axs[0, 0].scatter(self.X, self.Y, color="red", label="sampled") suggested_sample_value , _= self.objective.evaluate(self.suggested_sample) axs[0, 0].scatter(self.suggested_sample, suggested_sample_value, marker="x", color="red", label="suggested") axs[0, 0].legend() axs[0, 1].set_title('approximation Acqu Function') axs[0, 1].plot(design_plot, np.array(ac_f).reshape(-1)) axs[0, 1].legend() axs[1, 0].set_title("mu and pf separetely ") axs[1, 0].plot(design_plot, np.array(mu_f).reshape(-1) , label="mu") axs[1, 0].plot(design_plot, np.array(pf).reshape(-1), label="pf") axs[1, 0].legend() axs[1, 1].set_title("mu pf") axs[1, 1].plot(design_plot, np.array(mu_f).reshape(-1) * np.array(pf).reshape(-1)) axs[1, 1].legend() axs[2, 1].set_title('approximation kg Function') axs[2, 1].plot(design_plot, np.array(kg_f).reshape(-1)) axs[2, 1].legend() plt.show() def verbosity_plot_2D(self): ####plots print("generating plots") design_plot = initial_design('random', self.space, 1000) # precision = [] # for i in range(20): # kg_f = -self.acquisition._compute_acq(design_plot) # precision.append(np.array(kg_f).reshape(-1)) # print("mean precision", np.mean(precision, axis=0), "std precision", np.std(precision, axis=0), "max precision", np.max(precision, axis=0), "min precision",np.min(precision, axis=0)) ac_f = self.expected_improvement(design_plot) Y, _ = self.objective.evaluate(design_plot) C, _ = self.constraint.evaluate(design_plot) pf = self.probability_feasibility_multi_gp(design_plot, self.model_c).reshape(-1, 1) mu_f = self.model.predict(design_plot)[0] bool_C = np.product(np.concatenate(C, axis=1) < 0, axis=1) func_val = Y * bool_C.reshape(-1, 1) # kg_f = -self.acquisition._compute_acq(design_plot) fig, axs = plt.subplots(2, 2) axs[0, 0].set_title('True Function') axs[0, 0].scatter(design_plot[:, 0], design_plot[:, 1], c=np.array(func_val).reshape(-1)) axs[0, 0].scatter(self.X[:, 0], self.X[:, 1], color="red", label="sampled") #suggested_sample_value = self.objective.evaluate(self.suggested_sample) axs[0, 0].scatter(self.suggested_sample[:,0], self.suggested_sample[:,1], marker="x", color="red", label="suggested") axs[0, 0].legend() axs[0, 1].set_title('approximation Acqu Function') axs[0, 1].scatter(design_plot[:,0],design_plot[:,1], c=np.array(ac_f).reshape(-1)) axs[0, 1].legend() # axs[1, 0].set_title("KG") # axs[1, 0].scatter(design_plot[:,0],design_plot[:,1],c= np.array(kg_f).reshape(-1)) # axs[1, 0].legend() axs[1, 1].set_title("mu pf") axs[1, 1].scatter(design_plot[:,0],design_plot[:,1],c= np.array(mu_f).reshape(-1) * np.array(pf).reshape(-1)) axs[1, 1].legend() # axs[2, 1].set_title('approximation kg Function') # axs[2, 1].scatter(design_plot, np.array(kg_f).reshape(-1)) # axs[2, 1].legend() # import os # folder = "IMAGES" # subfolder = "new_branin" # cwd = os.getcwd() # print("cwd", cwd) # time_taken = time.time() # path = cwd + "/" + folder + "/" + subfolder + '/im_' +str(time_taken) +str(self.X.shape[0]) + '.pdf' # if os.path.isdir(cwd + "/" + folder + "/" + subfolder) == False: # os.makedirs(cwd + "/" + folder + "/" + subfolder) # plt.savefig(path) plt.show() def optimize_final_evaluation(self): # design_plot = initial_design('random', self.space, 1000) # ac_f = self.expected_improvement(design_plot) # fig, axs = plt.subplots(2, 2) # axs[0, 0].set_title('True Function') # axs[0, 0].scatter(design_plot[:, 0], design_plot[:, 1], c=np.array(ac_f).reshape(-1)) # start = time.time() # self.acquisition.optimizer.context_manager = ContextManager(self.space, self.context) # out = self.acquisition.optimizer.optimize(f=self.expected_improvement, duplicate_manager=None, re_use=False, num_samples=20, verbose=False) # suggested_sample = self.space.zip_inputs(out[0]) # stop = time.time() # # axs[0, 0].scatter(suggested_sample[:, 0], suggested_sample[:, 1], color="red") # # plt.show() # print("time EI", stop - start) # # print("self.suggested_sample",suggested_sample) # # --- Evaluate *f* in X, augment Y and update cost function (if needed) # Y, _ = self.objective.evaluate(suggested_sample) # # print("Y",Y) # C, _ = self.constraint.evaluate(suggested_sample) # # bool_C = np.product(np.concatenate(C, axis=1) < 0, axis=1) # func_val = Y * bool_C.reshape(-1, 1) feasable_Y_data = np.array(self.Y).reshape(-1) * np.product(np.concatenate(self.C, axis=1) < 0, axis=1) # print("suggested_sample",suggested_sample, "feasable_Y_data",func_val) # print("C[-1, :]",C[-1, :]) # feasable_point = bool_C Y_aux = np.array(feasable_Y_data).reshape(-1) self.Opportunity_Cost.append(np.max(Y_aux)) def expected_improvement(self, X, offset=0.0): ''' Computes the EI at points X based on existing samples X_sample and Y_sample using a Gaussian process surrogate model. Args: X: Points at which EI shall be computed (m x d). X_sample: Sample locations (n x d). Y_sample: Sample values (n x 1). gpr: A GaussianProcessRegressor fitted to samples. xi: Exploitation-exploration trade-off parameter. Returns: Expected improvements at points X. ''' mu, sigma = self.model.predict(X) # sigma = np.sqrt(sigma).reshape(-1, 1) mu = mu.reshape(-1,1) # Needed for noise-based model, # otherwise use np.max(Y_sample). # See also section 2.4 in [...] bool_C = np.product(np.concatenate(self.C, axis=1) < 0, axis=1) func_val = self.Y * bool_C.reshape(-1, 1) mu_sample_opt = np.max(func_val) - offset # with np.errstate(divide='warn'): # imp = mu - mu_sample_opt # Z = imp / sigma # ei = imp * norm.cdf(Z) + sigma * norm.pdf(Z) # ei[sigma == 0.0] = 0.0 pf = self.probability_feasibility_multi_gp(X,self.model_c).reshape(-1,1) pf[pf<0.50] = 0 return -(mu *pf ) def probability_feasibility_multi_gp(self, x, model, mean=None, cov=None, grad=False, l=0): # print("model",model.output) x = np.atleast_2d(x) if grad == False: Fz = [] for m in range(model.output_dim): Fz.append(self.probability_feasibility( x, model.output[m], grad, l)) Fz = np.product(Fz,axis=0) return Fz else: Fz = [] grad_Fz = [] for m in range(model.output_dim): # print("model.output[m]",model.output[m]) # print("mean[m]",mean[m]) # print("cov[m]",cov[m]) Fz_aux, grad_Fz_aux = self.probability_feasibility( x, model.output[m]) Fz.append(Fz_aux) grad_Fz.append(grad_Fz_aux) # print("np.array(Fz)", np.array(Fz), "grad_Fz", np.array(grad_Fz)) grad_Fz = self.product_gradient_rule(func = np.array(Fz), grad = np.array(grad_Fz)) # print("output grad_Fz", grad_Fz) Fz = np.product(Fz, axis=0) return Fz, grad_Fz def probability_feasibility(self, x, model, mean=None, cov=None, grad=False, l=0): model = model.model # kern = model.kern # X = model.X mean, cov = model.predict(x, full_cov=True) var = np.diag(cov).reshape(-1, 1) std = np.sqrt(var).reshape(-1, 1) aux_var = np.reciprocal(var) mean = mean.reshape(-1, 1) norm_dist = norm(mean, std) fz = norm_dist.pdf(l) Fz = norm_dist.cdf(l) if grad == True: grad_mean , grad_var = model.predictive_gradients(x) grad_std = (1/2.0)*grad_var # cov = kern.K(X, X) + np.eye(X.shape[0]) * 1e-3 # L = scipy.linalg.cholesky(cov, lower=True) # u = scipy.linalg.solve(L, np.eye(X.shape[0])) # Ainv = scipy.linalg.solve(L.T, u) dims = range(x.shape[1]) grad_Fz = [] for d in dims: # K1 = np.diag(np.dot(np.dot(kern.dK_dX(x, X, d), Ainv), kern.dK_dX2(X, x, d))) # K2 = np.diag(kern.dK2_dXdX2(x, x, d, d)) # var_grad = K2 - K1 # var_grad = var_grad.reshape(-1, 1) grd_mean_d = grad_mean[:, d].reshape(-1, 1) grd_std_d = grad_std[:, d].reshape(-1, 1) # print("grd_mean ",grd_mean_d, "var_grad ",grd_std_d ) # print("std",std) # print("aux_var", aux_var) # print("fz",fz) grad_Fz.append(fz * aux_var * (mean * grd_std_d - grd_mean_d * std)) grad_Fz = np.stack(grad_Fz, axis=1) return Fz.reshape(-1, 1), grad_Fz[:, :, 0] else: return Fz.reshape(-1, 1) def evaluate_objective(self): """ Evaluates the objective """ print(1) print(self.suggested_sample) self.Y_new, cost_new = self.objective.evaluate(self.suggested_sample) self.C_new, C_cost_new = self.constraint.evaluate(self.suggested_sample) self.cost.update_cost_model(self.suggested_sample, cost_new) for j in range(self.objective.output_dim): print(self.Y_new[j]) self.Y[j] = np.vstack((self.Y[j],self.Y_new[j])) for k in range(self.constraint.output_dim): print(self.C_new[k]) self.C[k] = np.vstack((self.C[k],self.C_new[k])) def compute_current_best(self): current_acqX = self.acquisition.current_compute_acq() return current_acqX def _distance_last_evaluations(self): """ Computes the distance between the last two evaluations. """ return np.sqrt(sum((self.X[self.X.shape[0]-1,:]-self.X[self.X.shape[0]-2,:])**2)) def _compute_next_evaluations(self, pending_zipped_X=None, ignored_zipped_X=None, re_use=False): """ Computes the location of the new evaluation (optimizes the acquisition in the standard case). :param pending_zipped_X: matrix of input configurations that are in a pending state (i.e., do not have an evaluation yet). :param ignored_zipped_X: matrix of input configurations that the user black-lists, i.e., those configurations will not be suggested again. :return: """ ## --- Update the context if any self.acquisition.optimizer.context_manager = ContextManager(self.space, self.context) aux_var = self.evaluator.compute_batch(duplicate_manager=None, re_use=re_use) ### We zip the value in case there are categorical variables return self.space.zip_inputs(aux_var[0]) #return initial_design('random', self.space, 1) def _update_model(self): """ Updates the model (when more than one observation is available) and saves the parameters (if available). """ if (self.num_acquisitions%self.model_update_interval)==0: ### --- input that goes into the model (is unziped in case there are categorical variables) X_inmodel = self.space.unzip_inputs(self.X) Y_inmodel = list(self.Y) C_inmodel = list(self.C) self.model.updateModel(X_inmodel, Y_inmodel) self.model_c.updateModel(X_inmodel, C_inmodel) ### --- Save parameters of the model #self._save_model_parameter_values() def get_evaluations(self): return self.X.copy(), self.Y.copy()
def __init__(self, estimator, domain=None, constraints=None, cost_withGradients=None, model_type="GP", X=None, Y=None, initial_design_numdata=5, initial_design_type="random", acquisition_type="EI", normalize_Y=True, exact_feval=False, acquisition_optimizer_type="lbfgs", model_update_interval=1, evaluator_type="sequential", batch_size=1, num_cores=1, verbosity_model=False, verbosity=False, de_duplication=False, max_iter=50, refit=True, cv=None, scoring=None, n_jobs=1, verbose=False, **kwargs): """Initialise the estimator.""" # super(BayesianOptimization, self).__init__( # f=f, domain=domain, constraints=constraints, # cost_withGradients=cost_withGradients, model_type=model_type, # X=X, Y=Y, initial_design_numdata=initial_design_numdata, # initial_design_type=initial_design_type, # acquisition_type=acquisition_type, normalize_Y=normalize_Y, # exact_feval=exact_feval, # acquisition_optimizer_type=acquisition_optimizer_type, # model_update_interval=model_update_interval, # evaluator_type=evaluator_type, batch_size=batch_size, # num_cores=num_cores, # verbosity=verbosity, verbosity_model=verbosity_model, # maximize=True, de_duplication=de_duplication, **kwargs) self.modular_optimization = False self.initial_iter = True self.verbosity = verbosity self.verbosity_model = verbosity_model self.model_update_interval = model_update_interval self.de_duplication = de_duplication self.kwargs = kwargs # --- Handle the arguments passed via kargs self.problem_config = ArgumentsManager(kwargs) # --- CHOOSE design space self.constraints = constraints self.domain = domain or {} self.space = Design_space(self.domain, self.constraints) # --- CHOOSE objective function self.objective_name = kwargs.get("objective_name", "") or "no_name" self.batch_size = batch_size self.num_cores = num_cores # self.maximize = True # self.objective = None # --- CHOOSE the cost model self.cost = CostModel(cost_withGradients) # --- CHOOSE initial design self.X = X # parameters self.Y = Y # evaluation self.initial_design_type = initial_design_type self.initial_design_numdata = initial_design_numdata # --- CHOOSE the model type. If an instance of a GPyOpt model is passed (possibly user defined), it is used. # note that this 2 options are not used with the predefined model self.model_type = model_type self.exact_feval = exact_feval self.normalize_Y = normalize_Y if "model" in self.kwargs and isinstance(kwargs["model"], GPyOpt.models.base.BOModel): self.model = kwargs["model"] self.model_type = "User defined model used." if self.verbose: print("Using a model defined by the used.") else: self.model = self._model_chooser() # --- CHOOSE the acquisition optimizer_type # This states how the discrete variables are handled (exact search or rounding) self.acquisition_optimizer_type = acquisition_optimizer_type self.acquisition_optimizer = AcquisitionOptimizer( self.space, self.acquisition_optimizer_type, model=self.model) # --- CHOOSE acquisition function. If an instance of an acquisition is passed (possibly user defined), it is used. self.acquisition_type = acquisition_type if "acquisition" in self.kwargs and isinstance( kwargs["acquisition"], GPyOpt.acquisitions.AcquisitionBase): self.acquisition = kwargs["acquisition"] self.acquisition_type = "User defined acquisition used." if self.verbose: print("Using an acquisition defined by the used.") else: self.acquisition = self._acquisition_chooser() # --- CHOOSE evaluator method self.evaluator_type = evaluator_type self.evaluator = self._evaluator_chooser() # --- Create optimization space self.cost = CostModel(self.cost) self.normalization_type = "stats" # not added in the API self.estimator = estimator self.cv = cv self.max_iter = max_iter self.refit = refit self.scoring = scoring self.verbose = verbose self.n_jobs = n_jobs
def __init__( self, f, domain=None, constraints=None, cost_withGradients=None, model_type='GP', X=None, Y=None, initial_design_numdata=5, initial_design_type='random', acquisition_type='EI', normalize_Y=True, exact_feval=False, acquisition_optimizer_type='lbfgs', model_update_interval=1, evaluator_type='sequential', batch_size=1, num_cores=1, verbosity=False, verbosity_model=False, maximize=False, de_duplication=False, **kwargs ): self.modular_optimization = False self.initial_iter = True self.verbosity = verbosity self.verbosity_model = verbosity_model self.model_update_interval = model_update_interval self.de_duplication = de_duplication self.kwargs = kwargs # --- Handle the arguments passed via kwargs self.problem_config = FlexibleArgumentsManager(kwargs) # --- CHOOSE design space self.constraints = constraints self.domain = domain self.space = Design_space(self.domain, self.constraints) # hoge = self.kwargs.get('domain_without_time', []) # import numpy as np # print("domain_without_time = {}".format(np.asarray(hoge))) # print("domain_with_time = {}".format(np.asarray(self.domain))) self.space_without_time = Design_space(self.kwargs.get('domain_without_time', []), self.constraints) # --- CHOOSE objective function self.maximize = maximize if 'objective_name' in kwargs: self.objective_name = kwargs['objective_name'] else: self.objective_name = 'no_name' self.batch_size = batch_size self.num_cores = num_cores if f is not None: self.f = self._sign(f) self.objective = SingleObjective(self.f, self.batch_size, self.objective_name) else: self.f = None self.objective = None # --- CHOOSE the cost model self.cost = CostModel(cost_withGradients) # --- CHOOSE initial design self.X = X self.Y = Y self.initial_design_type = initial_design_type self.initial_design_numdata = initial_design_numdata self._init_design_chooser() # --- CHOOSE the model type. # If an instance of a GPyOpt model is passed (possibly user defined), # it is used. self.model_type = model_type self.exact_feval = exact_feval # note that this 2 options are not used with the predefined model self.normalize_Y = normalize_Y if 'model' in self.kwargs: if isinstance(kwargs['model'], GPyOpt.models.base.BOModel): self.model = kwargs['model'] self.model_type = 'User defined model used.' print('Using a model defined by the used.') else: self.model = self._model_chooser() else: self.model = self._model_chooser() # --- CHOOSE the acquisition optimizer_type # This states how the discrete variables are handled (exact search or rounding) self.acquisition_optimizer_type = acquisition_optimizer_type if 'is_continuous_time_varying' in self.kwargs and self.kwargs['is_continuous_time_varying']: print("is continuous Varying!") self.acquisition_optimizer = FlexibleAcquisitionOptimizer( self.space_without_time, self.acquisition_optimizer_type, model=self.model, ) # more arguments may come here else: self.acquisition_optimizer = FlexibleAcquisitionOptimizer( self.space, self.acquisition_optimizer_type, model=self.model ) # --- CHOOSE acquisition function. # If an instance of an acquisition is passed (possibly user defined), it is used. self.acquisition_type = acquisition_type if 'acquisition' in self.kwargs: if isinstance(kwargs['acquisition'], GPyOpt.acquisitions.AcquisitionBase): self.acquisition = kwargs['acquisition'] self.acquisition_type = 'User defined acquisition used.' print('Using an acquisition defined by the used.') else: self.acquisition = self._acquisition_chooser() else: self.acquisition = self.acquisition = self._acquisition_chooser() # --- CHOOSE evaluator method self.evaluator_type = evaluator_type self.evaluator = self._evaluator_chooser() # --- Create optimization space super(FlexibleBayesianOptimization, self).__init__( model=self.model, space=self.space, objective=self.objective, acquisition=self.acquisition, evaluator=self.evaluator, X_init=self.X, Y_init=self.Y, cost=self.cost, normalize_Y=self.normalize_Y, model_update_interval=self.model_update_interval, de_duplication=self.de_duplication)
class CBO(object): """ Runner of the multi-attribute Bayesian optimization loop. This class wraps the optimization loop around the different handlers. :param model: GPyOpt model class. :param space: GPyOpt space class. :param objective: GPyOpt objective class. :param acquisition: GPyOpt acquisition class. :param evaluator: GPyOpt evaluator class. :param X_init: 2d numpy array containing the initial inputs (one per row) of the model. :param Y_init: 2d numpy array containing the initial outputs (one per row) of the model. :param cost: GPyOpt cost class (default, none). :param normalize_Y: whether to normalize the outputs before performing any optimization (default, True). :param model_update_interval: interval of collected observations after which the model is updated (default, 1). :param de_duplication: GPyOpt DuplicateManager class. Avoids re-evaluating the objective at previous, pending or infeasible locations (default, False). """ def __init__(self, model, space, objective, acquisition, evaluator, X_init, Y_init=None, cost=None, normalize_Y = False, model_update_interval = 1, expectation_utility=None): self.model = model self.space = space self.objective = objective self.acquisition = acquisition self.utility = acquisition.utility self.expectation_utility = expectation_utility self.evaluator = evaluator self.X = X_init self.Y = Y_init self.normalize_Y = normalize_Y self.cost = CostModel(cost) self.model_update_interval = model_update_interval self.historical_optimal_values = [] self.historical_time = [] self.n_attributes = self.model.output_dim self.n_hyps_samples = min(1, self.model.number_of_hyps_samples()) self.n_parameter_samples = 10 self.full_parameter_support = self.utility.parameter_dist.use_full_support self.evaluation_optimizer = GPyOpt.optimization.GeneralOptimizer(optimizer='lbfgs', space=space) # self.context = None self.current_argmax = np.atleast_2d(X_init[0,:]) def _current_max_value(self): """ Computes E_n[U(f(x_max))|f], where U is the utility function, f is the true underlying ojective function and x_max = argmax E_n[U(f(x))|U]. See function _marginal_max_value_so_far below. """ val = 0 if self.full_parameter_support: utility_param_support = self.utility.parameter_dist.support utility_param_dist = self.utility.parameter_dist.prob_dist for i in range(len(utility_param_support)): marginal_argmax = self._current_marginal_argmax(utility_param_support[i]) marginal_max_val = np.reshape(self.objective.evaluate(marginal_argmax)[0],(self.n_attributes,)) val += self.utility.eval_func(utility_param_support[i],marginal_max_val)*utility_param_dist[i] else: utility_param_samples = self.utility.parameter_dist.sample(self.n_parameter_samples) for i in range(len(utility_param_samples)): marginal_argmax = self._current_marginal_argmax(utility_param_samples[i]) marginal_max_val = np.reshape(self.objective.evaluate(marginal_argmax)[0],(self.n_attributes,)) val += self.utility.eval_func(utility_param_samples[i],marginal_max_val) val /= len(utility_param_samples) print('Current optimal value: {}'.format(val)) return val def _current_max_value_and_var(self): val = 0 var = 0 support = self.utility.parameter_dist.support utility_dist = self.utility.parameter_dist.prob_dist for i in range(len(support)): marginal_argmax = self._current_marginal_argmax(support[i]) marginal_max_val = np.reshape(self.objective.evaluate(marginal_argmax)[0],(self.n_attributes,)) var_marginal_argmax = np.reshape(self.model.posterior_variance_noiseless(marginal_argmax),(self.n_attributes,)) var += self.utility.eval_func(support[i],var_marginal_argmax)*utility_dist[i] val += self.utility.eval_func(support[i],marginal_max_val)*utility_dist[i] print('Current optimal value: {}'.format(val)) return val, var def _current_max_value_parallel(self): """ Computes E_n[U(f(x_max))|f], where U is the utility function, f is the true underlying ojective function and x_max = argmax E_n[U(f(x))|U]. See function _marginal_max_value_so_far below. """ pool = Pool(4) utility_param_samples = self.utility.parameter_dist.sample(self.n_parameter_samples) val = sum(pool.map(self._current_marginal_max_value, utility_param_samples))/self.n_parameter_samples print('Current optimal value') print(val) return val def _current_marginal_max_value(self, parameter): marginal_argmax = self._current_marginal_argmax(parameter) marginal_max_val = np.reshape(self.objective.evaluate(marginal_argmax)[0], (self.objective.output_dim,)) return self.utility.eval_func(parameter, marginal_max_val) def _current_marginal_argmax(self, parameter): """ Computes argmax E_n[U(f(x))|U] (The abuse of notation can be misleading; note that the expectation is with respect to the posterior distribution on f after n evaluations) """ if self.utility.linear: def val_func(X): X = np.atleast_2d(X) muX = self.model.posterior_mean(X) valX = np.reshape(np.matmul(parameter, muX), (X.shape[0], 1)) return -valX def val_func_with_gradient(X): X = np.atleast_2d(X) muX = self.model.posterior_mean(X) dmu_dX = self.model.posterior_mean_gradient(X) valX = np.reshape(np.matmul(parameter, muX), (X.shape[0], 1)) dval_dX = np.tensordot(parameter, dmu_dX, axes=1) return -valX, -dval_dX elif self.expectation_utility is not None: # Note: the value of these functions is not normalized, i.e. it is not divided by the number of Z samples and GP hyps. def val_func(X): X = np.atleast_2d(X) func_val = np.empty((X.shape[0], 1)) for h in range(self.n_hyps_samples): self.model.set_hyperparameters(h) mean, var = self.model.predict_noiseless(X) for i in range(X.shape[0]): func_val[i,0] = self.expectation_utility.func(parameter, mean[:,i], var[:,i]) return -func_val def val_func_with_gradient(X): X = np.atleast_2d(X) func_val = np.zeros((X.shape[0], 1)) func_gradient = np.zeros(X.shape) for h in range(self.n_hyps_samples): self.model.set_hyperparameters(h) mean, var = self.model.predict_noiseless(X) dmean_dX = self.model.posterior_mean_gradient(X) dvar_dX = self.model.posterior_variance_gradient(X) aux = np.concatenate((dmean_dX,dvar_dX)) for i in range(X.shape[0]): func_val[i,0] = self.expectation_utility.func(parameter, mean[:,i], var[:,i]) func_gradient[i,:] = np.matmul(self.expectation_utility.gradient(parameter,mean[:,i],var[:,i]),aux[:,i]) return -func_val, -func_gradient else: Z_samples = np.random.normal(size=(50, self.n_attributes)) def val_func(X): X = np.atleast_2d(X) func_val = np.zeros((X.shape[0], 1)) for h in range(self.n_hyps_samples): self.model.set_hyperparameters(h) mean, var = self.model.predict_noiseless(X) std = np.sqrt(var) for i in range(X.shape[0]): for Z in Z_samples: func_val[i,0] += self.utility.eval_func(parameter,mean[:,i] + np.multiply(std[:,i],Z)) return -func_val def val_func_with_gradient(X): X = np.atleast_2d(X) func_val = np.zeros((X.shape[0], 1)) func_gradient = np.zeros(X.shape) for h in range(self.n_hyps_samples): self.model.set_hyperparameters(h) mean, var = self.model.predict_noiseless(X) std = np.sqrt(var) dmean_dX = self.model.posterior_mean_gradient(X) dstd_dX = self.model.posterior_variance_gradient(X) for i in range(X.shape[0]): for j in range(self.n_attributes): dstd_dX[j,i,:] /= (2*std[j,i]) for Z in Z_samples: aux1 = mean[:,i] + np.multiply(Z, std[:,i]) func_val[i,0] += self.utility.eval_func(parameter, aux1) aux2 = dmean_dX[:,i,:] + np.multiply(dstd_dX[:,i,:].T, Z).T func_gradient[i,:] += np.matmul(self.utility.eval_gradient(parameter, aux1), aux2) return -func_val, -func_gradient argmax = self.evaluation_optimizer.optimize(f=val_func, f_df=val_func_with_gradient, parallel=False)[0] self.current_argmax = argmax return argmax def run_optimization(self, max_iter=1, parallel=False, plot=False, results_file=None, max_time=np.inf, eps=1e-8, context=None, verbosity=False): """ Runs Bayesian Optimization for a number 'max_iter' of iterations (after the initial exploration data) :param max_iter: exploration horizon, or number of acquisitions. If nothing is provided optimizes the current acquisition. :param max_time: maximum exploration horizon in seconds. :param eps: minimum distance between two consecutive x's to keep running the model. :param context: fixes specified variables to a particular context (values) for the optimization run (default, None). :param verbosity: flag to print the optimization results after each iteration (default, False). :param evaluations_file: filename of the file where the evaluated points and corresponding evaluations are saved (default, None). """ if self.objective is None: raise InvalidConfigError("Cannot run the optimization loop without the objective function") # --- Save the options to print and save the results self.verbosity = verbosity self.results_file = results_file self.context = context # --- Setting up stop conditions self.eps = eps if (max_iter is None) and (max_time is None): self.max_iter = 0 self.max_time = np.inf elif (max_iter is None) and (max_time is not None): self.max_iter = np.inf self.max_time = max_time elif (max_iter is not None) and (max_time is None): self.max_iter = max_iter self.max_time = np.inf else: self.max_iter = max_iter self.max_time = max_time # --- Initial function evaluation if self.X is not None and self.Y is None: self.Y, cost_values = self.objective.evaluate(self.X) if self.cost.cost_type == 'evaluation_time': self.cost.update_cost_model(self.X, cost_values) # --- Initialize model self.model.updateModel(self.X,self.Y) # --- Initialize iterations and running time self.time_zero = time.clock() self.cum_time = 0 self.num_acquisitions = 0 self.suggested_sample = self.X self.Y_new = self.Y # --- Initialize time cost of the evaluations while (self.max_time > self.cum_time) and (self.num_acquisitions < self.max_iter): #if not ((self.num_acquisitions < self.max_iter) and (self._distance_last_evaluations() > self.eps)): tmp = self.suggested_sample self.suggested_sample = self.compute_next_evaluations() if np.all(self.suggested_sample == tmp): self.suggested_sample = self._perturb(self.suggested_sample) try: self.acquisition.update_Z_samples() except: pass # --- Augment X self.X = np.vstack((self.X, self.suggested_sample)) # --- Evaluate *f* in X, augment Y and update cost function (if needed) print('Acquisition {}'.format(self.num_acquisitions+1)) self.evaluate_objective() # --- Update model if (self.num_acquisitions%self.model_update_interval)==0: self._update_model() self.model.get_model_parameters_names() self.model.get_model_parameters() if parallel and (not self.full_parameter_support): current_max_val = self._current_max_value_parallel() else: current_max_val = self._current_max_value() self.historical_optimal_values.append(current_max_val) # --- Update current evaluation time and function evaluations self.cum_time = time.clock() - self.time_zero self.historical_time.append(self.cum_time) self.num_acquisitions += 1 if verbosity: print("num acquisition: {}, time elapsed: {:.2f}s".format( self.num_acquisitions, self.cum_time)) if results_file is not None: self.save_results(results_file) if plot: self.plot_convergence(confidence_interval=True) #self._plot_pareto_front() # --- Print the desired result in files #if self.evaluations_file is not None: #self.save_evaluations(self.evaluations_file) #file = open('test_file.txt','w') #plt.plot(range(self.num_acquisitions),value_so_far) #plt.show() #np.savetxt('test_file.txt',value_so_far) def best_evaluated(self): if self.n_attributes > 1: raise InvalidConfigError("This option is not avialable with multiple objectives") else: scores = self.Y[0].flatten() x_best = self.X[np.argsort(-scores)[0],:] fx_best = -np.sort(-scores) return x_best, fx_best def evaluate_objective(self): """ Evaluates the objective """ print('Suggested point to evaluate: {}'.format(self.suggested_sample)) self.Y_new, cost_new = self.objective.evaluate_w_noise(self.suggested_sample) self.cost.update_cost_model(self.suggested_sample, cost_new) for j in range(self.n_attributes): #print(self.Y_new[j]) self.Y[j] = np.vstack((self.Y[j],self.Y_new[j])) def _distance_last_evaluations(self): """ Computes the distance between the last two evaluations. """ return np.sqrt(sum((self.X[self.X.shape[0]-1,:]-self.X[self.X.shape[0]-2,:])**2)) def _perturb(self, x): perturbed_x = np.copy(x) while np.all(perturbed_x == x): perturbed_x = x + np.random.normal(size=x.shape, scale=1e-2) perturbed_x = self.space.round_optimum(perturbed_x) return perturbed_x def compute_next_evaluations(self, pending_zipped_X=None, ignored_zipped_X=None): """ Computes the location of the new evaluation (optimizes the acquisition in the standard case). :param pending_zipped_X: matrix of input configurations that are in a pending state (i.e., do not have an evaluation yet). :param ignored_zipped_X: matrix of input configurations that the user black-lists, i.e., those configurations will not be suggested again. :return: """ # --- Initial function evaluation if self.X is not None and self.Y is None: self.Y, cost_values = self.objective.evaluate(self.X) if self.cost.cost_type == 'evaluation_time': self.cost.update_cost_model(self.X, cost_values) # --- Initialize model self.model.updateModel(self.X, self.Y) ## --- Update the context if any self.acquisition.optimizer.context_manager = ContextManager(self.space, self.context) ### We zip the value in case there are categorical variables x_baseline = self.current_argmax + np.random.normal(loc=0.0, scale=0.1, size=self.current_argmax.shape) return self.space.zip_inputs(self.evaluator.compute_batch(duplicate_manager=None, x_baseline=self.current_argmax)) #return self.space.zip_inputs(self.evaluator.compute_batch(duplicate_manager=None)) #return initial_design('random', self.space, 1) def _update_model(self): """ Updates the model (when more than one observation is available) and saves the parameters (if available). """ ### --- input that goes into the model (is unziped in case there are categorical variables) X_inmodel = self.space.unzip_inputs(self.X) Y_inmodel = list(self.Y) #print(X_inmodel) #print(Y_inmodel) self.model.updateModel(X_inmodel, Y_inmodel) ### --- Save parameters of the model #self._save_model_parameter_values() def convergence_assesment(self, n_iter=10, attribute=0, context=None): if self.objective is None: raise InvalidConfigError("Cannot run the optimization loop without the objective function") #self.model_parameters_iterations = None self.context = context # --- Initial function evaluation if self.X is not None and self.Y is None: self.Y, cost_values = self.objective.evaluate(self.X) if self.cost.cost_type == 'evaluation_time': self.cost.update_cost_model(self.X, cost_values) self._update_model() for i in range(n_iter): self.suggested_sample = self.compute_next_evaluations() filename = './experiments/1d' + str(i) + '.eps' model_to_plot = deepcopy(self.model) integrated_plot(self.acquisition.space.get_bounds(), self.X.shape[1], model_to_plot, self.X, self.Y, self.acquisition.acquisition_function, self.suggested_sample, attribute, filename) self.X = np.vstack((self.X,self.suggested_sample)) self.evaluate_objective() self._update_model() #self.model.get_model_parameters_names() #self.model.get_model_parameters() #print('Acquisition value at previously evaluated points:') #print(self.acquisition.acquisition_function(self.X)) #print('Posterior mean and variance') #print(self.model.predict(self.X)) #print(self.Y) self.historical_optimal_values.append(self._current_max_value()) def one_step_assesment(self, attribute=0, context=None): """ """ if self.objective is None: raise InvalidConfigError("Cannot run the optimization loop without the objective function") #self.model_parameters_iterations = None self.context = context # --- Initial function evaluation if self.X is not None and self.Y is None: self.Y, cost_values = self.objective.evaluate(self.X) if self.cost.cost_type == 'evaluation_time': self.cost.update_cost_model(self.X, cost_values) self._update_model() self.suggested_sample = self.compute_next_evaluations() model_to_plot = deepcopy(self.model) integrated_plot(self.acquisition.space.get_bounds(), self.X.shape[1], model_to_plot, self.X, self.Y, self.acquisition.acquisition_function, self.suggested_sample, attribute, None) self.X = np.vstack((self.X,self.suggested_sample)) self.evaluate_objective() self._update_model() self.historical_optimal_values.append(self._current_max_value()) def integrated_plot(self, attribute=0, filename=None): """ Plots the model and the acquisition function. if self.input_dim = 1: Plots data, mean and variance in one plot and the acquisition function in another plot if self.input_dim = 2: as before but it separates the mean and variance of the model in two different plots :param filename: name of the file where the plot is saved """ from copy import deepcopy model_to_plot = deepcopy(self.model) integrated_plot(self.acquisition.space.get_bounds(), self.X.shape[1], model_to_plot, self.X, self.Y, self.acquisition.acquisition_function, self.suggest_next_locations(), attribute, filename) def plot_acquisition(self,filename=None): """ Plots the acquisition function. """ return plot_acquisition(self.acquisition.space.get_bounds(), self.X.shape[1], self.acquisition.acquisition_function, filename) def plot_convergence(self, confidence_interval=False, filename=None): """ Makes twp plots to evaluate the convergence of the model: plot 1: Iterations vs. distance between consecutive selected x's plot 2: Iterations vs. the mean of the current model in the selected sample. :param filename: name of the file where the plot is saved """ return plot_convergence(self.historical_optimal_values, self.var_at_historical_optima, confidence_interval, filename) def get_evaluations(self): return self.X.copy(), self.Y.copy() def save_results(self, filename): results = np.zeros((len(self.historical_optimal_values),2)) results[:, 0] = np.atleast_1d(self.historical_optimal_values) results[:, 1] = np.atleast_1d(self.historical_time) np.savetxt(filename,results)
def __init__(self, fill_in_strategy, f, mix=0.5, domain=None, constraints=None, cost_withGradients=None, X=None, Y=None, subspace_dim_size=0, model_type='GP', initial_design_numdata=1, initial_design_type='random', acquisition_type='LCB', normalize_Y=True, exact_feval=False, acquisition_optimizer_type='lbfgs', model_update_interval=1, evaluator_type='sequential', batch_size=1, maximize=False, de_duplication=False): if model_type == 'input_warped_GP': raise NotImplementedError( 'input_warped_GP model is not implemented') if acquisition_type in ['EI_MCMC', 'MPI_MCMC', 'LCB_MCMC']: raise NotImplementedError('MCMC is not implemented') if batch_size is not 1 or evaluator_type is not 'sequential': raise NotImplementedError( 'only sequential evaluation is implemented') if fill_in_strategy not in ['random', 'copy', 'mix']: raise ValueError('fill_in_strategy has to be random, copy or mix') # private field self._arguments_mng = ArgumentsManager(kwargs=dict()) self.fill_in_strategy = fill_in_strategy self.mix = mix self.subspace_dim_size = subspace_dim_size self.cost_withGradients = cost_withGradients self.initial_design_numdata = initial_design_numdata self.initial_design_type = initial_design_type self.model_type = model_type self.acquisition_type = acquisition_type self.evaluator_type = evaluator_type self.model_update_interval = model_update_interval self.maximize = maximize self.normalize_Y = normalize_Y self.de_duplication = de_duplication self.subspace_dim_size = subspace_dim_size # --- property injected in other methods. self.verbosity = False self.subspace_idx = None self.subspace = None self.max_time = None self.max_iter = None self.cum_time = None self.report_file = None self.evaluations_file = None self.models_file = None self.eps = None self.save_models_parameters = None # --- unnecessary property self.suggested_sample = None self.Y_new = None # --- BO class property in uncertain use self.num_cores = 1 self.objective = SingleObjective(self._sign(f), batch_size, f.get_function_name()) self.cost = CostModel(cost_withGradients=cost_withGradients) self.space = initialize_space(domain=domain, constraints=constraints) self.model = self._arguments_mng.model_creator( model_type=self.model_type, exact_feval=exact_feval, space=self.space) self.acquisition = self._arguments_mng.acquisition_creator( acquisition_type=self.acquisition_type, model=self.model, space=self.space, acquisition_optimizer=AcquisitionOptimizer( space=self.space, optimizer=acquisition_optimizer_type), cost_withGradients=self.cost_withGradients) self._choose_evaluator() self.X = X self.Y = Y self._set_initial_values() super().__init__(model=self.model, space=self.space, objective=self.objective, acquisition=self.acquisition, evaluator=self.evaluator, X_init=self.X, Y_init=self.Y, cost=self.cost, normalize_Y=self.normalize_Y, model_update_interval=self.model_update_interval, de_duplication=self.de_duplication)
class REMBO(BO): """ Args: f (function): function to optimize. domain (list | None): the description of the inputs variables (See GpyOpt.core.space.Design_space class for details) constraints (list | None): the description of the problem constraints (See GpyOpt.core.space.Design_space class for details) Attributes: initial_design_numdata (int): initial_design_type (string): domain (dict | None): constraints (dict | None): space (Design_space): model (BOModel): acquisition (AcquisitionBase): cost (CostModel): """ def __init__(self, f, domain=None, constraints=None, cost_withGradients=None, X=None, Y=None, subspace_dim_size=0, model_type='GP', initial_design_numdata=1, initial_design_type='random', acquisition_type='LCB', normalize_Y=True, exact_feval=False, acquisition_optimizer_type='lbfgs', model_update_interval=1, evaluator_type='sequential', batch_size=1, maximize=False, de_duplication=False): if model_type == 'input_warped_GP': raise NotImplementedError('input_warped_GP model is not implemented') if acquisition_type in ['EI_MCMC', 'MPI_MCMC', 'LCB_MCMC']: raise NotImplementedError('MCMC is not implemented') if batch_size is not 1 or evaluator_type is not 'sequential': raise NotImplementedError('only sequential evaluation is implemented') if cost_withGradients is not None: raise NotImplementedError('param cost is not implemented') if constraints is not None: raise NotImplementedError('param constraints is not implemented') # private field self._arguments_mng = ArgumentsManager(kwargs=dict()) self.subspace_dim_size = subspace_dim_size self.cost_withGradients = cost_withGradients self.initial_design_numdata = initial_design_numdata self.initial_design_type = initial_design_type self.model_type = model_type self.acquisition_type = acquisition_type self.evaluator_type = evaluator_type self.model_update_interval = model_update_interval self.maximize = maximize self.normalize_Y = normalize_Y self.de_duplication = de_duplication self.subspace_dim_size = subspace_dim_size self.original_domain = domain # --- property injected in other methods. self.verbosity = False self.subspace = None self.max_time = None self.max_iter = None self.cum_time = None self.report_file = None self.evaluations_file = None self.models_file = None self.eps = None self.save_models_parameters = None # --- unnecessary property self.suggested_sample = None self.Y_new = None # --- BO class property in uncertain use self.num_cores = 1 self.objective = SingleObjective(self._sign(f), batch_size, f.get_function_name()) self.cost = CostModel(cost_withGradients=cost_withGradients) self.space = initialize_space(domain=domain, constraints=constraints) self.embedding_matrix = np.random.normal(size=(self.dimensionality, subspace_dim_size)) subspace_domain = self.choose_subspace_domain(subspace_dim_size=subspace_dim_size) self.subspace = initialize_space(domain=subspace_domain, constraints=constraints) self.model = self._arguments_mng.model_creator( model_type=self.model_type, exact_feval=exact_feval, space=self.subspace) self.acquisition = self._arguments_mng.acquisition_creator( acquisition_type=self.acquisition_type, model=self.model, space=self.subspace, acquisition_optimizer=AcquisitionOptimizer(space=self.space, optimizer=acquisition_optimizer_type), cost_withGradients=self.cost_withGradients ) self.evaluator = self._arguments_mng.evaluator_creator( evaluator_type=self.evaluator_type, acquisition=self.acquisition, batch_size=self.batch_size, model_type=self.model_type, model=self.model, space=self.space, acquisition_optimizer=self.acquisition_optimizer ) self.X = X self.Y = Y self._set_initial_values() super().__init__( model=self.model, space=self.subspace, objective=self.objective, acquisition=self.acquisition, evaluator=self.evaluator, X_init=self.X, Y_init=self.Y, cost=self.cost, normalize_Y=self.normalize_Y, model_update_interval=self.model_update_interval, de_duplication=self.de_duplication ) @property def dimensionality(self): return self.space.objective_dimensionality @property def subspace_domain(self): return self.subspace.config_space @property def domain(self): return self.space.config_space @property def constraints(self): return self.space.constraints @property def f(self): return self.objective.func @property def cost_type(self): return self.cost.cost_withGradients @property def exact_feval(self): return self.model.exact_feval @property def acquisition_optimizer_type(self): return self.acquisition_optimizer.optimizer_name @property def acquisition_optimizer(self): return self.acquisition.optimizer @property def batch_size(self): return self.objective.n_procs @property def objective_name(self): return self.objective.objective_name def choose_subspace_domain(self, subspace_dim_size): subspace_domain = list() for i in range(subspace_dim_size): subspace_domain.append({ 'name': 'x' + str(i), 'type': 'continuous', 'domain': (-np.sqrt(self.dimensionality), np.sqrt(self.dimensionality)), 'dimensionality': 1 }) return subspace_domain def map_to_original_space(self, x): if x.shape != (1, self.subspace_dim_size): raise ValueError('x.shape is not correct ' + str(x.shape)) x = np.array([np.dot(self.embedding_matrix, el) for el in x]) return x def run_optimization(self, max_iter=0, max_time=np.inf, eps=1e-8, context=None, verbosity=False, save_models_parameters=True, report_file=None, evaluations_file=None, models_file=None): if self.objective is None: raise ValueError("Cannot run the optimization loop without the objective function") # --- Save the options to print and save the results self.verbosity = verbosity self.save_models_parameters = save_models_parameters self.report_file = report_file self.evaluations_file = evaluations_file self.models_file = models_file self.model_parameters_iterations = None self.context = context # --- Check if we can save the model parameters in each iteration if self.save_models_parameters: if not (isinstance(self.model, GPModel)): print('Models printout after each iteration is only available for GP and GP_MCMC models') self.save_models_parameters = False # --- Setting up stop conditions self.eps = eps if (max_iter is None) and (max_time is None): self.max_iter = 0 self.max_time = np.inf elif (max_iter is None) and (max_time is not None): self.max_iter = np.inf self.max_time = max_time elif (max_iter is not None) and (max_time is None): self.max_iter = max_iter self.max_time = np.inf else: self.max_iter = max_iter self.max_time = max_time # --- Initialize iterations and running time stopwatch = StopWatch() self.num_acquisitions = self.initial_design_numdata self.suggested_sample = self.X self.Y_new = self.Y self._compute_results() self._run_optimization() self.cum_time = stopwatch.passed_time() # --- Stop messages and execution time self._compute_results() # --- Print the desired result in files if self.report_file is not None: self.save_report(self.report_file) if self.evaluations_file is not None: self.save_evaluations(self.evaluations_file) if self.models_file is not None: self.save_models(self.models_file) self._save() def _run_optimization(self): while True: print('.') # --- update model try: self.update() except np.linalg.LinAlgError: print('np.linalg.LinAlgError') break if self.num_acquisitions >= self.max_iter: break self.next_point() # --- Update current evaluation time and function evaluations self.num_acquisitions += 1 def next_point(self): self.suggested_sample = self._compute_next_evaluations() # --- Augment X self.X = np.vstack((self.X, self.suggested_sample)) # --- Evaluate *f* in X, augment Y and update cost function (if needed) self.evaluate_objective() def evaluate_objective(self): """ Evaluates the objective """ original_suggested_sample = self.map_to_original_space(x=self.suggested_sample) self.Y_new, cost_new = self.objective.evaluate(original_suggested_sample) self.cost.update_cost_model(self.suggested_sample, cost_new) self.Y = np.vstack((self.Y, self.Y_new)) def get_best_point(self): self._compute_results() return self.x_opt, self.fx_opt def update(self): self._update_model(self.normalization_type) self._update_acquisition() self._update_evaluator() def _sign(self, f): if self.maximize: f_copy = f def f(x): return -f_copy(x) return f def _set_initial_values(self): if self.X is None: self.X = initial_design(self.initial_design_type, self.subspace, self.initial_design_numdata) self.Y, _ = self.objective.evaluate(self.map_to_original_space(x=self.X)) elif self.X is not None and self.Y is None: self.Y, _ = self.objective.evaluate(self.map_to_original_space(x=self.X)) # save initial values self.initial_X = deepcopy(self.X) if self.maximize: self.initial_Y = -deepcopy(self.Y) else: self.initial_Y = deepcopy(self.Y) def _update_acquisition(self): self.acquisition = self._arguments_mng.acquisition_creator( acquisition_type=self.acquisition_type, model=self.model, space=self.subspace, acquisition_optimizer=AcquisitionOptimizer(space=self.subspace, optimizer=self.acquisition_optimizer_type), cost_withGradients=self.cost_withGradients ) def _update_evaluator(self): self.evaluator.acquisition = self.acquisition def _update_model(self, normalization_type='stats'): if self.num_acquisitions % self.model_update_interval == 0: self.model = self._arguments_mng.model_creator( model_type=self.model_type, exact_feval=self.exact_feval, space=self.subspace) X_inmodel, Y_inmodel = self._input_data(normalization_type=normalization_type) self.model.updateModel(X_inmodel, Y_inmodel, None, None) self.X_inmodel = X_inmodel self.Y_inmodel = Y_inmodel # Save parameters of the model self._save_model_parameter_values() def _input_data(self, normalization_type): # input that goes into the model (is unziped in case there are categorical variables) X_inmodel = self.subspace.unzip_inputs(self.X) # Y_inmodel is the output that goes into the model if self.normalize_Y: Y_inmodel = normalize(self.Y, normalization_type) else: Y_inmodel = self.Y return X_inmodel, Y_inmodel def _compute_next_evaluations(self, pending_zipped_X=None, ignored_zipped_X=None): # --- Update the context if any self.acquisition.optimizer.context_manager = ContextManager(self.subspace, self.context) # --- Activate de_duplication if self.de_duplication: duplicate_manager = DuplicateManager( space=self.subspace, zipped_X=self.X, pending_zipped_X=pending_zipped_X, ignored_zipped_X=ignored_zipped_X) else: duplicate_manager = None # We zip the value in case there are categorical variables suggested_ = self.subspace.zip_inputs(self.evaluator.compute_batch( duplicate_manager=duplicate_manager, context_manager=self.acquisition.optimizer.context_manager)) return suggested_ def _save(self): mkdir_when_not_exist(abs_path=definitions.ROOT_DIR + '/storage/' + self.objective_name) dir_name = definitions.ROOT_DIR + '/storage/' + self.objective_name + '/' + now_str() + ' ' + str( len(self.original_domain)) + 'D REMBO_' + str(self.subspace_dim_size) mkdir_when_not_exist(abs_path=dir_name) self.save_report(report_file=dir_name + '/report.txt') self.save_evaluations(evaluations_file=dir_name + '/evaluation.csv') self.save_models(models_file=dir_name + '/model.csv') def save_report(self, report_file=None): with open(report_file, 'w') as file: import GPyOpt import time file.write( '-----------------------------' + ' GPyOpt Report file ' + '-----------------------------------\n') file.write('GPyOpt Version ' + str(GPyOpt.__version__) + '\n') file.write('Date and time: ' + time.strftime("%c") + '\n') if self.num_acquisitions == self.max_iter: file.write('Optimization completed: ' + 'YES, ' + str(self.X.shape[0]).strip( '[]') + ' samples collected.\n') file.write('Number initial samples: ' + str(self.initial_design_numdata) + ' \n') else: file.write('Optimization completed: ' + 'NO,' + str(self.X.shape[0]).strip( '[]') + ' samples collected.\n') file.write('Number initial samples: ' + str(self.initial_design_numdata) + ' \n') file.write('Tolerance: ' + str(self.eps) + '.\n') file.write('Optimization time: ' + str(self.cum_time).strip('[]') + ' seconds.\n') file.write('\n') file.write( '--------------------------------' + ' Problem set up ' + '------------------------------------\n') file.write('Problem name: ' + self.objective_name + '\n') file.write('Problem dimension: ' + str(self.dimensionality) + '\n') file.write('Number continuous variables ' + str(len(self.space.get_continuous_dims())) + '\n') file.write('Number discrete variables ' + str(len(self.space.get_discrete_dims())) + '\n') file.write('Number bandits ' + str(self.space.get_bandit().shape[0]) + '\n') file.write('Noiseless evaluations: ' + str(self.exact_feval) + '\n') file.write('Cost used: ' + self.cost.cost_type + '\n') file.write('Constraints: ' + str(self.constraints == True) + '\n') file.write('Subspace Dimension: ' + str(self.subspace_dim_size) + '\n') file.write('\n') file.write( '------------------------------' + ' Optimization set up ' + '---------------------------------\n') file.write('Normalized outputs: ' + str(self.normalize_Y) + '\n') file.write('Model type: ' + str(self.model_type).strip('[]') + '\n') file.write('Model update interval: ' + str(self.model_update_interval) + '\n') file.write('Acquisition type: ' + str(self.acquisition_type).strip('[]') + '\n') file.write( 'Acquisition optimizer: ' + str(self.acquisition_optimizer.optimizer_name).strip('[]') + '\n') file.write('Acquisition type: ' + str(self.acquisition_type).strip('[]') + '\n') if hasattr(self, 'acquisition_optimizer') and hasattr(self.acquisition_optimizer, 'optimizer_name'): file.write( 'Acquisition optimizer: ' + str(self.acquisition_optimizer.optimizer_name).strip('[]') + '\n') else: file.write('Acquisition optimizer: None\n') file.write('Evaluator type (batch size): ' + str(self.evaluator_type).strip('[]') + ' (' + str( self.batch_size) + ')' + '\n') file.write('Cores used: ' + str(self.num_cores) + '\n') file.write('\n') file.write( '---------------------------------' + ' Summary ' + '------------------------------------------\n') file.write('Initial X: ' + str(self.initial_X) + '\n') file.write('Initial Y: ' + str(self.initial_Y) + '\n') if self.maximize: file.write('Value at maximum: ' + str(format(-min(self.Y)[0], '.20f')).strip('[]') + '\n') file.write('Best found maximum location: ' + str(self.X[np.argmin(self.Y), :]).strip('[]') + '\n') else: file.write('Value at minimum: ' + str(format(min(self.Y)[0], '.20f')).strip('[]') + '\n') file.write('Best found minimum location: ' + str(self.X[np.argmin(self.Y), :]).strip('[]') + '\n') file.write( '----------------------------------------------------------------------------------------------\n') file.close()
def __init__(self, domain=None, constraints=None, cost_withGradients=None, model_type='GP', X=None, Y=None, initial_design_numdata=7, initial_design_type='random', acquisition_type='EI', normalize_Y=True, exact_feval=False, acquisition_optimizer_type='lbfgs', model_update_interval=1, evaluator_type='sequential', batch_size=1, num_cores=1, verbosity=False, verbosity_model=False, maximize=False, de_duplication=False, **kwargs): self.modular_optimization = False self.initial_iter = True self.verbosity = verbosity self.verbosity_model = verbosity_model self.model_update_interval = model_update_interval self.de_duplication = de_duplication self.kwargs = kwargs # --- Handle the arguments passed via kargs self.problem_config = ArgumentsManager(kwargs) # --- CHOOSE design space self.constraints = constraints self.domain = domain self.space = Design_space(self.domain, self.constraints) # --- CHOOSE objective function self.maximize = maximize self.batch_size = batch_size self.num_cores = num_cores # --- CHOOSE the cost model self.cost = CostModel(cost_withGradients) # --- CHOOSE initial design self.X = X self.Y = Y self.initial_design_type = initial_design_type self.initial_design_numdata = initial_design_numdata # --- CHOOSE the model type. If an instance of a GPyOpt model is passed (possibly user defined), it is used. self.model_type = model_type self.exact_feval = exact_feval # note that this 2 options are not used with the predefined model self.normalize_Y = normalize_Y self.model = self._model_chooser() # --- CHOOSE the acquisition optimizer_type # This states how the discrete variables are handled (exact search or rounding) self.acquisition_optimizer_type = acquisition_optimizer_type self.acquisition_optimizer = AcquisitionOptimizer( self.space, self.acquisition_optimizer_type, model=self.model) ## more arguments may come here # --- CHOOSE acquisition function. If an instance of an acquisition is passed (possibly user defined), it is used. self.acquisition_type = acquisition_type self.acquisition = self.acquisition = self._acquisition_chooser() # --- CHOOSE evaluator method self.evaluator_type = evaluator_type self.evaluator = self._evaluator_chooser() # --- Create optimization space super(RosBayesianOptimization, self).__init__( model=self.model, space=self.space, acquisition=self.acquisition, evaluator=self.evaluator, X_init=self.X, Y_init=self.Y, cost=self.cost, normalize_Y=self.normalize_Y, model_update_interval=self.model_update_interval, de_duplication=self.de_duplication, initial_design_numdata=self.initial_design_numdata)
class BO(object): """ Runner of the multi-attribute Bayesian optimization loop. This class wraps the optimization loop around the different handlers. :param model: GPyOpt model class. :param space: GPyOpt space class. :param objective: GPyOpt objective class. :param acquisition: GPyOpt acquisition class. :param evaluator: GPyOpt evaluator class. :param X_init: 2d numpy array containing the initial inputs (one per row) of the model. :param Y_init: 2d numpy array containing the initial outputs (one per row) of the model. :param cost: GPyOpt cost class (default, none). :param normalize_Y: whether to normalize the outputs before performing any optimization (default, True). :param model_update_interval: interval of collected observations after which the model is updated (default, 1). :param de_duplication: GPyOpt DuplicateManager class. Avoids re-evaluating the objective at previous, pending or infeasible locations (default, False). """ def __init__(self, model, model_c, space, objective, constraint, acquisition, evaluator, X_init, penalty_tag="fixed", penalty_value=0.0, expensive=False, Y_init=None, C_init=None, cost=None, normalize_Y=False, model_update_interval=1, true_preference=0.5): self.true_preference = true_preference self.model_c = model_c self.model = model self.space = space self.objective = objective self.constraint = constraint self.acquisition = acquisition self.utility = acquisition.utility self.evaluator = evaluator self.normalize_Y = normalize_Y self.model_update_interval = model_update_interval self.X = X_init self.Y = Y_init self.C = C_init self.cost = CostModel(cost) self.model_parameters_iterations = None self.expensive = expensive self.penalty_tag = penalty_tag self.penalty_value = penalty_value def suggest_next_locations(self, context=None, pending_X=None, ignored_X=None): """ Run a single optimization step and return the next locations to evaluate the objective. Number of suggested locations equals to batch_size. :param context: fixes specified variables to a particular context (values) for the optimization run (default, None). :param pending_X: matrix of input configurations that are in a pending state (i.e., do not have an evaluation yet) (default, None). :param ignored_X: matrix of input configurations that the user black-lists, i.e., those configurations will not be suggested again (default, None). """ self.model_parameters_iterations = None self.num_acquisitions = 0 self.context = context self._update_model(self.normalization_type) suggested_locations = self._compute_next_evaluations( pending_zipped_X=pending_X, ignored_zipped_X=ignored_X) return suggested_locations # def _value_so_far(self): # """ # Computes E_n[U(f(x_max))|f], where U is the utility function, f is the true underlying ojective function and x_max = argmax E_n[U(f(x))|U]. See # function _marginal_max_value_so_far below. # """ # # output = 0 # support = self.utility.parameter_dist.support # utility_dist = self.utility.parameter_dist.prob_dist # # a = np.reshape(self.objective.evaluate(self._marginal_max_value_so_far())[0],(self.objective.output_dim,)) # # output += self.utility.eval_func(support[i],a)*utility_dist[i] # #print(output) # return output # # # def _marginal_max_value_so_far(self): # """ # Computes argmax E_n[U(f(x))|U] (The abuse of notation can be misleading; note that the expectation is with # respect to the posterior distribution on f after n evaluations) # """ # # def val_func(X): # X = np.atleast_2d(X) # muX = self.model.posterior_mean(X) # return muX # # def val_func_with_gradient(X): # X = np.atleast_2d(X) # muX = self.model.posterior_mean(X) # dmu_dX = self.model.posterior_mean_gradient(X) # valX = np.reshape( muX, (X.shape[0],1)) # dval_dX = dmu_dX # return -valX, -dval_dX # # # argmax = self.acquisition.optimizer.optimize_inner_func(f=val_func, f_df=val_func_with_gradient)[0] # return argmax # # def run_optimization(self, max_iter=1, max_time=np.inf, eps=1e-8, context=None, verbosity=False, evaluations_file=None): """ Runs Bayesian Optimization for a number 'max_iter' of iterations (after the initial exploration data) :param max_iter: exploration horizon, or number of acquisitions. If nothing is provided optimizes the current acquisition. :param max_time: maximum exploration horizon in seconds. :param eps: minimum distance between two consecutive x's to keep running the model. :param context: fixes specified variables to a particular context (values) for the optimization run (default, None). :param verbosity: flag to print the optimization results after each iteration (default, False). :param evaluations_file: filename of the file where the evaluated points and corresponding evaluations are saved (default, None). """ self.verbosity = verbosity if self.objective is None: raise InvalidConfigError( "Cannot run the optimization loop without the objective function" ) # --- Save the options to print and save the results self.verbosity = verbosity self.evaluations_file = evaluations_file self.context = context # --- Setting up stop conditions self.eps = eps if (max_iter is None) and (max_time is None): self.max_iter = 0 self.max_time = np.inf elif (max_iter is None) and (max_time is not None): self.max_iter = np.inf self.max_time = max_time elif (max_iter is not None) and (max_time is None): self.max_iter = max_iter self.max_time = np.inf else: self.max_iter = max_iter self.max_time = max_time # print("------------------------TRAINING HYPERS----------------------") # self._get_hyperparameters() # --- Initial function evaluation and model fitting if self.X is not None and self.Y is None: self.Y, cost_values = self.objective.evaluate(self.X) if self.constraint is not None: self.C, cost_values = self.constraint.evaluate(self.X) if self.cost.cost_type == 'evaluation_time': self.cost.update_cost_model(self.X, cost_values) #self.model.updateModel(self.X,self.Y) # --- Initialize iterations and running time self.time_zero = time.time() self.cum_time = 0 self.num_acquisitions = 0 self.suggested_sample = self.X self.Y_new = self.Y self.Opportunity_Cost = [] value_so_far = [] # --- Initialize time cost of the evaluations print("-----------------------MAIN LOOP STARTS----------------------") Opportunity_Cost = [] self.true_best_stats = { "true_best": [], "mean_gp": [], "std gp": [], "pf": [], "mu_pf": [], "var_pf": [], "residual_noise": [] } while (self.max_iter > self.num_acquisitions): self._update_model() if self.constraint is None: self.Opportunity_Cost_caller_unconstrained() else: self.Opportunity_Cost_caller_constrained() print("maKG optimizer") start = time.time() self.suggested_sample = self._compute_next_evaluations() finish = time.time() print("time optimisation point X", finish - start) if verbosity: ####plots design_plot = initial_design('random', self.space, 1000) ac_f = self.expected_improvement(design_plot) Y, _ = self.objective.evaluate(design_plot) C, _ = self.constraint.evaluate(design_plot) pf = self.probability_feasibility_multi_gp( design_plot, self.model_c).reshape(-1, 1) mu_f = self.model.predict(design_plot)[0] bool_C = np.product(np.concatenate(C, axis=1) < 0, axis=1) func_val = Y * bool_C.reshape(-1, 1) print("self.suggested_sample", self.suggested_sample) fig, axs = plt.subplots(2, 2) axs[0, 0].set_title('True Function') axs[0, 0].scatter(design_plot[:, 0], design_plot[:, 1], c=np.array(func_val).reshape(-1)) axs[0, 0].scatter(self.X[:, 0], self.X[:, 1], color="red", label="sampled") axs[0, 0].scatter(self.suggested_sample[:, 0], self.suggested_sample[:, 1], marker="x", color="red", label="suggested") axs[0, 1].set_title('approximation Acqu Function') axs[0, 1].scatter(design_plot[:, 0], design_plot[:, 1], c=np.array(ac_f).reshape(-1)) axs[1, 0].set_title("convergence") axs[1, 0].plot(range(len(self.Opportunity_Cost)), np.array(self.Opportunity_Cost).reshape(-1)) axs[1, 0].set_yscale("log") axs[1, 1].set_title("mu") axs[1, 1].scatter(design_plot[:, 0], design_plot[:, 1], c=np.array(mu_f).reshape(-1) * np.array(pf).reshape(-1)) plt.show() self.X = np.vstack((self.X, self.suggested_sample)) # --- Evaluate *f* in X, augment Y and update cost function (if needed) self.evaluate_objective() print("X", self.X, "Y", self.Y, "C", self.C, "OC", self.Opportunity_Cost) # --- Update current evaluation time and function evaluations self.cum_time = time.time() - self.time_zero self.num_acquisitions += 1 return self.X, self.Y, self.C, self.Opportunity_Cost # --- Print the desired result in files #if self.evaluations_file is not None: #self.save_evaluations(self.evaluations_file) #file = open('test_file.txt','w') #np.savetxt('test_file.txt',value_so_far) def Opportunity_Cost_caller_constrained(self): # design_plot = initial_design('random', self.space, 1000) # ac_f = self.expected_improvement(design_plot) # fig, axs = plt.subplots(2, 2) # axs[0, 0].set_title('True Function') # axs[0, 0].scatter(design_plot[:, 0], design_plot[:, 1], c=np.array(ac_f).reshape(-1)) # axs[0, 0].scatter(suggested_sample[:, 0], suggested_sample[:, 1], color="red") # plt.show() # print("self.suggested_sample",suggested_sample) # --- Evaluate *f* in X, augment Y and update cost function (if needed) samples = self.X print("samples", samples) Y = self.model.posterior_mean(samples) # print("Y",Y) pf = self.probability_feasibility_multi_gp(samples, model=self.model_c) func_val = np.array(Y).reshape(-1) * np.array(pf).reshape(-1) print("Y", Y, "pf", pf) suggested_final_sample = samples[np.argmax(func_val)] suggested_final_sample = np.array(suggested_final_sample).reshape(-1) suggested_final_sample = np.array(suggested_final_sample).reshape( 1, -1) print("suggested_final_sample", suggested_final_sample, "max", np.max(func_val)) Y_true, _ = self.objective.evaluate(suggested_final_sample, true_val=True) C_true, _ = self.constraint.evaluate(suggested_final_sample, true_val=True) bool_C_true = np.product(np.concatenate(C_true, axis=1) < 0, axis=1) func_val_true = Y_true * bool_C_true.reshape(-1, 1) print("suggested_final_sample", suggested_final_sample, "func_val_true", func_val_true) if self.expensive: self.Opportunity_Cost.append( np.array(np.abs(np.max(func_val_true))).reshape(-1)) else: self.true_best_value() optimum = np.max(np.abs(self.true_best_stats["true_best"])) # print("optimum", optimum) self.Opportunity_Cost.append( optimum - np.array(np.abs(np.max(func_val_true))).reshape(-1)) # print("OC_i", optimum - np.array(np.abs(np.max(func_val_true))).reshape(-1)) def Opportunity_Cost_caller_unconstrained(self): # design_plot = initial_design('random', self.space, 1000) # ac_f = self.expected_improvement(design_plot) # fig, axs = plt.subplots(2, 2) # axs[0, 0].set_title('True Function') # axs[0, 0].scatter(design_plot[:, 0], design_plot[:, 1], c=np.array(ac_f).reshape(-1)) # axs[0, 0].scatter(suggested_sample[:, 0], suggested_sample[:, 1], color="red") # plt.show() # print("self.suggested_sample",suggested_sample) # --- Evaluate *f* in X, augment Y and update cost function (if needed) samples = self.X print("samples", samples) Y = self.model.posterior_mean(samples) # print("Y",Y) #pf = self.probability_feasibility_multi_gp(samples, model=self.model_c) func_val = np.array(Y).reshape(-1) #* np.array(pf).reshape(-1) #print("Y", Y, "pf", pf) suggested_final_sample = samples[np.argmax(func_val)] suggested_final_sample = np.array(suggested_final_sample).reshape(-1) suggested_final_sample = np.array(suggested_final_sample).reshape( 1, -1) print("suggested_final_sample", suggested_final_sample, "max", np.max(func_val)) Y_true, _ = self.objective.evaluate(suggested_final_sample, true_val=True) #C_true, _ = self.constraint.evaluate(suggested_final_sample, true_val=True) #bool_C_true = np.product(np.concatenate(C_true, axis=1) < 0, axis=1) func_val_true = Y_true #* bool_C_true.reshape(-1, 1) print("suggested_final_sample", suggested_final_sample, "func_val_true", func_val_true) if self.expensive: self.Opportunity_Cost.append( np.array(np.abs(np.max(func_val_true))).reshape(-1)) else: self.true_best_value() optimum = np.max(np.abs(self.true_best_stats["true_best"])) # print("optimum", optimum) self.Opportunity_Cost.append( optimum - np.array(np.abs(np.max(func_val_true))).reshape(-1)) # print("OC_i", optimum - np.array(np.abs(np.max(func_val_true))).reshape(-1)) def optimize_final_evaluation(self): out = self.acquisition.optimizer.optimize(f=self.current_best, duplicate_manager=None) print("out", out) print("out", out[1]) self.best_mu_all = -1 * np.array(out[1]).reshape(-1) self.acquisition.optimizer.context_manager = ContextManager( self.space, self.context) out = self.acquisition.optimizer.optimize(f=self.expected_improvement, duplicate_manager=None) suggested_sample = self.space.zip_inputs(out[0]) # suggested_sample = suggested_sample.astype("int") print("suggested_sample", suggested_sample) return suggested_sample def current_best(self, X): # X = X.astype("int") mu = self.model.posterior_mean(X) mu = mu.reshape(-1, 1) return -mu.reshape(-1) def expected_improvement(self, X, offset=0.0): ''' Computes the EI at points X based on existing samples X_sample and Y_sample using a Gaussian process surrogate model. Args: X: Points at which EI shall be computed (m x d). X_sample: Sample locations (n x d). Y_sample: Sample values (n x 1). gpr: A GaussianProcessRegressor fitted to samples. xi: Exploitation-exploration trade-off parameter. Returns: Expected improvements at points X. ''' # X = X.astype("int") mu = self.model.posterior_mean(X) sigma = self.model.posterior_variance(X, noise=False) sigma = np.sqrt(sigma).reshape(-1, 1) mu = mu.reshape(-1, 1) # Needed for noise-based model, # otherwise use np.max(Y_sample). # See also section 2.4 in [...] bool_C = np.product(np.concatenate(self.C, axis=1) < 0, axis=1) func_val = self.Y * bool_C.reshape(-1, 1) mu_sample_opt = np.max(func_val) - offset with np.errstate(divide='warn'): imp = mu - mu_sample_opt Z = imp / sigma ei = imp * norm.cdf(Z) + sigma * norm.pdf(Z) ei[sigma == 0.0] = 0.0 pf = self.probability_feasibility_multi_gp(X, self.model_c).reshape( -1, 1) if self.penalty_tag == "fixed": self.penalty = self.penalty_value else: dif = np.array(mu).reshape(-1) - np.array( self.best_mu_all).reshape(-1) constraints = self.model_c.posterior_mean( X) # MAYBE CHECK FOR SEVERAL CONSTRAINTS sum_constraints = np.sum(constraints, axis=0).reshape(-1) self.penalty = dif * sum_constraints return -(ei.reshape(-1) * pf.reshape(-1) + (1 - pf.reshape(-1)) * self.penalty) def probability_feasibility_multi_gp(self, x, model, mean=None, cov=None, grad=False, l=0): # print("model",model.output) x = np.atleast_2d(x) if grad == False: Fz = [] for m in range(model.output_dim): Fz.append( self.probability_feasibility(x, model.output[m], grad, l)) Fz = np.product(Fz, axis=0) return Fz else: Fz = [] grad_Fz = [] for m in range(model.output_dim): # print("model.output[m]",model.output[m]) # print("mean[m]",mean[m]) # print("cov[m]",cov[m]) Fz_aux, grad_Fz_aux = self.probability_feasibility( x, model.output[m]) Fz.append(Fz_aux) grad_Fz.append(grad_Fz_aux) # print("np.array(Fz)", np.array(Fz), "grad_Fz", np.array(grad_Fz)) grad_Fz = self.product_gradient_rule(func=np.array(Fz), grad=np.array(grad_Fz)) # print("output grad_Fz", grad_Fz) Fz = np.product(Fz, axis=0) return Fz, grad_Fz def probability_feasibility(self, x, model, mean=None, cov=None, grad=False, l=0): model = model.model # kern = model.kern # X = model.X mean = model.posterior_mean(x) var = model.posterior_variance(x, noise=False) std = np.sqrt(var).reshape(-1, 1) aux_var = np.reciprocal(var) mean = mean.reshape(-1, 1) norm_dist = norm(mean, std) fz = norm_dist.pdf(l) Fz = norm_dist.cdf(l) if grad == True: grad_mean, grad_var = model.predictive_gradients(x) grad_std = (1 / 2.0) * grad_var # cov = kern.K(X, X) + np.eye(X.shape[0]) * 1e-3 # L = scipy.linalg.cholesky(cov, lower=True) # u = scipy.linalg.solve(L, np.eye(X.shape[0])) # Ainv = scipy.linalg.solve(L.T, u) dims = range(x.shape[1]) grad_Fz = [] for d in dims: # K1 = np.diag(np.dot(np.dot(kern.dK_dX(x, X, d), Ainv), kern.dK_dX2(X, x, d))) # K2 = np.diag(kern.dK2_dXdX2(x, x, d, d)) # var_grad = K2 - K1 # var_grad = var_grad.reshape(-1, 1) grd_mean_d = grad_mean[:, d].reshape(-1, 1) grd_std_d = grad_std[:, d].reshape(-1, 1) # print("grd_mean ",grd_mean_d, "var_grad ",grd_std_d ) # print("std",std) # print("aux_var", aux_var) # print("fz",fz) grad_Fz.append(fz * aux_var * (mean * grd_std_d - grd_mean_d * std)) grad_Fz = np.stack(grad_Fz, axis=1) return Fz.reshape(-1, 1), grad_Fz[:, :, 0] else: return Fz.reshape(-1, 1) def evaluate_objective(self): """ Evaluates the objective """ print(1) print(self.suggested_sample) self.Y_new, cost_new = self.objective.evaluate(self.suggested_sample) self.C_new, C_cost_new = self.constraint.evaluate( self.suggested_sample) self.cost.update_cost_model(self.suggested_sample, cost_new) for j in range(self.objective.output_dim): print(self.Y_new[j]) self.Y[j] = np.vstack((self.Y[j], self.Y_new[j])) for k in range(self.constraint.output_dim): print(self.C_new[k]) self.C[k] = np.vstack((self.C[k], self.C_new[k])) def compute_current_best(self): current_acqX = self.acquisition.current_compute_acq() return current_acqX def _distance_last_evaluations(self): """ Computes the distance between the last two evaluations. """ return np.sqrt( sum((self.X[self.X.shape[0] - 1, :] - self.X[self.X.shape[0] - 2, :])**2)) def _compute_next_evaluations(self, pending_zipped_X=None, ignored_zipped_X=None, re_use=False): """ Computes the location of the new evaluation (optimizes the acquisition in the standard case). :param pending_zipped_X: matrix of input configurations that are in a pending state (i.e., do not have an evaluation yet). :param ignored_zipped_X: matrix of input configurations that the user black-lists, i.e., those configurations will not be suggested again. :return: """ ## --- Update the context if any self.acquisition.optimizer.context_manager = ContextManager( self.space, self.context) aux_var = self.evaluator.compute_batch(duplicate_manager=None, re_use=re_use) ### We zip the value in case there are categorical variables return self.space.zip_inputs(aux_var[0]) #return initial_design('random', self.space, 1) def _get_hyperparameters(self): """ Updates the model (when more than one observation is available) and saves the parameters (if available). """ X_basis = GPyOpt.experiment_design.initial_design( 'latin', self.space, 2000) X_train = X_basis # N=4 # for i in range(N - 1): # X_train = np.concatenate([X_train, X_basis]) Y_train, cost_train = self.objective.evaluate(X_train) C_train, C_cost_train = self.constraint.evaluate(X_train) ### --- input that goes into the model (is unziped in case there are categorical variables) X_inmodel = self.space.unzip_inputs(X_train) Y_inmodel = list(Y_train) C_inmodel = list(C_train) print("X", X_inmodel, len(X_inmodel), "Y", Y_inmodel, len(Y_inmodel)) self.model.trainModel(X_inmodel, Y_inmodel) self.model_c.trainModel(X_inmodel, C_inmodel) def _update_model(self): """ Updates the model (when more than one observation is available) and saves the parameters (if available). """ if (self.num_acquisitions % self.model_update_interval) == 0: ### --- input that goes into the model (is unziped in case there are categorical variables) X_inmodel = self.space.unzip_inputs(self.X) Y_inmodel = list(self.Y) if self.constraint is not None: C_inmodel = list(self.C) self.model_c.updateModel(X_inmodel, C_inmodel) print("X", X_inmodel, len(X_inmodel), "Y", Y_inmodel, len(Y_inmodel)) self.model.updateModel(X_inmodel, Y_inmodel) ### --- Save parameters of the model #self._save_model_parameter_values() def get_evaluations(self): return self.X.copy(), self.Y.copy() def true_best_value(self): from scipy.optimize import minimize X = initial_design('random', self.space, 1000) fval = self.func_val(X) anchor_point = np.array(X[np.argmin(fval)]).reshape(-1) anchor_point = anchor_point.reshape(1, -1) print("anchor_point", anchor_point) best_design = minimize(self.func_val, anchor_point, method='Nelder-Mead', tol=1e-8).x self.true_best_stats["true_best"].append(self.func_val(best_design)) self.true_best_stats["mean_gp"].append( self.model.posterior_mean(best_design)) self.true_best_stats["std gp"].append( self.model.posterior_variance(best_design, noise=False)) self.true_best_stats["pf"].append( self.probability_feasibility_multi_gp(best_design, self.model_c).reshape(-1, 1)) mean = self.model_c.posterior_mean(best_design) var = self.model_c.posterior_variance(best_design, noise=False) residual_noise = self.model_c.posterior_variance(self.X[1], noise=False) self.true_best_stats["mu_pf"].append(mean) self.true_best_stats["var_pf"].append(var) self.true_best_stats["residual_noise"].append(residual_noise) if False: fig, axs = plt.subplots(3, 2) N = len(np.array(self.true_best_stats["std gp"]).reshape(-1)) GAP = np.array( np.abs( np.abs(self.true_best_stats["true_best"]).reshape(-1) - np.abs(self.true_best_stats["mean_gp"]).reshape(-1)) ).reshape(-1) print("GAP len", len(GAP)) print("N", N) axs[0, 0].set_title('GAP') axs[0, 0].plot(range(N), GAP) axs[0, 0].set_yscale("log") axs[0, 1].set_title('VAR') axs[0, 1].plot(range(N), np.array(self.true_best_stats["std gp"]).reshape(-1)) axs[0, 1].set_yscale("log") axs[1, 0].set_title("PF") axs[1, 0].plot(range(N), np.array(self.true_best_stats["pf"]).reshape(-1)) axs[1, 1].set_title("mu_PF") axs[1, 1].plot( range(N), np.abs(np.array(self.true_best_stats["mu_pf"]).reshape(-1))) axs[1, 1].set_yscale("log") axs[2, 1].set_title("std_PF") axs[2, 1].plot( range(N), np.sqrt(np.array(self.true_best_stats["var_pf"]).reshape(-1))) axs[2, 1].set_yscale("log") axs[2, 0].set_title("Irreducible noise") axs[2, 0].plot( range(N), np.sqrt( np.array( self.true_best_stats["residual_noise"]).reshape(-1))) axs[2, 0].set_yscale("log") plt.show() def func_val(self, x): if len(x.shape) == 1: x = x.reshape(1, -1) Y, _ = self.objective.evaluate(x, true_val=True) C, _ = self.constraint.evaluate(x, true_val=True) Y = np.array(Y).reshape(-1) out = Y.reshape(-1) * np.product(np.concatenate(C, axis=1) < 0, axis=1).reshape(-1) out = np.array(out).reshape(-1) return -out
def __init__(self, domain, initial_design, graph_function, normalize_Y=False, max_eval=-1, fn=None, fn_optimizer=None, noise_var=None, exact_feval=None, exploration_weight_function=None, learnDependencyStructureRate=None, learnParameterRate=None, acq_opt_restarts=1): #self.design_space = Design_space(domain.get_gpy_domain()) self.fn = fn self.objective = SingleObjective(self.fn, 1, "no name", space=domain) self._init_design(initial_design) self.domain = domain self.acquisition_optimizer = MPAcquisitionOptimizer( domain, graph_function, [], self.fn.mlflow_logging, max_eval=max_eval, acq_opt_restarts=acq_opt_restarts) #self.acquisition_optimizer = AcquisitionOptimizer(domain) # model needed for LCB self.model = MyGPModel( noise_var=noise_var, exact_feval=exact_feval, optimize_restarts=0, exploration_weight_function=exploration_weight_function, learnDependencyStructureRate=learnDependencyStructureRate, learnParameterRate=learnParameterRate, graph_function=graph_function, mlflow_logging=self.fn.mlflow_logging, fn=self.fn) ## !!! models inside acqu1 must be the same as models in MyModel !!! -> Ok in Python, the object are references, not copied self.acquisition = MyAcquisitionModular(self.model, self.acquisition_optimizer, domain) self.evaluator = Sequential(self.acquisition) self.modular_optimization = False self.cost = CostModel(None) self.fn_optimizer = fn_optimizer super(MyBOModular, self).__init__(model=self.model, space=domain, objective=self.objective, acquisition=self.acquisition, evaluator=self.evaluator, X_init=self.X, Y_init=self.Y, cost=self.cost, normalize_Y=normalize_Y, model_update_interval=1)