Commit 80f82b8c authored by Brice Videau's avatar Brice Videau
Browse files

Added basic support for configurations.

parent 504dee7a
......@@ -38,6 +38,7 @@ enum ccs_error_e {
CCS_INVALID_SCALE,
CCS_INVALID_DISTRIBUTION,
CCS_INVALID_HYPERPARAMETER,
CCS_INVALID_NAME,
CCS_TYPE_NOT_COMPARABLE,
CCS_INVALID_BOUNDS,
CCS_OUT_OF_BOUNDS,
......@@ -137,12 +138,12 @@ struct ccs_interval_s {
typedef struct ccs_interval_s ccs_interval_t;
struct ccs_datum_u {
struct ccs_datum_s {
ccs_value_t value;
ccs_data_type_t type;
};
typedef struct ccs_datum_u ccs_datum_t;
typedef struct ccs_datum_s ccs_datum_t;
extern const ccs_datum_t ccs_none;
......
......@@ -22,12 +22,17 @@ ccs_configuration_get_configuration_space(ccs_configuration_t configurati
extern ccs_error_t
ccs_configuration_get_user_data(ccs_configuration_t configuration,
void **user_data);
void **user_data_ret);
extern ccs_error_t
ccs_configuration_get_value(ccs_configuration_t configuration,
size_t index,
ccs_datum_t *value);
ccs_datum_t *value_ret);
extern ccs_error_t
ccs_configuration_set_value(ccs_configuration_t configuration,
size_t index,
ccs_datum_t value);
extern ccs_error_t
ccs_configuration_get_values(ccs_configuration_t configuration,
......@@ -35,6 +40,11 @@ ccs_configuration_get_values(ccs_configuration_t configuration,
ccs_datum_t *values,
size_t *num_values_ret);
extern ccs_error_t
ccs_configuration_get_value_by_name(ccs_configuration_t configuration,
const char *name,
ccs_datum_t *value_ret);
#ifdef __cplusplus
}
#endif
......
......@@ -57,6 +57,12 @@ ccs_configuration_space_get_hyperparameter_by_name(
const char * name,
ccs_hyperparameter_t *hyperparameter_ret);
extern ccs_error_t
ccs_configuration_space_get_hyperparameter_index_by_name(
ccs_configuration_space_t configuration_space,
const char *name,
size_t *index_ret);
extern ccs_error_t
ccs_configuration_space_get_hyperparameters(ccs_configuration_space_t configuration_space,
size_t num_hyperparameters,
......@@ -123,7 +129,7 @@ ccs_configuration_space_check_configuration_values(ccs_configuration_space_t co
extern ccs_error_t
ccs_configuration_space_get_default_configuration(ccs_configuration_space_t configuration_space,
ccs_configuration_t *configuration);
ccs_configuration_t *configuration_ret);
extern ccs_error_t
ccs_configuration_space_sample_configuration(ccs_configuration_space_t configuration_space,
......
......@@ -70,6 +70,17 @@ extern ccs_error_t
ccs_hyperparameter_get_default_distribution(ccs_hyperparameter_t hyperparameter,
ccs_distribution_t *distribution);
extern ccs_error_t
ccs_hyperparameter_check_value(ccs_hyperparameter_t hyperparameter,
ccs_datum_t value,
ccs_bool_t *result_ret);
extern ccs_error_t
ccs_hyperparameter_check_values(ccs_hyperparameter_t hyperparameter,
size_t num_values,
const ccs_datum_t *values,
ccs_bool_t *results);
// Sampling Interface
extern ccs_error_t
ccs_hyperparameter_sample(ccs_hyperparameter_t hyperparameter,
......
......@@ -18,11 +18,14 @@ libcconfigspace_la_SOURCES = \
hyperparameter.c \
hyperparameter_internal.h \
hyperparameter_numerical.c \
hyperparameter_categorical.c \
uthash.h \
datum_hash.h \
hyperparameter_categorical.c \
hyperparameter_ordinal.c \
utarray.h \
configuration_space.c \
configuration_space_internal.h
configuration_space_internal.h \
configuration.c \
configuration_internal.h
@VALGRIND_CHECK_RULES@
#include "cconfigspace_internal.h"
#include "configuration_internal.h"
static inline _ccs_configuration_ops_t *
ccs_configuration_get_ops(ccs_configuration_t configuration) {
return (_ccs_configuration_ops_t *)configuration->obj.ops;
}
static ccs_error_t
_ccs_configuration_del(ccs_object_t object) {
ccs_configuration_t configuration = (ccs_configuration_t)object;
ccs_release_object(configuration->data->configuration_space);
return CCS_SUCCESS;
}
static _ccs_configuration_ops_t _configuration_ops =
{ {&_ccs_configuration_del} };
ccs_error_t
ccs_create_configuration(ccs_configuration_space_t configuration_space,
size_t num_values,
ccs_datum_t *values,
void *user_data,
ccs_configuration_t *configuration_ret) {
if (!configuration_ret)
return -CCS_INVALID_VALUE;
if (num_values && !values)
return -CCS_INVALID_VALUE;
if (!num_values && values)
return -CCS_INVALID_VALUE;
ccs_error_t err;
size_t num;
err = ccs_configuration_space_get_num_hyperparameters(configuration_space, &num);
if (err)
return err;
if (values && num != num_values)
return -CCS_INVALID_VALUE;
uintptr_t mem = (uintptr_t)calloc(1, sizeof(struct _ccs_configuration_s) + sizeof(struct _ccs_configuration_data_s) + num * sizeof(ccs_datum_t));
if (!mem)
return CCS_ENOMEM;
err = ccs_retain_object(configuration_space);
if (err) {
free((void*)mem);
return err;
}
ccs_configuration_t config = (ccs_configuration_t)mem;
_ccs_object_init(&(config->obj), CCS_CONFIGURATION, (_ccs_object_ops_t*)&_configuration_ops);
config->data = (struct _ccs_configuration_data_s*)(mem + sizeof(struct _ccs_configuration_s));
config->data->user_data = user_data;
config->data->num_values = num;
config->data->configuration_space = configuration_space;
config->data->values = (ccs_datum_t *)(mem + sizeof(struct _ccs_configuration_s) + sizeof(struct _ccs_configuration_data_s));
if (values)
for (size_t i = 0; i < num; i++)
config->data->values[i] = values[i];
*configuration_ret = config;
return CCS_SUCCESS;
}
ccs_error_t
ccs_configuration_get_configuration_space(ccs_configuration_t configuration,
ccs_configuration_space_t *configuration_space_ret) {
if (!configuration || !configuration->data)
return -CCS_INVALID_OBJECT;
if (!configuration_space_ret)
return -CCS_INVALID_VALUE;
*configuration_space_ret = configuration->data->configuration_space;
return CCS_SUCCESS;
}
ccs_error_t
ccs_configuration_get_user_data(ccs_configuration_t configuration,
void **user_data_ret) {
if (!configuration || !configuration->data)
return -CCS_INVALID_OBJECT;
if (!user_data_ret)
return -CCS_INVALID_VALUE;
*user_data_ret = configuration->data->user_data;
return CCS_SUCCESS;
}
ccs_error_t
ccs_configuration_get_value(ccs_configuration_t configuration,
size_t index,
ccs_datum_t *value_ret) {
if (!configuration || !configuration->data)
return -CCS_INVALID_OBJECT;
if (!value_ret)
return -CCS_INVALID_VALUE;
if (index >= configuration->data->num_values)
return -CCS_OUT_OF_BOUNDS;
*value_ret = configuration->data->values[index];
return CCS_SUCCESS;
}
ccs_error_t
ccs_configuration_set_value(ccs_configuration_t configuration,
size_t index,
ccs_datum_t value) {
if (!configuration || !configuration->data)
return -CCS_INVALID_OBJECT;
if (index >= configuration->data->num_values)
return -CCS_OUT_OF_BOUNDS;
configuration->data->values[index] = value;
return CCS_SUCCESS;
}
ccs_error_t
ccs_configuration_get_values(ccs_configuration_t configuration,
size_t num_values,
ccs_datum_t *values,
size_t *num_values_ret) {
if (!configuration || !configuration->data)
return -CCS_INVALID_OBJECT;
if (!num_values_ret && !values)
return -CCS_INVALID_VALUE;
if (num_values && !values)
return -CCS_INVALID_VALUE;
if (!num_values && values)
return -CCS_INVALID_VALUE;
size_t num = configuration->data->num_values;
if (values) {
if (num_values < num)
return -CCS_INVALID_VALUE;
for (size_t i = 0; i < num; i++)
values[i] = configuration->data->values[i];
for (size_t i = num; i < num_values; i++) {
values[i].value.i = 0;
values[i].type = CCS_NONE;
}
}
if (num_values_ret)
*num_values_ret = num;
return CCS_SUCCESS;
}
ccs_error_t
ccs_configuration_get_value_by_name(ccs_configuration_t configuration,
const char *name,
ccs_datum_t *value_ret) {
if (!configuration || !configuration->data)
return -CCS_INVALID_OBJECT;
if (!name)
return -CCS_INVALID_VALUE;
size_t index;
ccs_error_t err;
err = ccs_configuration_space_get_hyperparameter_index_by_name(
configuration->data->configuration_space, name, &index);
if (err)
return err;
*value_ret = configuration->data->values[index];
return CCS_SUCCESS;
}
#ifndef _CONFIGURATION_INTERNAL_H
#define _CONFIGURATION_INTERNAL_H
struct _ccs_configuration_ops_s {
_ccs_object_ops_t obj_ops;
};
typedef struct _ccs_configuration_ops_s _ccs_configuration_ops_t;
struct _ccs_configuration_data_s;
typedef struct _ccs_configuration_data_s _ccs_configuration_data_t;
struct _ccs_configuration_s {
_ccs_object_internal_t obj;
_ccs_configuration_data_t *data;
};
struct _ccs_configuration_data_s {
void *user_data;
ccs_configuration_space_t configuration_space;
size_t num_values;
ccs_datum_t *values;
};
#endif //_CONFIGURATION_INTERNAL_H
......@@ -261,10 +261,9 @@ ccs_configuration_space_get_hyperparameter(ccs_configuration_space_t configurat
return -CCS_INVALID_VALUE;
_ccs_hyperparameter_wrapper_t *wrapper = (_ccs_hyperparameter_wrapper_t*)
utarray_eltptr(configuration_space->data->hyperparameters, (unsigned int)index);
if (wrapper)
*hyperparameter_ret = wrapper->hyperparameter;
else
*hyperparameter_ret = NULL;
if (!wrapper)
return -CCS_OUT_OF_BOUNDS;
*hyperparameter_ret = wrapper->hyperparameter;
return CCS_SUCCESS;
}
......@@ -282,10 +281,29 @@ ccs_configuration_space_get_hyperparameter_by_name(
sz_name = strlen(name);
HASH_FIND(hh_name, configuration_space->data->name_hash,
name, sz_name, wrapper);
if (wrapper)
*hyperparameter_ret = wrapper->hyperparameter;
else
*hyperparameter_ret = NULL;
if (!wrapper)
return -CCS_INVALID_NAME;
*hyperparameter_ret = wrapper->hyperparameter;
return CCS_SUCCESS;
}
ccs_error_t
ccs_configuration_space_get_hyperparameter_index_by_name(
ccs_configuration_space_t configuration_space,
const char *name,
size_t *index_ret) {
if (!configuration_space || !configuration_space->data)
return -CCS_INVALID_OBJECT;
if (!index_ret)
return -CCS_INVALID_VALUE;
_ccs_hyperparameter_wrapper_t *wrapper;
size_t sz_name;
sz_name = strlen(name);
HASH_FIND(hh_name, configuration_space->data->name_hash,
name, sz_name, wrapper);
if (!wrapper)
return -CCS_INVALID_NAME;
*index_ret = wrapper->index;
return CCS_SUCCESS;
}
......@@ -306,6 +324,8 @@ ccs_configuration_space_get_hyperparameters(ccs_configuration_space_t configura
UT_array *array = configuration_space->data->hyperparameters;
size_t size = utarray_len(array);
if (hyperparameters) {
if (num_hyperparameters < size)
return -CCS_INVALID_VALUE;
_ccs_hyperparameter_wrapper_t *wrapper = NULL;
size_t index = 0;
while ( (wrapper = (_ccs_hyperparameter_wrapper_t *)utarray_next(array, wrapper)) )
......@@ -318,3 +338,35 @@ ccs_configuration_space_get_hyperparameters(ccs_configuration_space_t configura
return CCS_SUCCESS;
}
ccs_error_t
ccs_configuration_space_get_default_configuration(ccs_configuration_space_t configuration_space,
ccs_configuration_t *configuration_ret) {
if (!configuration_space || !configuration_space->data)
return -CCS_INVALID_OBJECT;
if (!configuration_ret)
return -CCS_INVALID_VALUE;
ccs_error_t err;
ccs_configuration_t config;
err = ccs_create_configuration(configuration_space, 0, NULL, NULL, &config);
if (err)
return err;
UT_array *array = configuration_space->data->hyperparameters;
size_t index = 0;
_ccs_hyperparameter_wrapper_t *wrapper = NULL;
ccs_datum_t d;
while ( (wrapper = (_ccs_hyperparameter_wrapper_t *)utarray_next(array, wrapper)) ) {
err = ccs_hyperparameter_get_default_value(wrapper->hyperparameter, &d);
if (unlikely(err))
goto error;
err = ccs_configuration_set_value(config, index++, d);
if (unlikely(err))
goto error;
}
*configuration_ret = config;
return CCS_SUCCESS;
error:
ccs_release_object(config);
return err;
}
#ifndef _DATUMHASH_H
#define _DATUMHASH_H
#define HASH_NONFATAL_OOM 1
#define HASH_FUNCTION(s,len,hashv) (hashv) = _hash_datum((ccs_datum_t *)(s))
#define HASH_KEYCMP(a,b,len) (_datum_cmp((ccs_datum_t *)a, (ccs_datum_t *)b))
#include "uthash.h"
/* BEWARE: ccs_float_t are used as hash keys. In order to recall sucessfully,
* The *SAME* float must be used.
* Alternative is o(n) access for floating point values as they would all go
* in the same bucket. May be the wisest... Switch to find in the possiblie_values list?
* #define MAXULPDIFF 7 // To define
* // Could be doing type puning...
* static inline int _cmp_float(ccs_float_t a, ccs_float_t b) {
* int64_t t1, t2, cmp;
* memcpy(&t1, &a, sizeof(int64));
* memcpy(&t2, &b, sizeof(int64));
* if (a == b)
* return 0;
* if ((t1 < 0) != (t2 < 0)) {
* if (t1 < 0)
* return -1;
* if (t2 < 0)
* return 1;
* }
* cmp = labs(t1-t2);
* if (cmp <= MAXULPDIFF)
* return 0;
* else if (a < b)
* return -1;
* else
* return 1;
* }
*/
static inline int _datum_cmp(ccs_datum_t *a, ccs_datum_t *b) {
if (a->type < b->type) {
return -1;
} else if (a->type > b->type) {
return 1;
} else {
switch(a->type) {
case CCS_STRING:
if (a->value.s == b->value.s)
return 0;
else if (!a->value.s)
return -1;
else if (!b->value.s)
return 1;
else
return strcmp(a->value.s, b->value.s);
case CCS_NONE:
return 0;
break;
default:
return memcmp(&(a->value), &(b->value), sizeof(ccs_value_t));
}
}
}
static inline unsigned _hash_datum(ccs_datum_t *d) {
unsigned h;
switch(d->type) {
case CCS_STRING:
if (d->value.s)
HASH_JEN(d->value.s, strlen(d->value.s), h);
else
HASH_JEN(d, sizeof(ccs_datum_t), h);
break;
case CCS_NONE:
HASH_JEN(&(d->type), sizeof(d->type), h);
break;
default:
HASH_JEN(d, sizeof(ccs_datum_t), h);
}
return h;
}
struct _ccs_hash_datum_s {
ccs_datum_t d;
UT_hash_handle hh;
};
typedef struct _ccs_hash_datum_s _ccs_hash_datum_t;
#endif //_DATUMHASH_H
......@@ -62,6 +62,31 @@ ccs_hyperparameter_get_default_distribution(ccs_hyperparameter_t hyperparameter
return ops->get_default_distribution( hyperparameter->data, distribution);
}
ccs_error_t
ccs_hyperparameter_check_value(ccs_hyperparameter_t hyperparameter,
ccs_datum_t value,
ccs_bool_t *result_ret) {
if (!hyperparameter || !hyperparameter->data)
return -CCS_INVALID_OBJECT;
if (!result_ret)
return -CCS_INVALID_VALUE;
_ccs_hyperparameter_ops_t *ops = ccs_hyperparameter_get_ops(hyperparameter);
return ops->check_values(hyperparameter->data, 1, &value, result_ret);
}
ccs_error_t
ccs_hyperparameter_check_values(ccs_hyperparameter_t hyperparameter,
size_t num_values,
const ccs_datum_t *values,
ccs_bool_t *results) {
if (!hyperparameter || !hyperparameter->data)
return -CCS_INVALID_OBJECT;
if (num_values && (!values || !results ))
return -CCS_INVALID_VALUE;
_ccs_hyperparameter_ops_t *ops = ccs_hyperparameter_get_ops(hyperparameter);
return ops->check_values(hyperparameter->data, num_values, values, results);
}
ccs_error_t
ccs_hyperparameter_sample(ccs_hyperparameter_t hyperparameter,
ccs_distribution_t distribution,
......
#include "cconfigspace_internal.h"
#include "hyperparameter_internal.h"
#include "datum_hash.h"
#include <string.h>
struct _ccs_hyperparameter_categorical_data_s {
_ccs_hyperparameter_common_data_t common_data;
ccs_int_t num_possible_values;
ccs_datum_t *possible_values;
_ccs_hash_datum_t *possible_values;
_ccs_hash_datum_t *hash;
};
typedef struct _ccs_hyperparameter_categorical_data_s _ccs_hyperparameter_categorical_data_t;
static ccs_error_t
_ccs_hyperparameter_categorical_del(ccs_object_t o) {
(void)o;
ccs_hyperparameter_t d = (ccs_hyperparameter_t)o;
_ccs_hyperparameter_categorical_data_t *data = (_ccs_hyperparameter_categorical_data_t *)(d->data);
HASH_CLEAR(hh, data->hash);
return CCS_SUCCESS;
}
static ccs_error_t
_ccs_hyperparameter_categorical_check_values(_ccs_hyperparameter_data_t *data,
size_t num_values,
const ccs_datum_t *values,
ccs_bool_t *results) {
_ccs_hyperparameter_categorical_data_t *d =
(_ccs_hyperparameter_categorical_data_t *)data;
for (size_t i = 0; i < num_values; i++) {
_ccs_hash_datum_t *p;
HASH_FIND(hh, d->hash, values + i, sizeof(ccs_datum_t), p);
results[i] = p ? CCS_TRUE : CCS_FALSE;
}
return CCS_SUCCESS;
}
......@@ -24,36 +43,37 @@ _ccs_hyperparameter_categorical_samples(_ccs_hyperparameter_data_t *data,
_ccs_hyperparameter_categorical_data_t *d =
(_ccs_hyperparameter_categorical_data_t *)data;
ccs_error_t err;
ccs_numeric_t *vs = (ccs_numeric_t *)values + num_values;
ccs_int_t *vs = (ccs_int_t *)values + num_values;
ccs_bool_t oversampling;
err = ccs_distribution_check_oversampling(distribution,
&(d->common_data.interval),
&oversampling);
if (err)
return err;
err = ccs_distribution_samples(distribution, rng, num_values, vs);
err = ccs_distribution_samples(distribution, rng,
num_values, (ccs_numeric_t *)vs);
if (err)
return err;
if (!oversampling) {
for(size_t i = 0; i < num_values; i++)
values[i] = d->possible_values[vs[i].i];
values[i] = d->possible_values[vs[i]].d;
} else {
size_t found = 0;
for(size_t i = 0; i < num_values; i++)
if (vs[i].i >= 0 && vs[i].i < d->num_possible_values)
values[found++] = d->possible_values[vs[i].i];
if (vs[i] >= 0 && vs[i] < d->num_possible_values)
values[found++] = d->possible_values[vs[i]].d;
vs = NULL;
size_t coeff = 2;
while (found < num_values) {
size_t buff_sz = (num_values - found)*coeff;
vs = (ccs_numeric_t *)malloc(sizeof(ccs_numeric_t)*buff_sz);
vs = (ccs_int_t *)malloc(sizeof(ccs_int_t)*buff_sz);
if (!vs)
return -CCS_ENOMEM;
err = ccs_distribution_samples(distribution, rng,
buff_sz, vs);
buff_sz, (ccs_numeric_t *)vs);
for(size_t i = 0; i < buff_sz && found < num_values; i++)
if (vs[i].i >= 0 && vs[i].i < d->num_possible_values)
values[found++] = d->possible_values[vs[i].i];
if (vs[i] >= 0 && vs[i] < d->num_possible_values)
values[found++] = d->possible_values[vs[i]].d;
coeff <<= 1;
free(vs);
if (coeff > 32)
......@@ -77,10 +97,18 @@ _ccs_hyperparameter_categorical_get_default_distribution(
static _ccs_hyperparameter_ops_t _ccs_hyperparameter_categorical_ops = {
{ &_ccs_hyperparameter_categorical_del },
&_ccs_hyperparameter_categorical_check_values,
&_ccs_hyperparameter_categorical_samples,
&_ccs_hyperparameter_categorical_get_default_distribution
};
#undef uthash_nonfatal_oom
#define uthash_nonfatal_oom(elt) { \
HASH_CLEAR(hh, hyperparam_data->hash); \
free((void*)mem); \
return -CCS_ENOMEM; \
}
ccs_error_t
ccs_create_categorical_hyperparameter(const char *name,
size_t num_possible_values,
......@@ -96,11 +124,20 @@ ccs_create_categorical_hyperparameter(const char *name,
return -CCS_INVALID_VALUE;
if (!possible_values)