def thompson_sampling( bufferx, objective_weights, regression_models, param_space, scalarization_method, objective_limits, model_type, classification_model=None, number_of_cpus=0, ): """ Multi-objective thompson sampling acquisition function as detailed in https://arxiv.org/abs/1805.12168. :param bufferx: a list of tuples containing the points to predict and scalarize. :param objective_weights: a list containing the weights for each objective. :param regression_models: the surrogate models used to evaluate points. :param param_space: a space object containing the search space. :param scalarization_method: a string indicating which scalarization method to use. :param objective_limits: a dictionary with estimated minimum and maximum values for each objective. :param number_of_cpus: an integer for the number of cpus to be used in parallel. :return: a list of scalarized values for each point in bufferx. """ tmp_objective_limits = copy.deepcopy(objective_limits) model_predictions = {} t0 = datetime.datetime.now() model_predictions = models.sample_model_posterior( bufferx, regression_models, model_type, param_space ) number_of_predictions = len(model_predictions[list(model_predictions.keys())[0]]) if classification_model != None: classification_prediction_results = models.model_probabilities( bufferx, classification_model, param_space ) feasible_parameter = param_space.get_feasible_parameter()[0] true_value_index = ( classification_model[feasible_parameter].classes_.tolist().index(True) ) feasibility_indicator = classification_prediction_results[feasible_parameter][ :, true_value_index ] else: feasibility_indicator = [ 1 ] * number_of_predictions # if no classification model is used, then all points are feasible if scalarization_method == "linear": scalarized_predictions = np.zeros(number_of_predictions) for objective in regression_models: scalarized_predictions += ( objective_weights[objective] * model_predictions[objective] ) scalarized_predictions = scalarized_predictions * feasibility_indicator # The paper does not propose this, I applied their methodology to the original tchebyshev to get the approach below # Important: since this was not proposed in the paper, their proofs and bounds for the modified_tchebyshev may not be valid here. elif scalarization_method == "tchebyshev": scalarized_predictions = np.zeros(number_of_predictions) scalarized_values = np.zeros(number_of_predictions) total_values = np.zeros(number_of_predictions) for objective in regression_models: scalarized_values = objective_weights[objective] * np.absolute( model_predictions[objective] ) total_values += scalarized_values scalarized_predictions = np.maximum( scalarized_values, scalarized_predictions ) scalarized_predictions += 0.05 * total_values scalarized_predictions = scalarized_predictions * feasibility_indicator elif scalarization_method == "modified_tchebyshev": scalarized_predictions = np.full((number_of_predictions), float("inf")) reciprocated_weights = reciprocate_weights(objective_weights) for objective in regression_models: scalarized_value = reciprocated_weights[objective] * np.absolute( model_predictions[objective] ) # TODO scalarized_predictions2 not defined scalarized_predictions = np.minimum( scalarized_value, scalarized_predictions ) scalarized_predictions = scalarized_predictions * feasibility_indicator scalarized_predictions = ( -scalarized_predictions ) # We will minimize later, but we want to maximize instead, so we invert the sign else: print("Error: unrecognized scalarization method:", scalarization_method) raise SystemExit return scalarized_predictions, tmp_objective_limits
def EI( bufferx, data_array, objective_weights, regression_models, param_space, scalarization_method, objective_limits, iteration_number, model_type, classification_model=None, number_of_cpus=0, ): """ Compute a multi-objective EI acquisition function on bufferx. The mean and variance of the predictions are computed as defined by Hutter et al.: https://arxiv.org/pdf/1211.0906.pdf :param bufferx: a list of tuples containing the points to predict and scalarize. :param data_array: a dictionary containing the previously run points and their function values. :param objective_weights: a list containing the weights for each objective. :param regression_models: the surrogate models used to evaluate points. :param param_space: a space object containing the search space. :param scalarization_method: a string indicating which scalarization method to use. :param evaluations_per_optimization_iteration: how many configurations to return. :param objective_limits: a dictionary with estimated minimum and maximum values for each objective. :param iteration_number: an integer for the current iteration number, used to compute the beta :param classification_model: the surrogate model used to evaluate feasibility constraints :param number_of_cpus: an integer for the number of cpus to be used in parallel. :return: a list of scalarized values for each point in bufferx. """ augmentation_constant = 0.05 prediction_means = {} prediction_variances = {} number_of_predictions = len(bufferx) tmp_objective_limits = copy.deepcopy(objective_limits) prediction_means, prediction_variances = models.compute_model_mean_and_uncertainty( bufferx, regression_models, model_type, param_space, var=True ) if classification_model != None: classification_prediction_results = models.model_probabilities( bufferx, classification_model, param_space ) feasible_parameter = param_space.get_feasible_parameter()[0] true_value_index = ( classification_model[feasible_parameter].classes_.tolist().index(True) ) feasibility_indicator = classification_prediction_results[feasible_parameter][ :, true_value_index ] else: feasibility_indicator = [ 1 ] * number_of_predictions # if no classification model is used, then all points are feasible data_array_scalarization, tmp_objective_limits = compute_data_array_scalarization( data_array, objective_weights, tmp_objective_limits, scalarization_method ) if scalarization_method == "linear": scalarized_predictions = np.zeros(number_of_predictions) scalarized_value = 0 for objective in regression_models: if ( tmp_objective_limits[objective][1] - tmp_objective_limits[objective][0] == 0 ): difference_obj = 1 else: difference_obj = ( tmp_objective_limits[objective][1] - tmp_objective_limits[objective][0] ) f_min = 1 - ( min(data_array[objective]) - tmp_objective_limits[objective][0] ) / (difference_obj) x_std = np.sqrt(prediction_variances[objective]) x_mean = 1 - prediction_means[objective] v = (x_mean - f_min) / x_std objective_ei = (x_mean - f_min) * stats.norm.cdf( v ) + x_std * stats.norm.pdf(v) scalarized_predictions += objective_ei * objective_weights[objective] scalarized_predictions = -1 * scalarized_predictions * feasibility_indicator # The paper does not propose this, I applied their methodology to the original tchebyshev to get the approach below # Important: since this was not proposed in the paper, their proofs and bounds for the modified_tchebyshev may not be valid here. elif scalarization_method == "tchebyshev": scalarized_predictions = np.zeros(number_of_predictions) total_value = np.zeros(number_of_predictions) for objective in regression_models: if ( tmp_objective_limits[objective][1] - tmp_objective_limits[objective][0] == 0 ): difference_obj = 1 else: difference_obj = ( tmp_objective_limits[objective][1] - tmp_objective_limits[objective][0] ) f_min = 1 - ( min(data_array[objective]) - tmp_objective_limits[objective][0] ) / (difference_obj) x_std = np.sqrt(prediction_variances[objective]) x_mean = 1 - prediction_means[objective] v = (x_mean - f_min) / x_std scalarized_value = objective_weights[objective] * ( (1 - prediction_means[objective] - f_min) * stats.norm.cdf(v) + x_std * stats.norm.pdf(v) ) scalarized_predictions = np.maximum( scalarized_value, scalarized_predictions ) total_value += scalarized_value scalarized_predictions = ( -1 * (scalarized_predictions + total_value * augmentation_constant) * feasibility_indicator ) elif scalarization_method == "modified_tchebyshev": scalarized_predictions = np.full((number_of_predictions), float("inf")) reciprocated_weights = reciprocate_weights(objective_weights) for objective in regression_models: if ( tmp_objective_limits[objective][1] - tmp_objective_limits[objective][0] == 0 ): difference_obj = 1 else: difference_obj = ( tmp_objective_limits[objective][1] - tmp_objective_limits[objective][0] ) f_min = 1 - ( min(data_array[objective]) - tmp_objective_limits[objective][0] ) / (difference_obj) x_std = np.sqrt(prediction_variances[objective]) x_mean = 1 - prediction_means[objective] v = (x_mean - f_min) / x_std scalarized_value = reciprocated_weights[objective] * ( (x_mean - f_min) * stats.norm.cdf(v) + x_std * stats.norm.pdf(v) ) scalarized_predictions = np.minimum( scalarized_value, scalarized_predictions ) scalarized_predictions = scalarized_predictions * feasibility_indicator else: print("Error: unrecognized scalarization method:", scalarization_method) raise SystemExit return scalarized_predictions, tmp_objective_limits
def ucb( bufferx, objective_weights, regression_models, param_space, scalarization_method, objective_limits, iteration_number, model_type, classification_model=None, number_of_cpus=0, ): """ Multi-objective ucb acquisition function as detailed in https://arxiv.org/abs/1805.12168. The mean and variance of the predictions are computed as defined by Hutter et al.: https://arxiv.org/pdf/1211.0906.pdf :param bufferx: a list of tuples containing the points to predict and scalarize. :param objective_weights: a list containing the weights for each objective. :param regression_models: the surrogate models used to evaluate points. :param param_space: a space object containing the search space. :param scalarization_method: a string indicating which scalarization method to use. :param evaluations_per_optimization_iteration: how many configurations to return. :param objective_limits: a dictionary with estimated minimum and maximum values for each objective. :param iteration_number: an integer for the current iteration number, used to compute the beta :param classification_model: the surrogate model used to evaluate feasibility constraints :param number_of_cpus: an integer for the number of cpus to be used in parallel. :return: a list of scalarized values for each point in bufferx. """ beta = np.sqrt(0.125 * np.log(2 * iteration_number + 1)) augmentation_constant = 0.05 prediction_means = {} prediction_variances = {} number_of_predictions = len(bufferx) tmp_objective_limits = copy.deepcopy(objective_limits) prediction_means, prediction_variances = models.compute_model_mean_and_uncertainty( bufferx, regression_models, model_type, param_space, var=True ) if classification_model != None: classification_prediction_results = models.model_probabilities( bufferx, classification_model, param_space ) feasible_parameter = param_space.get_feasible_parameter()[0] true_value_index = ( classification_model[feasible_parameter].classes_.tolist().index(True) ) feasibility_indicator = classification_prediction_results[feasible_parameter][ :, true_value_index ] else: feasibility_indicator = [ 1 ] * number_of_predictions # if no classification model is used, then all points are feasible # Compute scalarization if scalarization_method == "linear": scalarized_predictions = np.zeros(number_of_predictions) beta_factor = 0 for objective in regression_models: scalarized_predictions += ( objective_weights[objective] * prediction_means[objective] ) beta_factor += ( objective_weights[objective] * prediction_variances[objective] ) scalarized_predictions -= beta * np.sqrt(beta_factor) scalarized_predictions = scalarized_predictions * feasibility_indicator # The paper does not propose this, I applied their methodology to the original tchebyshev to get the approach below # Important: since this was not proposed in the paper, their proofs and bounds for the modified_tchebyshev may not be valid here. elif scalarization_method == "tchebyshev": scalarized_predictions = np.zeros(number_of_predictions) total_values = np.zeros(number_of_predictions) for objective in regression_models: scalarized_values = objective_weights[objective] * np.absolute( prediction_means[objective] - beta * np.sqrt(prediction_variances[objective]) ) total_values += scalarized_values scalarized_predictions = np.maximum( scalarized_values, scalarized_predictions ) scalarized_predictions += augmentation_constant * total_values scalarized_predictions = scalarized_predictions * feasibility_indicator elif scalarization_method == "modified_tchebyshev": scalarized_predictions = np.full((number_of_predictions), float("inf")) reciprocated_weights = reciprocate_weights(objective_weights) for objective in regression_models: scalarized_value = reciprocated_weights[objective] * ( prediction_means[objective] - beta * np.sqrt(prediction_variances[objective]) ) scalarized_predictions = np.minimum( scalarized_value, scalarized_predictions ) scalarized_predictions = scalarized_predictions * feasibility_indicator scalarized_predictions = ( -scalarized_predictions ) # We will minimize later, but we want to maximize instead, so we invert the sign else: print("Error: unrecognized scalarization method:", scalarization_method) raise SystemExit return scalarized_predictions, tmp_objective_limits