/** * @brief Returns the best known value for indicator_name matching the given key if the key is found, and * throws a coco_error otherwise. */ static double suite_biobj_get_best_value(const char *indicator_name, const char *key) { size_t i, count; double best_value = 0; char *curr_key; if (strcmp(indicator_name, "hyp") == 0) { curr_key = coco_allocate_string(COCO_PATH_MAX); count = sizeof(suite_biobj_best_values_hyp) / sizeof(char *); for (i = 0; i < count; i++) { sscanf(suite_biobj_best_values_hyp[i], "%s %lf", curr_key, &best_value); if (strcmp(curr_key, key) == 0) { coco_free_memory(curr_key); return best_value; } } coco_free_memory(curr_key); coco_warning("suite_biobj_get_best_value(): best value of %s could not be found; set to 1.0", key); return 1.0; } else { coco_error("suite_biobj_get_best_value(): indicator %s not supported", indicator_name); return 0; /* Never reached */ } coco_error("suite_biobj_get_best_value(): unexpected exception"); return 0; /* Never reached */ }
/** * Wraps the observer around the problem if the observer is not NULL and invokes initialization of the * corresponding logger. * * @param problem The given COCO problem. * @param observer The COCO observer that will wrap the problem. * @returns The observed problem in the form of a new COCO problem instance or the same problem if the * observer is NULL. */ coco_problem_t *coco_problem_add_observer(coco_problem_t *problem, coco_observer_t *observer) { if ((observer == NULL) || (observer->is_active == 0)) { coco_warning("The problem will not be observed. %s", observer == NULL ? "(observer == NULL)" : "(observer not active)"); return problem; } return observer->logger_initialize_function(observer, problem); }
/** * Layer added to the transformed-problem evaluate_function by the logger */ static void logger_bbob2009_evaluate(coco_problem_t *self, const double *x, double *y) { logger_bbob2009_t *data = coco_transformed_get_data(self); coco_problem_t * inner_problem = coco_transformed_get_inner_problem(self); if (!data->is_initialized) { logger_bbob2009_initialize(data, inner_problem); } if (bbob2009_logger_verbosity > 2 && data->number_of_evaluations == 0) { if (inner_problem->suite_dep_index >= 0) { printf("%4ld: ", inner_problem->suite_dep_index); } printf("on problem %s ... ", coco_problem_get_id(inner_problem)); } coco_evaluate_function(inner_problem, x, y); data->last_fvalue = y[0]; data->written_last_eval = 0; if (data->number_of_evaluations == 0 || y[0] < data->best_fvalue) { size_t i; data->best_fvalue = y[0]; for (i = 0; i < self->number_of_variables; i++) data->best_solution[i] = x[i]; } data->number_of_evaluations++; /* Add sanity check for optimal f value */ /* assert(y[0] >= data->optimal_fvalue); */ if (!bbob2009_raisedOptValWarning && y[0] < data->optimal_fvalue) { coco_warning("Observed fitness is smaller than supposed optimal fitness."); bbob2009_raisedOptValWarning = 1; } /* Add a line in the .dat file for each logging target reached. */ if (y[0] - data->optimal_fvalue <= data->f_trigger) { logger_bbob2009_write_data(data->fdata_file, data->number_of_evaluations, y[0], data->best_fvalue, data->optimal_fvalue, x, self->number_of_variables); logger_bbob2009_update_f_trigger(data, y[0]); } /* Add a line in the .tdat file each time an fevals trigger is reached. */ if (data->number_of_evaluations >= data->t_trigger) { data->written_last_eval = 1; logger_bbob2009_write_data(data->tdata_file, data->number_of_evaluations, y[0], data->best_fvalue, data->optimal_fvalue, x, self->number_of_variables); logger_bbob2009_update_t_trigger(data, self->number_of_variables); } /* Flush output so that impatient users can see progress. */ fflush(data->fdata_file); }
/** * @brief Creates the transformation. */ static coco_problem_t *transform_vars_asymmetric(coco_problem_t *inner_problem, const double beta) { transform_vars_asymmetric_data_t *data; coco_problem_t *problem; data = (transform_vars_asymmetric_data_t *) coco_allocate_memory(sizeof(*data)); data->x = coco_allocate_vector(inner_problem->number_of_variables); data->beta = beta; problem = coco_problem_transformed_allocate(inner_problem, data, transform_vars_asymmetric_free, "transform_vars_asymmetric"); problem->evaluate_function = transform_vars_asymmetric_evaluate; if (coco_problem_best_parameter_not_zero(inner_problem)) { coco_warning("transform_vars_asymmetric(): 'best_parameter' not updated, set to NAN"); coco_vector_set_to_nan(inner_problem->best_parameter, inner_problem->number_of_variables); } return problem; }
static coco_problem_t *logger_bbob2009(coco_problem_t *inner_problem, const char *alg_name) { logger_bbob2009_t *data; coco_problem_t *self; data = coco_allocate_memory(sizeof(*data)); data->alg_name = coco_strdup(alg_name); if (bbob2009_logger_is_open) coco_error("The current bbob2009_logger (observer) must be closed before a new one is opened"); /* This is the name of the folder which happens to be the algName */ data->path = coco_strdup(alg_name); data->index_file = NULL; data->fdata_file = NULL; data->tdata_file = NULL; data->rdata_file = NULL; data->number_of_variables = inner_problem->number_of_variables; if (inner_problem->best_value == NULL) { /* coco_error("Optimal f value must be defined for each problem in order for the logger to work properly"); */ /* Setting the value to 0 results in the assertion y>=optimal_fvalue being susceptible to failure */ coco_warning("undefined optimal f value. Set to 0"); data->optimal_fvalue = 0; } else { data->optimal_fvalue = *(inner_problem->best_value); } bbob2009_raisedOptValWarning = 0; data->idx_f_trigger = INT_MAX; data->idx_t_trigger = 0; data->idx_tdim_trigger = 0; data->f_trigger = DBL_MAX; data->t_trigger = 0; data->number_of_evaluations = 0; data->best_solution = coco_allocate_vector(inner_problem->number_of_variables); /* TODO: the following inits are just to be in the safe side and * should eventually be removed. Some fields of the bbob2009_logger struct * might be useless */ data->function_id = coco_problem_get_suite_dep_function_id(inner_problem); data->instance_id = coco_problem_get_suite_dep_instance_id(inner_problem); data->written_last_eval = 1; data->last_fvalue = DBL_MAX; data->is_initialized = 0; self = coco_transformed_allocate(inner_problem, data, logger_bbob2009_free); self->evaluate_function = logger_bbob2009_evaluate; bbob2009_logger_is_open = 1; return self; }
/** * Initializes the toy logger. */ static coco_problem_t *logger_toy(coco_observer_t *observer, coco_problem_t *problem) { logger_toy_t *logger; coco_problem_t *self; FILE *output_file; if (problem->number_of_objectives != 1) { coco_warning("logger_toy(): The toy logger shouldn't be used to log a problem with %d objectives", problem->number_of_objectives); } logger = coco_allocate_memory(sizeof(*logger)); logger->observer = observer; logger->next_target = 0; logger->number_of_evaluations = 0; output_file = ((observer_toy_t *) logger->observer->data)->log_file; fprintf(output_file, "\n%s, %s\n", coco_problem_get_id(problem), coco_problem_get_name(problem)); self = coco_transformed_allocate(problem, logger, NULL); self->evaluate_function = logger_toy_evaluate; return self; }
/** * @brief Computes the instance number of the second problem/objective so that the resulting bi-objective * problem has more than a single optimal solution. * * Starts by setting instance2 = instance1 + 1 and increases this number until an appropriate instance has * been found (or until a maximum number of tries has been reached, in which case it throws a coco_error). * An appropriate instance is the one for which the resulting bi-objective problem (in any considered * dimension) has the ideal and nadir points apart enough in the objective space and the extreme optimal * points apart enough in the decision space. When the instance has been found, it is output through * coco_warning, so that the user can see it and eventually manually add it to suite_biobj_instances. */ static size_t suite_biobj_get_new_instance(coco_suite_t *suite, const size_t instance, const size_t instance1, const size_t num_bbob_functions, const size_t *bbob_functions) { size_t instance2 = 0; size_t num_tries = 0; const size_t max_tries = 1000; const double apart_enough = 1e-4; int appropriate_instance_found = 0, break_search, warning_produced = 0; coco_problem_t *problem1, *problem2, *problem = NULL; size_t d, f1, f2, i; size_t function1, function2, dimension; double norm; suite_biobj_t *data; assert(suite->data); data = (suite_biobj_t *) suite->data; while ((!appropriate_instance_found) && (num_tries < max_tries)) { num_tries++; instance2 = instance1 + num_tries; break_search = 0; /* An instance is "appropriate" if the ideal and nadir points in the objective space and the two * extreme optimal points in the decisions space are apart enough for all problems (all dimensions * and function combinations); therefore iterate over all dimensions and function combinations */ for (f1 = 0; (f1 < num_bbob_functions) && !break_search; f1++) { function1 = bbob_functions[f1]; for (f2 = f1; (f2 < num_bbob_functions) && !break_search; f2++) { function2 = bbob_functions[f2]; for (d = 0; (d < suite->number_of_dimensions) && !break_search; d++) { dimension = suite->dimensions[d]; if (dimension == 0) { if (!warning_produced) coco_warning("suite_biobj_get_new_instance(): remove filtering of dimensions to get generally acceptable instances!"); warning_produced = 1; continue; } problem1 = coco_get_bbob_problem(function1, dimension, instance1); problem2 = coco_get_bbob_problem(function2, dimension, instance2); if (problem) { coco_problem_stacked_free(problem); problem = NULL; } problem = coco_problem_stacked_allocate(problem1, problem2); /* Check whether the ideal and nadir points are too close in the objective space */ norm = mo_get_norm(problem->best_value, problem->nadir_value, 2); if (norm < 1e-1) { /* TODO How to set this value in a sensible manner? */ coco_debug( "suite_biobj_get_new_instance(): The ideal and nadir points of %s are too close in the objective space", problem->problem_id); coco_debug("norm = %e, ideal = %e\t%e, nadir = %e\t%e", norm, problem->best_value[0], problem->best_value[1], problem->nadir_value[0], problem->nadir_value[1]); break_search = 1; } /* Check whether the extreme optimal points are too close in the decision space */ norm = mo_get_norm(problem1->best_parameter, problem2->best_parameter, problem->number_of_variables); if (norm < apart_enough) { coco_debug( "suite_biobj_get_new_instance(): The extremal optimal points of %s are too close in the decision space", problem->problem_id); coco_debug("norm = %e", norm); break_search = 1; } } } } /* Clean up */ if (problem) { coco_problem_stacked_free(problem); problem = NULL; } if (break_search) { /* The search was broken, continue with next instance2 */ continue; } else { /* An appropriate instance was found */ appropriate_instance_found = 1; coco_info("suite_biobj_set_new_instance(): Instance %lu created from instances %lu and %lu", instance, instance1, instance2); /* Save the instance to new_instances */ for (i = 0; i < data->max_new_instances; i++) { if (data->new_instances[i][0] == 0) { data->new_instances[i][0] = instance; data->new_instances[i][1] = instance1; data->new_instances[i][2] = instance2; break; }; } } } if (!appropriate_instance_found) { coco_error("suite_biobj_get_new_instance(): Could not find suitable instance %lu in $lu tries", instance, num_tries); return 0; /* Never reached */ } return instance2; }
/** * @brief Returns the numbers defined by the ranges. * * Reads ranges from a string of positive ranges separated by commas. For example: "-3,5-6,8-". Returns the * numbers that are defined by the ranges if min and max are used as their extremes. If the ranges with open * beginning/end are not allowed, use 0 as min/max. The returned string has an appended 0 to mark its end. * A maximum of max_count values is returned. If there is a problem with one of the ranges, the parsing stops * and the current result is returned. The memory of the returned object needs to be freed by the caller. */ static size_t *coco_string_parse_ranges(const char *string, const size_t min, const size_t max, const char *name, const size_t max_count) { char *ptr, *dash = NULL; char **ranges, **numbers; size_t i, j, count; size_t num[2]; size_t *result; size_t i_result = 0; char *str = coco_strdup(string); /* Check for empty string */ if ((str == NULL) || (strlen(str) == 0)) { coco_warning("coco_string_parse_ranges(): cannot parse empty ranges"); coco_free_memory(str); return NULL; } ptr = str; /* Check for disallowed characters */ while (*ptr != '\0') { if ((*ptr != '-') && (*ptr != ',') && !isdigit((unsigned char )*ptr)) { coco_warning("coco_string_parse_ranges(): problem parsing '%s' - cannot parse ranges with '%c'", str, *ptr); coco_free_memory(str); return NULL; } else ptr++; } /* Check for incorrect boundaries */ if ((max > 0) && (min > max)) { coco_warning("coco_string_parse_ranges(): incorrect boundaries"); coco_free_memory(str); return NULL; } result = coco_allocate_vector_size_t(max_count + 1); /* Split string to ranges w.r.t commas */ ranges = coco_string_split(str, ','); coco_free_memory(str); if (ranges) { /* Go over the current range */ for (i = 0; *(ranges + i); i++) { ptr = *(ranges + i); /* Count the number of '-' */ count = 0; while (*ptr != '\0') { if (*ptr == '-') { if (count == 0) /* Remember the position of the first '-' */ dash = ptr; count++; } ptr++; } /* Point again to the start of the range */ ptr = *(ranges + i); /* Check for incorrect number of '-' */ if (count > 1) { coco_warning("coco_string_parse_ranges(): problem parsing '%s' - too many '-'s", string); /* Cleanup */ for (j = i; *(ranges + j); j++) coco_free_memory(*(ranges + j)); coco_free_memory(ranges); if (i_result == 0) { coco_free_memory(result); return NULL; } result[i_result] = 0; return result; } else if (count == 0) { /* Range is in the format: n (no range) */ num[0] = (size_t) strtol(ptr, NULL, 10); num[1] = num[0]; } else { /* Range is in one of the following formats: n-m / -n / n- / - */ /* Split current range to numbers w.r.t '-' */ numbers = coco_string_split(ptr, '-'); j = 0; if (numbers) { /* Read the numbers */ for (j = 0; *(numbers + j); j++) { assert(j < 2); num[j] = (size_t) strtol(*(numbers + j), NULL, 10); coco_free_memory(*(numbers + j)); } } coco_free_memory(numbers); if (j == 0) { /* Range is in the format - (open ends) */ if ((min == 0) || (max == 0)) { coco_warning("coco_string_parse_ranges(): '%s' ranges cannot have an open ends; some ranges ignored", name); /* Cleanup */ for (j = i; *(ranges + j); j++) coco_free_memory(*(ranges + j)); coco_free_memory(ranges); if (i_result == 0) { coco_free_memory(result); return NULL; } result[i_result] = 0; return result; } num[0] = min; num[1] = max; } else if (j == 1) { if (dash - *(ranges + i) == 0) { /* Range is in the format -n */ if (min == 0) { coco_warning("coco_string_parse_ranges(): '%s' ranges cannot have an open beginning; some ranges ignored", name); /* Cleanup */ for (j = i; *(ranges + j); j++) coco_free_memory(*(ranges + j)); coco_free_memory(ranges); if (i_result == 0) { coco_free_memory(result); return NULL; } result[i_result] = 0; return result; } num[1] = num[0]; num[0] = min; } else { /* Range is in the format n- */ if (max == 0) { coco_warning("coco_string_parse_ranges(): '%s' ranges cannot have an open end; some ranges ignored", name); /* Cleanup */ for (j = i; *(ranges + j); j++) coco_free_memory(*(ranges + j)); coco_free_memory(ranges); if (i_result == 0) { coco_free_memory(result); return NULL; } result[i_result] = 0; return result; } num[1] = max; } } /* if (j == 2), range is in the format n-m and there is nothing to do */ } /* Make sure the boundaries are taken into account */ if ((min > 0) && (num[0] < min)) { num[0] = min; coco_warning("coco_string_parse_ranges(): '%s' ranges adjusted to be >= %lu", name, (unsigned long) min); } if ((max > 0) && (num[1] > max)) { num[1] = max; coco_warning("coco_string_parse_ranges(): '%s' ranges adjusted to be <= %lu", name, (unsigned long) max); } if (num[0] > num[1]) { coco_warning("coco_string_parse_ranges(): '%s' ranges not within boundaries; some ranges ignored", name); /* Cleanup */ for (j = i; *(ranges + j); j++) coco_free_memory(*(ranges + j)); coco_free_memory(ranges); if (i_result == 0) { coco_free_memory(result); return NULL; } result[i_result] = 0; return result; } /* Write in result */ for (j = num[0]; j <= num[1]; j++) { if (i_result > max_count - 1) break; result[i_result++] = j; } coco_free_memory(*(ranges + i)); *(ranges + i) = NULL; } } coco_free_memory(ranges); if (i_result == 0) { coco_free_memory(result); return NULL; } result[i_result] = 0; return result; }
/** * Currently, three observers are supported: * - "bbob" is the observer for single-objective (both noisy and noiseless) problems with known optima, which * creates *.info, *.dat, *.tdat and *.rdat files and logs the distance to the optimum. * - "bbob-biobj" is the observer for bi-objective problems, which creates *.info and *.dat files for the * given indicators, as well as an archive folder with *.dat files containing nondominated solutions. * - "toy" is a simple observer that logs when a target has been hit. * * @param observer_name A string containing the name of the observer. Currently supported observer names are * "bbob", "bbob-biobj", "toy". "no_observer", "" or NULL return NULL. * @param observer_options A string of pairs "key: value" used to pass the options to the observer. Some * observer options are general, while others are specific to some observers. Here we list only the general * options, see observer_bbob, observer_biobj and observer_toy for options of the specific observers. * - "result_folder: NAME" determines the output folder. If the folder with the given name already exists, * first NAME_001 will be tried, then NAME_002 and so on. The default value is "results". * - "algorithm_name: NAME", where NAME is a short name of the algorithm that will be used in plots (no * spaces are allowed). The default value is "ALG". * - "algorithm_info: STRING" stores the description of the algorithm. If it contains spaces, it must be * surrounded by double quotes. The default value is "" (no description). * - "precision_x: VALUE" defines the precision used when outputting variables and corresponds to the number * of digits to be printed after the decimal point. The default value is 8. * - precision_f: VALUE defines the precision used when outputting f values and corresponds to the number of * digits to be printed after the decimal point. The default value is 15. * @return The constructed observer object or NULL if observer_name equals NULL, "" or "no_observer". */ coco_observer_t *coco_observer(const char *observer_name, const char *observer_options) { coco_observer_t *observer; char *result_folder, *algorithm_name, *algorithm_info; int precision_x, precision_f; if (0 == strcmp(observer_name, "no_observer")) { return NULL; } else if (strlen(observer_name) == 0) { coco_warning("Empty observer_name has no effect. To prevent this warning use 'no_observer' instead"); return NULL; } result_folder = (char *) coco_allocate_memory(COCO_PATH_MAX); algorithm_name = (char *) coco_allocate_memory(COCO_PATH_MAX); algorithm_info = (char *) coco_allocate_memory(5 * COCO_PATH_MAX); /* Read result_folder, algorithm_name and algorithm_info from the observer_options and use * them to initialize the observer */ if (coco_options_read_string(observer_options, "result_folder", result_folder) == 0) { strcpy(result_folder, "exdata-default"); } coco_create_unique_path(&result_folder); coco_info("Results will be output to folder %s", result_folder); if (coco_options_read_string(observer_options, "algorithm_name", algorithm_name) == 0) { strcpy(algorithm_name, "ALG"); } if (coco_options_read_string(observer_options, "algorithm_info", algorithm_info) == 0) { strcpy(algorithm_info, ""); } precision_x = 8; if (coco_options_read_int(observer_options, "precision_x", &precision_x) != 0) { if ((precision_x < 1) || (precision_x > 32)) precision_x = 8; } precision_f = 15; if (coco_options_read_int(observer_options, "precision_f", &precision_f) != 0) { if ((precision_f < 1) || (precision_f > 32)) precision_f = 15; } observer = coco_observer_allocate(result_folder, algorithm_name, algorithm_info, precision_x, precision_f); coco_free_memory(result_folder); coco_free_memory(algorithm_name); coco_free_memory(algorithm_info); /* Here each observer must have an entry */ if (0 == strcmp(observer_name, "toy")) { observer_toy(observer, observer_options); } else if (0 == strcmp(observer_name, "bbob")) { observer_bbob(observer, observer_options); } else if (0 == strcmp(observer_name, "bbob-biobj")) { observer_biobj(observer, observer_options); } else { coco_warning("Unknown observer!"); return NULL; } return observer; }
int coco_archive_add_solution(coco_archive_t *archive, const double y1, const double y2, const char *text) { coco_archive_avl_item_t* insert_item; avl_node_t *node, *next_node; int update = 0; int dominance; double *y = coco_allocate_vector(2); y[0] = y1; y[1] = y2; insert_item = coco_archive_node_item_create(y, archive->ideal, archive->nadir, archive->number_of_objectives, text); coco_free_memory(y); /* Find the first point that is not worse than the new point (NULL if such point does not exist) */ node = avl_item_search_right(archive->tree, insert_item, NULL); if (node == NULL) { /* The new point is an extreme point */ update = 1; next_node = archive->tree->head; } else { dominance = mo_get_dominance(insert_item->normalized_y, ((coco_archive_avl_item_t*) node->item)->normalized_y, archive->number_of_objectives); if (dominance > -1) { update = 1; next_node = node->next; if (dominance == 1) { /* The new point dominates the next point, remove the next point */ assert((node != archive->extreme1) && (node != archive->extreme2)); avl_node_delete(archive->tree, node); } } else { /* The new point is dominated or equal to an existing one, ignore */ update = 0; } } if (!update) { coco_archive_node_item_free(insert_item, NULL); } else { /* Perform tree update */ while (next_node != NULL) { /* Check the dominance relation between the new node and the next node. There are only two possibilities: * dominance = 0: the new node and the next node are nondominated * dominance = 1: the new node dominates the next node */ node = next_node; dominance = mo_get_dominance(insert_item->normalized_y, ((coco_archive_avl_item_t*) node->item)->normalized_y, archive->number_of_objectives); if (dominance == 1) { next_node = node->next; /* The new point dominates the next point, remove the next point */ assert((node != archive->extreme1) && (node != archive->extreme2)); avl_node_delete(archive->tree, node); } else { break; } } if(avl_item_insert(archive->tree, insert_item) == NULL) { coco_warning("Solution %s did not update the archive", text); update = 0; } archive->is_up_to_date = 0; } return update; }