Preparing implementation & python API for IFS mutation

This commit is contained in:
Yann Weber 2024-01-04 19:23:07 +01:00
commit 30c6c92493
20 changed files with 1865 additions and 307 deletions

View file

@ -68,6 +68,7 @@ static PyStructSequence_Field token_types_fields[] = {
{ .name = "var",
.doc = "Variable (argument)",
},
{ NULL, NULL },
};
PyStructSequence_Desc rpn_token_types_desc = {
.name = "RPNTokenTypes",
@ -77,51 +78,6 @@ PyStructSequence_Desc rpn_token_types_desc = {
};
PyTypeObject rpn_token_types_SeqDesc;
/**@brief @ref pymod_pyrpn_RPNMutationParams field definition
* @ingroup pymod_pyrpn_RPNMutationParams */
static PyStructSequence_Field mutation_params_fields[] = {
{ .name = "minimum_length",
.doc = "Expression minimum length",
},
{ .name = "weight_add",
.doc = "The weight for 'add token' mutation",
},
{ .name = "weight_del",
.doc = "The weight for 'delete token' mutation",
},
{ .name = "weight_mut",
.doc = "The weight for 'mutate token' mutation",
},
{ .name = "weight_mut_soft",
.doc = "The weight for 'mutate soft (value not type) token' mutation",
},
{
.name = "weight_add_elt",
.doc = "The weights for each token types when adding a token",
},
{
.name = "weight_del_elt",
.doc= "The weights for each token types when deleting a token",
}
};
PyStructSequence_Desc rpn_mutation_params_desc = {
.name = "RPNMutationParams",
.doc = "Named tuple for mutation parameters",
.n_in_sequence = (sizeof(mutation_params_fields) / sizeof(*mutation_params_fields)),
.fields = mutation_params_fields,
};
PyTypeObject rpn_mutation_params_SeqDesc;
rpn_mutation_params_t rpn_mutation_params_default = {
.min_len = 3,
.w_add = 1.25,
.w_del = 1.0,
.w_mut = 2.0,
.w_mut_soft = 4.0,
.w_add_elt = {1.0,1.0,1.0},
.w_mut_elt = {1.0,1.0,1.0},
};
/**@brief Adds a constant to @ref pymod_pyrpn_const module
* @param mod The @ref pymod_pyrpn_const module
@ -150,8 +106,6 @@ PyObject *rpnconst_init(void)
mod = PyModule_Create(&rpnconstmodule);
if(mod == NULL) { return NULL; }
rpn_mutation_init_params(&rpn_mutation_params_default);
if(Py_rpnconst_add(mod, "POS_LINEAR", RPN_IF_POSITION_LINEAR) ||
Py_rpnconst_add(mod, "POS_XY", RPN_IF_POSITION_XY) ||
Py_rpnconst_add(mod, "POS_XDIM", RPN_IF_POSITION_XDIM) ||
@ -169,4 +123,46 @@ PyObject *rpnconst_init(void)
return mod;
}
PyObject *pyrpn_PyErr_ReraiseFormat(PyObject *exception, const char *fmt, ...)
{
va_list vargs;
PyObject *exc, *val, *val2, *tb;
va_start(vargs, fmt);
// we ay trigger a warning when in debug mode ?
#ifdef NDEBUG
assert(PyErr_Occurred());
#else
if(!PyErr_Occurred())
{
PyErr_WarnEx(PyExc_SyntaxWarning,
"Warning, reraising but PyErr_Occurred() not set", 1);
PyErr_FormatV(exception, fmt, vargs);
va_end(vargs);
return NULL;
}
#endif
PyErr_Fetch(&exc, &val, &tb);
PyErr_NormalizeException(&exc, &val, &tb);
if(tb != NULL)
{
PyException_SetTraceback(val, tb);
Py_DECREF(tb);
}
Py_DECREF(exc);
assert(!PyErr_Occurred());
PyErr_FormatV(exception, fmt, vargs);
PyErr_Fetch(&exc, &val2, &tb);
PyErr_NormalizeException(&exc, &val2, &tb);
Py_INCREF(val);
PyException_SetCause(val2, val);
PyException_SetContext(val2, val);
PyErr_Restore(exc, val2, tb);
va_end(vargs);
return NULL;
}

View file

@ -22,12 +22,15 @@
#include "config.h"
#include <errno.h>
#include <stdarg.h>
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "structmember.h"
#include "rpn_mutate.h"
#include "rpn_if_mutate.h"
#include "rpn_ifs_mutate.h"
#include "rpn_if_default.h"
/**@file python_const.h
@ -66,20 +69,11 @@ extern PyTypeObject rpn_token_types_SeqDesc;
/**@brief @ref rpn_token_types_SeqDesc named tuple description */
extern PyStructSequence_Desc rpn_token_types_desc;
/**@defgroup pymod_pyrpn_RPNMutationParams pyrpn.RPNMutationParams
* @brief namedtuple storing mutation parameters
* @ingroup pymod_pyrpn */
/**@brief RPNMutationParams named tuple with mutation parameters */
extern PyTypeObject rpn_mutation_params_SeqDesc;
/**@brief @ref rpn_mutation_params_SeqDesc named tuple description */
extern PyStructSequence_Desc rpn_mutation_params_desc;
/**@brief Default values for mutations parameters */
extern rpn_mutation_params_t rpn_mutation_params_default;
/**@brief pyrpn.const module initialisation function
* @return The initialized module
* @ingroup pymod_pyrpn */
PyObject *rpnconst_init(void);
PyObject *pyrpn_PyErr_ReraiseFormat(PyObject *exception, const char *fmt, ...);
#endif

View file

@ -529,7 +529,14 @@ PyObject* rpnif_mutate(PyObject *self, PyObject *args, PyObject *kwargs)
PyObject *py_params = NULL, *py_weights = NULL;
unsigned int n_mutations = 1;
rpn_mutation_params_t params;
rnd_t *weights = malloc(sizeof(rnd_t)*rpn_sz);
rnd_t *weights;
if(!if_mutation_alloc_weights(expr_self->rif, &weights))
{
PyErr_Format(PyExc_MemoryError,
"Unable to allocate weights : %s\n",
strerror(errno));
return NULL;
}
if(!PyArg_ParseTupleAndKeywords(args, kwargs, str_args, names,
&n_mutations, &py_params, &py_weights))
@ -565,36 +572,36 @@ PyObject* rpnif_mutate(PyObject *self, PyObject *args, PyObject *kwargs)
goto err;
}
long double total = 0;
float *fweights;
if(!if_mutation_alloc_weights(expr_self->rif, &fweights))
{
PyErr_Format(PyExc_MemoryError,
"Unable to allocate memory : %s",
strerror(errno));
goto err;
}
for(size_t i=0; i<rpn_sz; i++)
{
PyObject *elt = PySequence_Fast_GET_ITEM(seq, i);
if(!PyNumber_Check(elt))
{
Py_DECREF(seq);
free(fweights);
PyErr_SetString(PyExc_TypeError,
"weights must contains float values");
goto err;
}
total += PyFloat_AsDouble(elt);
fweights[i] = (float)PyFloat_AsDouble(elt);
if(PyErr_Occurred())
{
Py_DECREF(seq);
free(fweights);
goto err;
}
}
for(size_t i=0; i<rpn_sz; i++)
{
if(total == 0)
{
weights[i] = (rnd_t)((rnd_t_max / rpn_sz)*(i+1));
continue;
}
PyObject *py_elt = PySequence_Fast_GET_ITEM(seq, i);
long double elt = PyFloat_AsDouble(py_elt);
weights[i] = (elt*rnd_t_max)/total;
weights[i] += i>0? weights[i-1] : 0;
}
rpnifs_fast_rnd_weights(rpn_sz, fweights, weights);
free(fweights);
Py_DECREF(seq);
}
@ -606,7 +613,7 @@ PyObject* rpnif_mutate(PyObject *self, PyObject *args, PyObject *kwargs)
}
else
{
if(rpnexpr_pyobj_to_mutation_params(py_params, &params) < 0)
if(pyrpn_pyobj_to_mutation_params(py_params, &params) < 0)
{
if(!PyErr_Occurred())
{
@ -618,25 +625,13 @@ PyObject* rpnif_mutate(PyObject *self, PyObject *args, PyObject *kwargs)
}
}
for(size_t i=0; i<n_mutations; i++)
if(if_mutation(expr_self->rif, weights, n_mutations, &params) < 0)
{
size_t rpn_num;
if(_rpn_random_choice(rpn_sz, weights, &rpn_num) < 0)
{
PyErr_Format(PyExc_RuntimeError,
"Unable to choose an expression \
randomly : %s", strerror(errno));
goto err;
}
if(rpn_mutation(&(expr_self->rif->rpn[rpn_num].toks),
&params) < 0)
{
PyErr_Format(PyExc_RuntimeError,
"Mutation failed : %s",
strerror(errno));
goto err;
}
rpn_expr_tokens_updated(&(expr_self->rif->rpn[rpn_num]));
free(weights);
PyErr_Format(PyExc_RuntimeError,
"Error during mutations : %s",
strerror(errno));
goto err;
}
free(weights);

View file

@ -29,6 +29,8 @@
#include "rpn_if.h"
#include "rpn_if_default.h"
#include "rpn_if_mutate.h"
#include "python_mutation.h"
#include "python_rpnexpr.h"
#include "python_const.h"

View file

@ -44,10 +44,25 @@ in the system."),
METH_O,
"self, position, /",
"Return a coordinates tuple from given position."),
PYRPN_method("set_mutation_params", rpnifs_set_mutation_params,
METH_VARARGS | METH_KEYWORDS,
"self, params=None, /, weight_type=None, \
mutation_weight_range=None, if_weights=None, if_component_weights=None, \
if_mut_params=None",
"Set mutation parameters. \n\
The first argument 'params' is for giving an IFSMutationParams \
(self.ifs_mutation_weights) instance.\n\
Keywords arguments corresponds to IFSMutationParams writable fields \
(see help(pyrpn.IFSMutationParams)).\n\n\
Note : if params and keywords arguments are given, params is used and \
then overwrited by given keywords arguments."),
{NULL} // Sentinel
};
static PyMemberDef RPNIFS_members[] = {
{"mutation_params", T_OBJECT,
offsetof(PyRPNIFS_t, py_ifs_mut_weights), READONLY,
"The mutation weights & parameters for mutating the IFS"},
{"expressions", T_OBJECT, offsetof(PyRPNIFS_t, rpn_if), READONLY,
"The tuple with RPNIterExpr instances"},
{"mmap", T_OBJECT, offsetof(PyRPNIFS_t, mmap), READONLY,
@ -104,6 +119,52 @@ PyObject *rpnifs_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
return ret;
}
/**@brief Initialize internal fields */
static int _rpnifs_init_weights(PyObject *self)
{
PyRPNIFS_t *ifs_self = (PyRPNIFS_t*)self;
ifs_mutation_weights_t *imw = &ifs_self->ifs_mut_weights;
if(ifs_mutation_weights_alloc(imw, ifs_self->ifs, 0, 0) < 0)
{
PyErr_Format(PyExc_RuntimeError,
"Error allocating ifs mutation weights : %s",
strerror(errno));
return -1;
}
imw->w_mut_type[0] = imw->w_mut_type[1] = 0.5;
imw->w_weight_range[0] = 0.01;
imw->w_weight_range[1] = 0.1;
for(size_t i=0; i < ifs_self->ifs->if_sz; i++)
{
imw->w_mut_if[i] = 1.0/ifs_self->ifs->if_sz;
}
for(size_t i=0; i < imw->w_comp_if_sz; i++)
{
imw->w_comp_if[i] = 1.0/imw->w_comp_if_sz;
}
memcpy(imw->if_mut_params, &rpn_mutation_params_default,
sizeof(rpn_mutation_params_default));
if(ifs_mutation_weights_update(imw) < 0)
{
PyErr_Format(PyExc_RuntimeError,
"Error while updating mutation weights : '%s'\n",
strerror(errno));
return -1;
}
if(pyrpn_ifs_mut_weight_to_pyobj(imw,
&ifs_self->py_ifs_mut_weights) < 0)
{
return -1;
}
return 0;
}
int rpnifs_init(PyObject *self, PyObject *args, PyObject *kwds)
{
@ -191,6 +252,11 @@ length %ld provided",
{
return -1;
}
if(_rpnifs_init_weights(self) < 0)
{
return -1;
}
return 0;
}
@ -230,13 +296,11 @@ PyObject *rpnifs_from_pos(PyObject *self, PyObject* _pos)
}
PyObject *rpnifs_weights(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
{
PyRPNIFS_t *ifs_self = (PyRPNIFS_t*)self;
PyObject *iter;
unsigned int *weights;
if(nargs == 1 && args[0] != Py_None)
{
// Try to update weights
@ -278,6 +342,9 @@ PyObject *rpnifs_weights(PyObject *self, PyObject *const *args, Py_ssize_t nargs
Py_DECREF(item);
if(PyErr_Occurred())
{
pyrpn_PyErr_ReraiseFormat(PyExc_ValueError,
"\
Weights sequence element #%ld invalid", i);
goto err_weights;
}
weights[i] = w;
@ -287,7 +354,7 @@ PyObject *rpnifs_weights(PyObject *self, PyObject *const *args, Py_ssize_t nargs
free(weights);
}
// return weights in a new tuple
// construct returned weights in a new tuple
PyObject *ret = PyTuple_New(ifs_self->ifs->if_sz);
if(PyErr_Occurred())
{
@ -305,6 +372,20 @@ PyObject *rpnifs_weights(PyObject *self, PyObject *const *args, Py_ssize_t nargs
{
return NULL;
}
if(ifs_mutation_weights_if_count_update(&ifs_self->ifs_mut_weights,
ifs_self->ifs->if_sz) < 0)
{
PyErr_Format(PyExc_RuntimeError,
"Error reallocating mutation weights : %s",
strerror(errno));
return NULL;
}
Py_DECREF(ifs_self->py_ifs_mut_weights);
if(pyrpn_ifs_mut_weight_to_pyobj(&ifs_self->ifs_mut_weights,
&ifs_self->py_ifs_mut_weights) < 0)
{
return NULL;
}
return ret;
err_weights:
@ -393,6 +474,122 @@ err:
return NULL;
}
PyObject *rpnifs_set_mutation_params(PyObject *self, PyObject *args,
PyObject *kwds)
{
PyRPNIFS_t *ifs_self = (PyRPNIFS_t*)self;
char *names[] = {"params", "weight_type", "mutation_weight_range",
"if_weights", "if_component_weights", "if_mut_params", NULL};
PyObject *params = NULL;
PyObject *mut_type_weight = NULL, \
*weight_range = NULL, \
*mut_if_weight = NULL, \
*if_comp_weight = NULL, \
*mutation_params = NULL;
if(!PyArg_ParseTupleAndKeywords(args, kwds,
"|OOOOOO:RPNIFS.set_mutation_weight",
names,
&params, &mut_type_weight,
&weight_range, &mut_if_weight, &if_comp_weight,
&mutation_params))
{
return NULL;
}
if(params && params != Py_None)
{
ifs_mutation_weights_t tmp;
if(pyrpn_pyobj_to_ifs_mutation_weights(params, &tmp) < 0)
{
pyrpn_PyErr_ReraiseFormat(PyExc_ValueError,
"params argument invalid");
return NULL;
}
if(tmp.if_count != ifs_self->ifs->if_sz)
{
PyErr_Format(PyExc_ValueError,
"Expected params for %ld IF but given \
params is for %ld IF.", ifs_self->ifs->if_sz, tmp.if_count);
}
ifs_mutation_weights_dealloc(&ifs_self->ifs_mut_weights);
memcpy(&ifs_self->ifs_mut_weights, &tmp, sizeof(tmp));
}
// handling kwds, overwriting optional params argument value
if(mut_type_weight && mut_type_weight != Py_None)
{
if(pyrpn_ifs_mut_params_mut_type_weight(
&ifs_self->ifs_mut_weights,
mut_type_weight) < 0)
{
pyrpn_PyErr_ReraiseFormat(PyExc_ValueError,
"Error with weight_type argument");
return NULL; // TODO check if nothing to clean
}
}
if(weight_range && weight_range != Py_None)
{
if(pyrpn_ifs_mut_params_weight_range(
&ifs_self->ifs_mut_weights,
weight_range) < 0)
{
pyrpn_PyErr_ReraiseFormat(PyExc_ValueError,
"Error with mutation_weight_range argument");
return NULL; // TODO check if nothing to clean
}
}
if(mut_if_weight && mut_if_weight != Py_None)
{
if(pyrpn_ifs_mut_params_weight_mut_if(&ifs_self->ifs_mut_weights,
mut_if_weight, 0) < 0)
{
pyrpn_PyErr_ReraiseFormat(PyExc_ValueError,
"Error with if_weights parameters");
return NULL; // TODO howto clean ?
}
}
if(if_comp_weight && if_comp_weight != Py_None)
{
if(pyrpn_ifs_mut_params_if_comp_weight(&ifs_self->ifs_mut_weights,
if_comp_weight) < 0)
{
pyrpn_PyErr_ReraiseFormat(PyExc_ValueError,
"Error with if_comp_weight parameter");
return NULL; // TODO hwoto clean ?
}
}
if(mutation_params && mutation_params != Py_None)
{
if(pyrpn_ifs_mut_params_mutation_params(&ifs_self->ifs_mut_weights,
mutation_params) < 0)
{
pyrpn_PyErr_ReraiseFormat(PyExc_ValueError,
"Error with mutation_params argument");
return NULL; // TODO howto clean ?
}
}
// Construct the python repr of the params
Py_DECREF(ifs_self->py_ifs_mut_weights);
ifs_self->py_ifs_mut_weights = NULL;
if(pyrpn_ifs_mut_weight_to_pyobj(&ifs_self->ifs_mut_weights,
&ifs_self->py_ifs_mut_weights) < 0)
{
return NULL;
}
Py_RETURN_NONE;
}
PyObject *rpnifs_str(PyObject *self)
{
PyErr_SetString(PyExc_NotImplementedError, "TODO");

View file

@ -63,6 +63,11 @@ typedef struct
rpn_if_t **rifs;
PyObject *py_ifs_mut_weights;
ifs_mutation_weights_t ifs_mut_weights;
/**@brief Python mmap.mmap instance representing rif memory map
* @note NULL if unpickled instance */
PyObject *mmap;
@ -82,13 +87,17 @@ int rpnifs_init(PyObject *self, PyObject *args, PyObject *kwds);
void rpnifs_del(PyObject *self);
PyObject *rpnifs_to_pos(PyObject *self, PyObject** argv, Py_ssize_t argc);
PyObject *rpnifs_from_pos(PyObject *self, PyObject* _pos);
PyObject *rpnifs_weights(PyObject *self, PyObject *const *args, Py_ssize_t nargs);
PyObject *rpnifs_position(PyObject *self, PyObject *const *args, Py_ssize_t nargs);
PyObject *rpnifs_run(PyObject *self, PyObject *const *args, Py_ssize_t nargs);
PyObject *rpnifs_set_mutation_params(PyObject *self, PyObject *args,
PyObject *kwargs);
PyObject *rpnifs_to_pos(PyObject *self, PyObject** argv, Py_ssize_t argc);
PyObject *rpnifs_from_pos(PyObject *self, PyObject* _pos);
PyObject *rpnifs_str(PyObject *self);
PyObject *rpnifs_repr(PyObject *self);

702
python_mutation.c Normal file
View file

@ -0,0 +1,702 @@
#include "python_mutation.h"
/**@brief @ref pymod_pyrpn_RPNMutationParams field definition
* @ingroup pymod_pyrpn_RPNMutationParams */
static PyStructSequence_Field mutation_params_fields[] = {
{ .name = "minimum_length",
.doc = "Expression minimum length",
},
{ .name = "weight_add",
.doc = "The weight for 'add token' mutation",
},
{ .name = "weight_del",
.doc = "The weight for 'delete token' mutation",
},
{ .name = "weight_mut",
.doc = "The weight for 'mutate token' mutation",
},
{ .name = "weight_mut_soft",
.doc = "The weight for 'mutate soft (value not type) token' mutation",
},
{
.name = "weight_add_elt",
.doc = "The weights for each token types when adding a token",
},
{
.name = "weight_del_elt",
.doc= "The weights for each token types when deleting a token",
},
{ NULL, NULL},
};
PyStructSequence_Desc rpn_mutation_params_desc = {
.name = "RPNMutationParams",
.doc = "Named tuple for mutation parameters",
.n_in_sequence = (sizeof(mutation_params_fields) / \
sizeof(*mutation_params_fields)) - 1,
.fields = mutation_params_fields,
};
PyTypeObject rpn_mutation_params_SeqDesc;
static PyStructSequence_Field ifs_mutation_weights_fields[] = {
{ .name = "weight_type",
.doc = "Two element defining weight for random choice of \
mutation type : (Weight_Mutation_weight, IF_Mutation_weight).",
},
{ .name = "mutation_weight_range",
.doc = "Min & max values for weight modifications. A random \
float will be choosen in [min..max] and will be randomly \
added/substracted to a randomly choosen weight",
},
{ .name = "if_count",
.doc = "Stores the number of IF in the system to mutate",
},
{ .name = "if_weights",
.doc = "Indicate weights (chance) for each IF to be mutated",
},
{ .name = "if_component_weights",
.doc = "Can be an array of weight, indicating the chance \
for an IF component to be mutated. Else can be an array of array of weight, \
allowing custom component weights for each expression.",
},
{ .name = "if_mut_params",
.doc = "Stores 1 global mutation params or a list of mutation \
params named tuple (one per IF)",
},
{ NULL, NULL},
};
PyStructSequence_Desc ifs_mutation_weights_desc = {
.name = "IFSMutationParams",
.doc = "Named tuple for mutation parameters",
.n_in_sequence = (sizeof(ifs_mutation_weights_fields) / \
sizeof(*ifs_mutation_weights_fields)) - 1,
.fields = ifs_mutation_weights_fields,
};
PyTypeObject ifs_mutation_weights_SeqDesc;
int pyrpn_python_mutation_init(PyObject *pyrpn_mod)
{
rpn_mutation_init_params(&rpn_mutation_params_default);
// Named tuple for mutation parameters
PyStructSequence_InitType(&rpn_mutation_params_SeqDesc,
&rpn_mutation_params_desc);
if(PyErr_Occurred()) { return -1; }
Py_INCREF(&rpn_mutation_params_SeqDesc);
if(PyModule_AddObject(pyrpn_mod, "RPNMutationParamsTuple",
(PyObject*)&rpn_mutation_params_SeqDesc) < 0)
{
goto err;
}
// Named tuple for ifs mutation parameters
PyStructSequence_InitType(&ifs_mutation_weights_SeqDesc,
&ifs_mutation_weights_desc);
if(PyErr_Occurred()) {
dprintf(2, "FAILED SEQUENCE INITTYPE %d '%s'\n",
ifs_mutation_weights_desc.n_in_sequence,
ifs_mutation_weights_desc.fields[5].doc);
goto err;
}
Py_INCREF(&ifs_mutation_weights_SeqDesc);
if(PyModule_AddObject(pyrpn_mod, "IFSMutationParams",
(PyObject*)&ifs_mutation_weights_SeqDesc) < 0)
{
goto err2;
}
return 0;
err2:
Py_DECREF(&ifs_mutation_weights_SeqDesc);
err:
Py_DECREF(&rpn_mutation_params_SeqDesc);
return -1;
}
/**@brief Parse given object to float
* @param obj The python object to parse
* @param res Points on result
* @note Raise python exception on error
*/
static inline void _parse_float(PyObject *obj, float *res)
{
double tmp = PyFloat_AsDouble(obj);
if(tmp > FLT_MAX)
{
PyErr_SetString(PyExc_OverflowError, "Float overflows");
return;
}
*res=tmp;
return;
}
/** Parse weights python parameter
* @param obj The python object
* @param w Points on the 3 weights to parse
* @note Raise python exception on error
*/
static inline void _parse_type_weights(PyObject *obj, float w[3])
{
PyObject *fast = PySequence_Fast(obj, "Not a RPNTokenTypeTuple nor with a length of 3");
if(!fast) { return; }
if(PySequence_Length(obj) != 3)
{
PyErr_Format(PyExc_ValueError, "Excpected RPNTokenTypeTuple or 3 elements but got %d elements", PySequence_Length(obj));
return;
}
PyObject **elts = PySequence_Fast_ITEMS(fast);
const char* names[3] = {"op", "const", "var"};
for(size_t i=0; i<3; i++)
{
_parse_float(elts[i], &w[i]);
if(PyErr_Occurred())
{
PyErr_Format(PyExc_ValueError,
"Bad value for .%s field",
names[i]);
break;
}
}
return;
}
static inline void _parse_floats(PyObject *obj, float *w, size_t len)
{
PyObject *fast;
fast = PySequence_Fast(obj, "A sequence is expected");
if(!fast || PyErr_Occurred()) { return; }
Py_INCREF(fast);
if((size_t)PySequence_Length(obj) != len)
{
PyErr_Format(PyExc_ValueError,
"Expected %ld element but %ld given",
len, PySequence_Length(fast));
goto ret;
}
PyObject **elt = PySequence_Fast_ITEMS(fast);
for(size_t i=0; i<len; i++)
{
_parse_float(elt[i], &w[i]);
if(PyErr_Occurred())
{
break;
}
}
ret:
Py_DECREF(fast);
return;
}
int pyrpn_pyobj_to_mutation_params(PyObject* py_params, rpn_mutation_params_t *params)
{
if(!PySequence_Check(py_params))
{
PyErr_SetString(PyExc_TypeError, "The given object is not a a sequence");
return -1;
}
if(PySequence_Size(py_params) != 7)
{
PyErr_SetString(PyExc_ValueError, "The given object is not a RPNMutationParamsTuple nor of length 7");
return -1;
}
PyObject *fast = PySequence_Fast(py_params, "The given object is not a RPNMutationParamsTuple nor a sequence ?");
if(PyErr_Occurred()) { return -1; }
Py_INCREF(fast);
PyObject **elts = PySequence_Fast_ITEMS(fast);
if(!elts || PyErr_Occurred())
{
return -1;
}
params->min_len = PyLong_AsSize_t(elts[0]);
if(PyErr_Occurred())
{
PyErr_SetString(PyExc_ValueError, "Bad type for .minimum_length field");
return -1;
}
_parse_float(elts[1], &params->w_add);
if(PyErr_Occurred())
{
PyErr_SetString(PyExc_ValueError, "Bad value for .weight_add field");
return -1;
}
_parse_float(elts[2], &params->w_del);
if(PyErr_Occurred())
{
PyErr_SetString(PyExc_ValueError, "Bad value for .weight_del field");
return -1;
}
_parse_float(elts[3], &params->w_mut);
if(PyErr_Occurred())
{
PyErr_SetString(PyExc_ValueError, "Bad value for .weight_mut field");
return -1;
}
_parse_float(elts[4], &params->w_mut_soft);
if(PyErr_Occurred())
{
PyErr_SetString(PyExc_ValueError, "Bad value for .weight_mut_soft field");
return -1;
}
_parse_type_weights(elts[5], params->w_add_elt);
if(PyErr_Occurred())
{
PyErr_SetString(PyExc_ValueError, "Bad value for .weight_add_elt field");
return -1;
}
_parse_type_weights(elts[6], params->w_mut_elt);
if(PyErr_Occurred())
{
PyErr_SetString(PyExc_ValueError, "Bad value for .weight_add_elt field");
return -1;
}
rpn_mutation_init_params(params);
return 0;
}
int pyrpn_pyobj_to_ifs_mutation_weights(PyObject *py_params,
ifs_mutation_weights_t *w)
{
bzero(w, sizeof(*w));
if(!PySequence_Check(py_params))
{
PyErr_SetString(PyExc_TypeError,
"The given object is not a sequence");
return -1;
}
if(PySequence_Size(py_params) != 6)
{
PyErr_Format(PyExc_ValueError,
"Expect a sequence of 5 elements but given sequence \
contains %ld elements", PySequence_Size(py_params));
return -1;
}
PyObject *fast = PySequence_Fast(py_params,
"Unable to use as IFSMutationWeightsTuple");
if(PyErr_Occurred()) { return -1; }
Py_INCREF(fast);
PyObject **elts = PySequence_Fast_ITEMS(fast);
if(pyrpn_ifs_mut_params_mut_type_weight(w, elts[0]) < 0)
{
pyrpn_PyErr_ReraiseFormat(PyExc_ValueError,
"Error with weight_type field #0");
return -1;
}
if(pyrpn_ifs_mut_params_weight_range(w, elts[1]) < 0)
{
pyrpn_PyErr_ReraiseFormat(PyExc_ValueError,
"Error with weight_type field #1");
return -1;
}
w->if_count = PyLong_AsSize_t(elts[2]);
if(PyErr_Occurred())
{
pyrpn_PyErr_ReraiseFormat(PyExc_ValueError,
"Error with IF count field #2");
return -1;
}
if(pyrpn_ifs_mut_params_weight_mut_if(w, elts[3], 1) < 0)
{
pyrpn_PyErr_ReraiseFormat(PyExc_ValueError,
"Error with IFS IF mutation weights field #3");
return -1;
}
if(pyrpn_ifs_mut_params_if_comp_weight(w, elts[4]) < 0)
{
pyrpn_PyErr_ReraiseFormat(PyExc_ValueError,
"Error with IFS IF component mutation \
weight field #4");
goto err_free_w_mut_if;
}
if(pyrpn_ifs_mut_params_mutation_params(w, elts[5]) < 0)
{
pyrpn_PyErr_ReraiseFormat(PyExc_ValueError,
"Error with IFS RPN mutation params field #5");
goto err_free_comp_weight;
}
return 0;
err_free_comp_weight:
free(w->w_comp_if);
w->w_comp_if = NULL;
w->w_comp_if_sz = 0;
err_free_w_mut_if:
free(w->w_mut_if);
w->w_mut_if = NULL;
w->if_count = 0;
return -1;
}
int pyrpn_mutation_params_to_pyobj(const rpn_mutation_params_t *params,
PyObject **res)
{
PyObject *val;
Py_ssize_t pos;
*res = PyStructSequence_New(&rpn_mutation_params_SeqDesc);
if(PyErr_Occurred())
{
goto err;
}
Py_INCREF(*res);
pos = 0;
val = PyLong_FromSize_t(params->min_len);
if(PyErr_Occurred()) { goto err_ref; }
Py_INCREF(val);
PyStructSequence_SetItem(*res, pos, val);
pos++;
val = PyFloat_FromDouble(params->w_add);
if(PyErr_Occurred()) { goto err_ref; }
Py_INCREF(val);
PyStructSequence_SetItem(*res, pos, val);
pos++;
val = PyFloat_FromDouble(params->w_del);
if(PyErr_Occurred()) { goto err_ref; }
Py_INCREF(val);
PyStructSequence_SetItem(*res, pos, val);
pos++;
val = PyFloat_FromDouble(params->w_mut);
if(PyErr_Occurred()) { goto err_ref; }
Py_INCREF(val);
PyStructSequence_SetItem(*res, pos, val);
pos++;
val = PyFloat_FromDouble(params->w_mut_soft);
if(PyErr_Occurred()) { goto err_ref; }
Py_INCREF(val);
PyStructSequence_SetItem(*res, pos, val);
pos++;
val = Py_BuildValue("(f,f,f)",
params->w_add_elt[0],
params->w_add_elt[1],
params->w_add_elt[2]);
if(PyErr_Occurred()) { goto err_ref; }
Py_INCREF(val);
PyStructSequence_SetItem(*res, pos, val);
pos++;
val = Py_BuildValue("(f,f,f)",
params->w_mut_elt[0],
params->w_mut_elt[1],
params->w_mut_elt[2]);
if(PyErr_Occurred()) { goto err_ref; }
Py_INCREF(val);
PyStructSequence_SetItem(*res, pos, val);
// add elt & del elt
return 0;
err_ref:
Py_DECREF(*res);
err:
*res = NULL;
return -1;
}
int pyrpn_ifs_mut_weight_to_pyobj(const ifs_mutation_weights_t *w,
PyObject **res)
{
PyObject *val;
Py_ssize_t pos;
*res = PyStructSequence_New(&ifs_mutation_weights_SeqDesc);
if(PyErr_Occurred())
{
goto err;
}
Py_INCREF(*res);
pos = 0;
val = Py_BuildValue("(f, f)", w->w_mut_type[0], w->w_mut_type[1]);
if(PyErr_Occurred()) { goto ref_err; }
Py_INCREF(val);
PyStructSequence_SetItem(*res, pos, val);
pos++;
val = Py_BuildValue("(f, f)", w->w_weight_range[0],
w->w_weight_range[1]);
if(PyErr_Occurred()) { goto ref_err; }
Py_INCREF(val);
PyStructSequence_SetItem(*res, pos, val);
pos++;
val = PyLong_FromSize_t(w->if_count);
if(PyErr_Occurred()) { goto ref_err; }
Py_INCREF(val);
PyStructSequence_SetItem(*res, pos, val);
pos++;
val = PyTuple_New(w->if_count);
if(PyErr_Occurred()) { goto ref_err; }
Py_INCREF(val);
for(size_t i=0; i<w->if_count; i++)
{
PyObject *elt = PyFloat_FromDouble(w->w_mut_if[i]);
if(PyErr_Occurred()) { goto err_val; }
Py_INCREF(elt);
PyTuple_SET_ITEM(val, i, elt);
}
PyStructSequence_SetItem(*res, pos, val);
pos++;
val = PyTuple_New(w->w_comp_if_sz);
if(PyErr_Occurred()) { goto ref_err; }
Py_INCREF(val);
for(size_t i=0; i < w->w_comp_if_sz; i++)
{
PyObject *elt = PyFloat_FromDouble(w->w_comp_if[i]);
if(PyErr_Occurred()) { goto err_val; }
Py_INCREF(elt);
PyTuple_SET_ITEM(val, i, elt);
}
PyStructSequence_SetItem(*res, pos, val);
pos++;
if(!w->custom_params)
{
if(pyrpn_mutation_params_to_pyobj(w->if_mut_params, &val) < 0)
{
goto ref_err;
}
}
else
{
val = PyTuple_New(w->if_count);
for(size_t i=0; i<w->if_count; i++)
{
PyObject *elt;
if(pyrpn_mutation_params_to_pyobj(&(w->if_mut_params[i]),
&elt) < 0)
{
goto err_val;
}
PyTuple_SET_ITEM(val, i, elt);
}
}
PyStructSequence_SetItem(*res, pos, val);
return 0;
err_val:
Py_DECREF(val);
ref_err:
Py_DECREF(*res);
err:
*res = NULL;
return -1;
}
int pyrpn_ifs_mut_params_mut_type_weight(ifs_mutation_weights_t *w,
PyObject *mut_type_weight)
{
_parse_floats(mut_type_weight, w->w_mut_type, 2);
return PyErr_Occurred() ? -1 : 0;
}
int pyrpn_ifs_mut_params_weight_range(ifs_mutation_weights_t *w,
PyObject *weight_range)
{
_parse_floats(weight_range, w->w_weight_range, 2);
return PyErr_Occurred() ? -1 : 0;
}
int pyrpn_ifs_mut_params_weight_mut_if(ifs_mutation_weights_t *w,
PyObject *mut_if_weight, short do_realloc)
{
const size_t len = PySequence_Length(mut_if_weight);
short alloc = 0;
if(PyErr_Occurred())
{
return -1;
}
if(len != w->if_count)
{
PyErr_Format(PyExc_ValueError,
"Trying to set IF mutation weight in a system \
with %ld expression with a sequence of %ld weights", w->if_count, len);
return -1;
}
if(!w->w_mut_if)
{
alloc = 1;
if(!(w->w_mut_if = malloc(sizeof(*w->w_mut_if) * len)))
{
PyErr_Format(PyExc_MemoryError,
"Unable to allocate IFS IF mutation \
weights for %ld expressions", len);
return -1;
}
}
else if(do_realloc)
{
alloc = 1;
void *tmp = realloc(w->w_mut_if, sizeof(*w->w_mut_if) * len);
if(!tmp)
{
PyErr_Format(PyExc_MemoryError,
"Unable to reallocate IFS IF mutation \
weights for %ld expressiosn", len);
return -1;
}
w->w_mut_if = tmp;
}
_parse_floats(mut_if_weight, w->w_mut_if, len);
if(PyErr_Occurred())
{
if(alloc)
{
// TODO not good when realloc
free(w->w_mut_if);
w->if_count = 0;
}
return -1;
}
return 0;
}
int pyrpn_ifs_mut_params_if_comp_weight(ifs_mutation_weights_t *w,
PyObject *if_comp_weight)
{
const size_t len = PySequence_Length(if_comp_weight);
if(PyErr_Occurred())
{
return -1;
}
if(!len)
{
PyErr_SetString(PyExc_ValueError,
"Invalid IFS IF component mutation weights :\
empty sequence");
return -1;
}
if_mutation_weight_t *old = NULL;
size_t old_len = w->w_comp_if_sz;
if(len != w->w_comp_if_sz)
{
old = w->w_comp_if;
if(!(w->w_comp_if = malloc(sizeof(*w->w_comp_if) * len)))
{
PyErr_Format(PyExc_MemoryError,
"Unable to allocate IFS IF component \
mutation weights : %s", strerror(errno));
return -1;
}
w->w_comp_if_sz = len;
}
_parse_floats(if_comp_weight, w->w_comp_if, w->w_comp_if_sz);
if(PyErr_Occurred())
{
if(old)
{
free(w->w_comp_if);
w->w_comp_if = old;
w->w_comp_if_sz = old_len;
}
return -1;
}
return 0;
}
int pyrpn_ifs_mut_params_mutation_params(ifs_mutation_weights_t *w,
PyObject *mutation_params)
{
rpn_mutation_params_t tmp_params;
// Try
if(pyrpn_pyobj_to_mutation_params(mutation_params, &tmp_params) == 0)
{
// single RPNMutationParams converted
void *tmp = realloc(w->if_mut_params,
sizeof(*w->if_mut_params));
if(!tmp)
{
PyErr_Format(PyExc_MemoryError,
"Enable to reallocate IFS RPN \
mutation params for a single parameter");
return -1;
}
w->if_mut_params = tmp;
w->custom_params = 0;
memcpy(w->if_mut_params, &tmp_params, sizeof(tmp_params));
return 0;
}
// Except
// A sequence of RPNMutationParams is given
if(PyErr_Occurred()) { PyErr_Clear(); }
const size_t len = (size_t)PySequence_Length(mutation_params);
if(PyErr_Occurred()) { return -1; }
if(len != w->if_count && len != 1)
{
PyErr_Format(PyExc_ValueError,
"Invalid lenght for IFS RPN mutation \
params sequence lenght : should be 1 or %ld or a valid RPNMutationParams, \
but a sequence of length %ld given.", w->if_count, len);
return -1;
}
void *tmp = realloc(w->if_mut_params,
sizeof(*w->if_mut_params) * len);
if(!tmp)
{
PyErr_Format(PyExc_MemoryError,
"Enable to reallocate IFS RPN \
mutation params array : %s", strerror(errno));
return -1;
}
w->if_mut_params = tmp;
PyObject *fast_params = PySequence_Fast(mutation_params,
"IFS RPN Mutation parameters \
should be a sequence");
if(!fast_params) { return -1; }
Py_INCREF(fast_params);
PyObject **mut_params = PySequence_Fast_ITEMS(fast_params);
for(size_t i=0; i < len; i++)
{
if(pyrpn_pyobj_to_mutation_params(mut_params[i],
&w->if_mut_params[i]) < 0)
{
pyrpn_PyErr_ReraiseFormat(
PyExc_ValueError,
"\
Error with IFS RPN mutation params #%ld that is not a valid RPNMutationParams");
return -1;
}
}
w->custom_params = len == 1 ? 0 : 1;
return 0;
}

130
python_mutation.h Normal file
View file

@ -0,0 +1,130 @@
/*
* Copyright (C) 2020 Weber Yann
*
* This file is part of pyrpn.
*
* pyrpn is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* pyrpn is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with pyrpn. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _PYTHON_MUTATION_H__
#define _PYTHON_MUTATION_H__
#include "config.h"
#include <errno.h>
#include <float.h>
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "structmember.h"
#include "python_const.h"
#include "rpn_mutate.h"
#include "rpn_if_mutate.h"
#include "rpn_ifs_mutate.h"
/**@file python_mutation.h
* @brief Python types (named tuple) & utility to manipulate mutation
* parameters
* @ingroup python_ext
*/
/**@defgroup pymod_pyrpn_RPNMutationParams pyrpn.RPNMutationParams
* @brief namedtuple storing mutation parameters
* @ingroup pymod_pyrpn */
/**@brief RPNMutationParams named tuple with mutation parameters */
extern PyTypeObject rpn_mutation_params_SeqDesc;
/**@brief @ref rpn_mutation_params_SeqDesc named tuple description */
extern PyStructSequence_Desc rpn_mutation_params_desc;
extern PyTypeObject ifs_mutation_weights_SeqDesc;
extern PyStructSequence_Desc ifs_mutation_weights_desc;
/**@brief Initialize mutation related types & stuff. Adds references
* to pyrpn module given in argument
* @param pyrpn_mod pyrpn python module instance
* @return 0 if no error else -1 */
int pyrpn_python_mutation_init(PyObject *pyrpn_mod);
/**@brief Try to convert a python object into a rpn_mutation_params_t
* @param py_params The python object
* @param params A pointer on the parameters to initialize
* @return -1 on failure and raise an exception
* @ingroup pymod_pyrpn_RPNExpr
*/
int pyrpn_pyobj_to_mutation_params(PyObject* py_params, rpn_mutation_params_t *params);
int pyrpn_mutation_params_to_pyobj(const rpn_mutation_params_t *params,
PyObject **res);
int pyrpn_pyobj_to_ifs_mutation_weights(PyObject *py_params,
ifs_mutation_weights_t *w);
int pyrpn_ifs_mut_weight_to_pyobj(const ifs_mutation_weights_t *w,
PyObject **mut_weights);
/**@brief Set the mutation type weight from a PyObject* sequence
* @param w The IFS mutation weight structure to edit
* @param mut_type_weight Should be a sequence of 2 float values
* @return 0 if OK -1 on error (and PyErr_Occurred() == true)
*/
int pyrpn_ifs_mut_params_mut_type_weight(ifs_mutation_weights_t *w,
PyObject *mut_type_weight);
/**@brief Set the mutation weight range from a PyObject* sequence
* @param w The IFS mutation weight structure to edit
* @param weight_range Should be a sequence of 2 float values
* @return 0 if OK -1 on error (and PyErr_Occurred() == true)
*/
int pyrpn_ifs_mut_params_weight_range(ifs_mutation_weights_t *w,
PyObject *weight_range);
/**@brief Set the IF mutation weight from a PyObject* sequence
* @note Check that the sequence length correspond to the if_count
* in the ifs_mutation_weights_t struct
* @note If current w_mut_if field is NULL allocate it
* @param w The IFS mutation weight structure to edit
* @param weight_range Should be a sequence of if_count float values
* @param do_realloc If != 0 reallocate the w_mut_if field to correspond to
* if_count
* @return 0 if OK -1 on error (and PyErr_Occurred() == true)
*/
int pyrpn_ifs_mut_params_weight_mut_if(ifs_mutation_weights_t *w,
PyObject *mut_if_weight, short do_realloc);
/**@brief Set the IF component weights from a PyObject* sequence
* @note The function cannot check the length validity of the given
* sequence (except that empty sequence are invalid).
* @param w The IFS mutation weight structure to edit
* @param mut_type_weight Should be a sequence of 2 float values
* @return 0 if OK -1 on error (and PyErr_Occurred() == true)
*/
int pyrpn_ifs_mut_params_if_comp_weight(ifs_mutation_weights_t *w,
PyObject *if_comp_weight);
/**@brief Set the RPN mutation parameters from a PyObject*
* @param w The IFS mutation weight structure to edit
* @param mutation_params Can be a valid value for a RPNMutationParams
* or a sequence of valid values for a RPNMutationParams
* of len w->if_count
* @return 0 if OK -1 on error (and PyErr_Occurred() == true)
*/
int pyrpn_ifs_mut_params_mutation_params(ifs_mutation_weights_t *w,
PyObject *mutation_params);
#endif

View file

@ -167,19 +167,14 @@ PyInit_pyrpn(void)
goto fail_add_token_types_tuple;
}
// Named tuple for mutation parameters
PyStructSequence_InitType(&rpn_mutation_params_SeqDesc,
&rpn_mutation_params_desc);
Py_INCREF(&rpn_mutation_params_SeqDesc);
if(PyModule_AddObject(mod, "RPNMutationParamsTuple",
(PyObject*)&rpn_mutation_params_SeqDesc) < 0)
if(pyrpn_python_mutation_init(mod) < 0)
{
goto fail_add_mutation_params_tuple;
goto fail_init_mutation;
}
return mod;
fail_add_mutation_params_tuple:
fail_init_mutation:
Py_DECREF(&rpn_token_types_SeqDesc);
fail_add_token_types_tuple:
Py_DECREF(&rpnif_params_SeqDesc);

View file

@ -33,6 +33,7 @@
#include "python_rpnexpr.h"
#include "python_if.h"
#include "python_ifs.h"
#include "python_mutation.h"
/**@file python_pyrpn.h
* @brief Python module & type headers

View file

@ -569,7 +569,7 @@ PyObject* rpnexpr_mutate(PyObject* slf, PyObject *args, PyObject *kwds)
}
else
{
if(rpnexpr_pyobj_to_mutation_params(py_params, &params) < 0)
if(pyrpn_pyobj_to_mutation_params(py_params, &params) < 0)
{
PyErr_SetString(PyExc_ValueError, "Bad value for params arguments");
return NULL;
@ -736,119 +736,3 @@ PyObject* rpnexpr_default_mutation_params(PyObject *cls, PyObject **argv, Py_ssi
}
/**@brief Parse given object to float
* @param obj The python object to parse
* @param res Points on result
* @note Raise python exception on error
*/
static inline void _parse_float(PyObject *obj, float *res)
{
double tmp = PyFloat_AsDouble(obj);
if(tmp > FLT_MAX)
{
PyErr_SetString(PyExc_OverflowError, "Float overflows");
return;
}
*res=tmp;
return;
}
/** Parse weights python parameter
* @param obj The python object
* @param w Points on the 3 weights to parse
* @note Raise python exception on error
*/
static inline void _parse_type_weights(PyObject *obj, float w[3])
{
PyObject *fast = PySequence_Fast(obj, "Not a RPNTokenTypeTuple nor with a length of 3");
if(!fast) { return; }
if(PySequence_Length(obj) != 3)
{
PyErr_Format(PyExc_ValueError, "Excpected RPNTokenTypeTuple or 3 elements but got %d elements", PySequence_Length(obj));
return;
}
PyObject **elts = PySequence_Fast_ITEMS(fast);
const char* names[3] = {"op", "const", "var"};
for(size_t i=0; i<3; i++)
{
_parse_float(elts[i], &w[i]);
if(PyErr_Occurred())
{
PyErr_Format(PyExc_ValueError,
"Bad value for .%s field",
names[i]);
break;
}
}
return;
}
int rpnexpr_pyobj_to_mutation_params(PyObject* py_params, rpn_mutation_params_t *params)
{
if(!PySequence_Check(py_params))
{
PyErr_SetString(PyExc_TypeError, "The given object is not a a sequence");
return -1;
}
if(PySequence_Size(py_params) != 7)
{
PyErr_SetString(PyExc_ValueError, "The given object is not a RPNMutationParamsTuple nor of length 7");
return -1;
}
PyObject *fast = PySequence_Fast(py_params, "The given object is not a RPNMutationParamsTuple nor a sequence ?");
Py_INCREF(fast);
PyObject **elts = PySequence_Fast_ITEMS(fast);
params->min_len = PyLong_AsSize_t(elts[0]);
if(PyErr_Occurred())
{
PyErr_SetString(PyExc_ValueError, "Bad type for .minimum_length field");
return -1;
}
_parse_float(elts[1], &params->w_add);
if(PyErr_Occurred())
{
PyErr_SetString(PyExc_ValueError, "Bad value for .weight_add field");
return -1;
}
_parse_float(elts[2], &params->w_del);
if(PyErr_Occurred())
{
PyErr_SetString(PyExc_ValueError, "Bad value for .weight_del field");
return -1;
}
_parse_float(elts[3], &params->w_mut);
if(PyErr_Occurred())
{
PyErr_SetString(PyExc_ValueError, "Bad value for .weight_mut field");
return -1;
}
_parse_float(elts[4], &params->w_mut_soft);
if(PyErr_Occurred())
{
PyErr_SetString(PyExc_ValueError, "Bad value for .weight_mut_soft field");
return -1;
}
_parse_type_weights(elts[5], params->w_add_elt);
if(PyErr_Occurred())
{
PyErr_SetString(PyExc_ValueError, "Bad value for .weight_add_elt field");
return -1;
}
_parse_type_weights(elts[6], params->w_mut_elt);
if(PyErr_Occurred())
{
PyErr_SetString(PyExc_ValueError, "Bad value for .weight_add_elt field");
return -1;
}
rpn_mutation_init_params(params);
return 0;
}

View file

@ -30,6 +30,7 @@
#include "rpn_jit.h"
#include "python_rpntoken.h"
#include "python_mutation.h"
#include "python_const.h"
/**@file python_rpnexpr.h
@ -247,12 +248,4 @@ PyObject* rpnexpr_random(PyObject *cls, PyObject *args, PyObject *kwds);
*/
PyObject* rpnexpr_default_mutation_params(PyObject *cls, PyObject **argv, Py_ssize_t argc);
/**@brief Try to convert a python object into a rpn_mutation_params_t
* @param py_params The python object
* @param params A pointer on the parameters to initialize
* @return -1 on failure and raise an exception
* @ingroup pymod_pyrpn_RPNExpr
*/
int rpnexpr_pyobj_to_mutation_params(PyObject* py_params, rpn_mutation_params_t *params);
#endif

45
rpn_if_mutate.c Normal file
View file

@ -0,0 +1,45 @@
/*
* Copyright (C) 2020,2023 Weber Yann
*
* This file is part of pyrpn.
*
* pyrpn is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* pyrpn is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with pyrpn. If not, see <http://www.gnu.org/licenses/>.
*/
#include "rpn_if_mutate.h"
int if_mutation(rpn_if_t *rif, rnd_t *weights, size_t n_mut,
rpn_mutation_params_t *mut_params)
{
for(size_t i=0; i<n_mut; i++)
{
size_t rpn_num;
if(_rpn_random_choice(rif->params->rpn_sz, weights,
&rpn_num) < 0)
{
return -1;
}
if(rpn_mutation(&(rif->rpn[rpn_num].toks),
mut_params) < 0)
{
return -1;
}
}
for(size_t i=0; i<rif->params->rpn_sz; i++)
{
rpn_expr_tokens_updated(&(rif->rpn[i]));
}
return 0;
}

58
rpn_if_mutate.h Normal file
View file

@ -0,0 +1,58 @@
/*
* Copyright (C) 2020,2023 Weber Yann
*
* This file is part of pyrpn.
*
* pyrpn is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* pyrpn is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with pyrpn. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __rpn_if_mutate__h__
#define __rpn_if_mutate__h__
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <sys/random.h>
#include "rpn_mutate.h"
#include "rpn_if.h"
/**@file rpn_if_mutate.h
* @brief rpn_if_t mutation headers
*/
/**@brief IF mutation weight type */
typedef float if_mutation_weight_t;
/**@brief Allocate some weights for an IF
* @param rif rpn_if_t to allocate weights for
* @param to_alloc is a **SOMETYPE, *to_alloc will be set to the
* weights array
* @return NULL on error */
#define if_mutation_alloc_weights(rif, to_alloc) (*to_alloc = \
malloc(sizeof(**to_alloc)*rif->params->rpn_sz))
/**@brief Mutate the IF given weights array
* @param if the IF we want to mutate
* @param weights Array of weights initialized by
* @ref rpnifs_fast_rnd_weights()
* @param n_mut Number of mutation to execute
* @param mut_params Mutation parameters for IF expressions
* @return 0 if no error else -1 and sets errno */
int if_mutation(rpn_if_t *rif, rnd_t *weights, size_t n_mut,
rpn_mutation_params_t *mut_params);
#endif

View file

@ -71,7 +71,8 @@ struct rpn_ifs_s
/**@brief Stores the original chance of choosing corresponding IF */
unsigned int *weight;
/** @brief Stores an array of 256 pointers on IF allowing fast
* random choice.*/
* random choice
* @todo replace with rpnifs_random_*functions */
rpn_if_t *if_proba[256];
/**@brief Stores the RPN expressions pointer of the IF contained in

319
rpn_ifs_mutate.c Normal file
View file

@ -0,0 +1,319 @@
/*
* Copyright (C) 2020 Weber Yann
*
* This file is part of pyrpn.
*
* pyrpn is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* pyrpn is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with pyrpn. If not, see <http://www.gnu.org/licenses/>.
*/
#include "rpn_ifs_mutate.h"
int ifs_mutation_weights_alloc(ifs_mutation_weights_t *res,
rpn_ifs_t *ifs, short custom_params, short custom_comp_if)
{
#if DEBUG
if(!res || !ifs)
{
errno = EINVAL;
return -1;
}
#endif
res->if_count = ifs->if_sz;
res->w_mut_if = malloc(sizeof(*res->w_mut_if)*ifs->if_sz);
if(!res->w_mut_if) { goto err; }
res->_w_if = malloc(sizeof(*res->_w_if)*ifs->if_sz);
if(!res->_w_if) { goto err_w_if; }
res->w_comp_if_sz = ifs->params.rpn_sz;
if(custom_comp_if)
{
res->w_comp_if_sz *= ifs->if_sz;
}
res->w_comp_if = malloc(sizeof(*res->w_comp_if)*res->w_comp_if_sz);
if(!res->w_comp_if)
{
goto err_comp_if;
}
res->_w_comp_if = malloc(sizeof(*res->_w_comp_if)*res->w_comp_if_sz);
if(!res->_w_comp_if)
{
goto err__comp_if;
}
size_t p_sz = sizeof(*res->if_mut_params);
p_sz *= custom_params?res->if_count:1;
res->if_mut_params = malloc(p_sz);
if(!res->if_mut_params) { goto err_if_mut_params; }
bzero(res->if_mut_params, p_sz);
return 0;
err_if_mut_params:
free(res->_w_comp_if);
err__comp_if:
free(res->w_comp_if);
res->w_comp_if_sz = 0;
err_comp_if:
free(res->_w_if);
err_w_if:
free(res->w_mut_if);
err:
return -1;
}
int ifs_mutation_weights_if_count_update(ifs_mutation_weights_t *res,
size_t new_if_count)
{
#ifndef NDEBUG
if(!res)
{
errno=EINVAL;
return -1;
}
#endif
if(new_if_count == 0)
{
res->if_count = 0;
ifs_mutation_weights_dealloc(res);
res->w_mut_if = NULL;
res->_w_if = NULL;
res->if_mut_params = NULL;
return 0;
}
else if(new_if_count == res->if_count)
{
return 0;
}
ifs_mutation_weights_t tmp;
tmp.w_mut_if = realloc(res->w_mut_if,
sizeof(*res->w_mut_if)*new_if_count);
if(!tmp.w_mut_if) { goto err; }
res->w_mut_if = tmp.w_mut_if;
if(res->if_count < new_if_count)
{
// When needed new weights are weights average
double sum = 0.0;
for(size_t i=0; i<res->if_count; i++) { sum += res->w_mut_if[i]; }
sum = sum == 0.0 ? 1.0 : sum;
for(size_t i=res->if_count; i<new_if_count; i++)
{
if(res->if_count != 0)
{
res->w_mut_if[i] = sum / res->if_count;
}
else
{
res->w_mut_if[i] = sum / new_if_count;
}
}
}
tmp._w_if = realloc(res->_w_if,
sizeof(*res->_w_if) * new_if_count);
if(!tmp._w_if) { goto err; }
res->_w_if = tmp._w_if;
if(res->custom_params)
{
tmp.if_mut_params = realloc(res->if_mut_params,
sizeof(*res->if_mut_params)*new_if_count);
if(!tmp.if_mut_params)
{
goto err;
}
res->if_mut_params = tmp.if_mut_params;
// mutations parameters should be allready initialized
// and are not reinit after copy
if(res->if_count == 0)
{
// adds default mutation parameters
memcpy(res->if_mut_params,
&rpn_mutation_params_default,
sizeof(*res->if_mut_params));
}
const size_t start = res->if_count > 0 ? res->if_count : 1;
for(size_t i=start; i<new_if_count; i++)
{
memcpy(&(res->if_mut_params[i]),
&(res->if_mut_params[i-1]),
sizeof(*res->if_mut_params));
}
}
res->if_count = new_if_count;
return ifs_mutation_weights_update(res);
err:
return -1;
}
void ifs_mutation_weights_dealloc(ifs_mutation_weights_t *w)
{
if(w->w_mut_if) { free(w->w_mut_if); }
if(w->_w_if) { free(w->_w_if); }
if(w->if_mut_params) { free(w->if_mut_params); }
}
int ifs_mutation_weights_update(ifs_mutation_weights_t *w)
{
if(w->if_count == 0) { return 0; }
#if DEBUG
if(w->w_mut_if == NULL)
{ errno = EINVAL; return -1; }
if(w->_w_if == NULL) { errno = EINVAL; return -1; }
if(w->_w_comp_if == NULL) { errno = EINVAL; return -1; }
#endif
if(rpnifs_fast_rnd_weights(2, w->w_mut_type, w->_w_type) < 0)
{
return -1;
}
if(w->custom_params)
{
for(size_t i=0; i < w->if_count; i++)
{
if(rpn_mutation_init_params(&(w->if_mut_params[i])) < 0)
{
return -1;
}
}
}
else
{
if(rpn_mutation_init_params(w->if_mut_params) < 0)
{
return -1;
}
}
if(rpnifs_fast_rnd_weights(w->if_count, w->w_mut_if, w->_w_if) < 0)
{
goto err;
}
if(rpnifs_fast_rnd_weights(w->w_comp_if_sz, w->w_comp_if,
w->_w_comp_if) < 0)
{
goto err;
}
return 0;
err:
return -1;
}
int ifs_mutation(rpn_ifs_t *ifs, ifs_mutation_weights_t *w, size_t n_mut)
{
size_t choices[2] = {0, 0};
size_t ret;
for(size_t i=0; i<n_mut; i++)
{
if(_rpn_random_choice(2, w->_w_type, &ret) < 0)
{
return -1;
}
choices[ret]++;
}
if(choices[0])
{
if(ifs_weight_mutation(ifs, w, choices[0]) < 0)
{
return -1;
}
}
if(choices[1])
{
if(ifs_if_mutation(ifs, w, choices[1]) < 0)
{
return -1;
}
}
return 0;
}
int ifs_weight_mutation(rpn_ifs_t *ifs, ifs_mutation_weights_t *w, size_t n_mut)
{
for(size_t i=0; i<n_mut; i++)
{
size_t ret;
if(_rpn_random_choice(ifs->if_sz, NULL, &ret) < 0)
{
return -1;
}
float mod;
if(rpnifs_rnd_float(w->w_weight_range, &mod) < 0)
{
return -1;
}
ifs->weight[ret] += mod;
}
return 0;
}
int ifs_if_mutation(rpn_ifs_t *ifs, ifs_mutation_weights_t *w, size_t n_mut)
{
size_t *muts;
if(!(muts = malloc(sizeof(*muts)*ifs->if_sz)))
{
return -1;
}
bzero(muts, sizeof(*muts)*ifs->if_sz);
for(size_t i=0; i<n_mut; i++)
{
size_t ret;
if(_rpn_random_choice(ifs->if_sz, w->_w_if, &ret) < 0)
{
goto err;
}
muts[ret]++;
}
for(size_t i=0; i<n_mut; i++)
{
rnd_t *weights;
if(w->w_comp_if_sz == ifs->if_sz)
{
weights = w->_w_comp_if;
}
else
{
weights = &(w->_w_comp_if[i*ifs->if_sz]);
}
rpn_mutation_params_t *params;
if(w->custom_params)
{
params = &(w->if_mut_params[i]);
}
else
{
params = w->if_mut_params;
}
if(if_mutation(ifs->rpn_if[i], weights, muts[i], params) < 0)
{
return -1;
}
}
free(muts);
return 0;
err:
free(muts);
return -1;
}

134
rpn_ifs_mutate.h Normal file
View file

@ -0,0 +1,134 @@
/*
* Copyright (C) 2020,2023 Weber Yann
*
* This file is part of pyrpn.
*
* pyrpn is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* pyrpn is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with pyrpn. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __rpn_ifs_mutate__h__
#define __rpn_ifs_mutate__h__
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <sys/random.h>
#include "rpn_mutate.h"
#include "rpn_if_mutate.h"
#include "rpn_ifs.h"
/**@file rpn_ifs_mutate.h
* @brief rpn_ifs_t mutation headers
* @todo implement function to mutate IF (in another file ? : yep)
*/
/**@brief IFS mutation parameters */
typedef struct ifs_mutation_weights_s ifs_mutation_weights_t;
/**@brief IFS mutation parameters
* @todo add weights for mutations of IF (weights for component mutation in IF)
*/
struct ifs_mutation_weights_s
{
/**@brief Weight for mutation type : expr, weight */
float w_mut_type[2];
/**@brief internal use fast random choice for mutation type */
rnd_t _w_type[2];
/**@brief Minimum & maximum weight variation (considering weights
* in [0.0 .. 1.0] and sum(weights) == 1.0 */
float w_weight_range[2];
/**@brief Number of IF in the system */
size_t if_count;
/**@brief Weight for each IF (chance to mutate) */
float *w_mut_if;
/**@brief internal use fast random choice for if mutation */
rnd_t *_w_if;
/**@brief IF component mutation weights */
if_mutation_weight_t *w_comp_if;
rnd_t *_w_comp_if;
/**@brief Can be if->rpn_sz (number of component in an IF) or
* if_count * if->rpn_sz (one weight for each component of each IF) */
size_t w_comp_if_sz;
/**@brief If 1 if_mut_params will countain if_count params else 1 */
short custom_params;
/**@brief Optionnal (or NULL) array for custom parameters for
* each IF */
rpn_mutation_params_t *if_mut_params;
};
/**@brief Allocate internal fields according to the number of IF in the
* system
* @param res The struct to allocate in
* @param sz The number of if in the system
* @param custom_params If 0 do not allocate
* @ref ifs_mutation_weights_s::if_mut_params
* @param custom_comp_if Determine the value in
* @ref ifs_mutation_weights_s::w_comp_if_sz. If 0 allocate
* one weight per IF component, else allocate one weight for
* each component in each IF.
* @return 0 if no error else -1 and sets errno */
int ifs_mutation_weights_alloc(ifs_mutation_weights_t *res,
rpn_ifs_t *ifs, short custom_params, short custom_comp_if);
/**@brief When needed reallocate internal fields to match the number of IF in
* the system.
* @note Weights added by reallocation will be the weights average
* @note New if_mut_params added by reallocation will be copies of previous
* mutation params
* @param res The struct to reallocate in
* @param if_count The new IF count to addapt to
* @return 0 if no error else -1 and sets errno */
int ifs_mutation_weights_if_count_update(ifs_mutation_weights_t *res,
size_t new_if_count);
/**@brief Deallocate internal fields
* @param res The struct to cleanup */
void ifs_mutation_weights_dealloc(ifs_mutation_weights_t *w);
/**@brief Once allocate and all values set use this function to
* update internal fast random indexes
* @param w the ifs weights to update
* @return 0 if no error else -1 and sets errno */
int ifs_mutation_weights_update(ifs_mutation_weights_t *w);
/**@brief Use updated weights to mutate an IFS
* @param ifs The system to mutate
* @param w The mutation weights & parameters
* @param n_mut The number of mutations
* @param default_mut_params If custom
* @ref ifs_mutation_weights_s::if_mut_params params for each IF in
* the system not set, this argument must be not NULL and is used
* as IF mutation parameter
*/
int ifs_mutation(rpn_ifs_t *ifs, ifs_mutation_weights_t *w, size_t n_mut);
int ifs_weight_mutation(rpn_ifs_t *ifs, ifs_mutation_weights_t *w, size_t n_mut);
int ifs_if_mutation(rpn_ifs_t *ifs, ifs_mutation_weights_t *w, size_t n_mut);
#endif

View file

@ -2,7 +2,7 @@
const rnd_t rnd_t_max = -1;
const rpn_mutation_params_t rpn_default_mutation = {
rpn_mutation_params_t rpn_mutation_params_default = {
.min_len = 3,
.w_add = 1.25,
.w_del = 1.0,
@ -12,33 +12,13 @@ const rpn_mutation_params_t rpn_default_mutation = {
.w_mut_elt={1,1,1},
};
#define to_weight(total, elt) ((rnd_t)((((long double)elt)*rnd_t_max)/total))
int rpn_mutation_init_params(rpn_mutation_params_t *params)
{
long double total;
if(params->w_add < 0) { goto err; }
if(params->w_del < 0) { goto err; }
if(params->w_mut < 0) { goto err; }
if(params->w_mut_soft < 0) { goto err; }
total = params->w_add + params->w_del + params->w_mut + params->w_mut_soft;
params->_weights[0] = to_weight(total, params->w_add);
params->_weights[1] = to_weight(total, params->w_del);
params->_weights[2] = to_weight(total, params->w_mut);
params->_weights[3] = to_weight(total, params->w_mut_soft);
/*
#ifdef DEBUG
dprintf(2, "weights : %d %d %d %d\n",
params->_weights[0],
params->_weights[1],
params->_weights[2],
params->_weights[3]);
#endif
*/
for(uint8_t i=1; i<4; i++)
float mut_weights[4] = {params->w_add, params->w_del,
params->w_mut, params->w_mut_soft};
if(rpnifs_fast_rnd_weights(4, mut_weights, params->_weights)<0)
{
params->_weights[i] += params->_weights[i-1];
return -1;
}
/*
#ifdef DEBUG
@ -49,61 +29,21 @@ dprintf(2, "summed weights : %d %d %d %d\n",
params->_weights[3]);
#endif
*/
total = 0;
for(uint8_t i=0; i<3; i++)
/**@todo test this */
if(rpnifs_fast_rnd_weights(3, params->w_add_elt,
&(params->_weights[4])) < 0)
{
if(params->w_add_elt[i] < 0) { goto err; }
total += params->w_add_elt[i];
}
for(uint8_t i=0; i<3; i++)
{
rnd_t w;
if(total > 0)
{
w = to_weight(total, params->w_add_elt[i]);
}
else
{
w = to_weight(3,1);
}
params->_weights[i+4] = w;
if(i>0)
{
params->_weights[i+4] += params->_weights[i+3];
}
return -1;
}
total = 0;
for(uint8_t i=0; i<3; i++)
if(rpnifs_fast_rnd_weights(3, params->w_mut_elt,
&(params->_weights[7])) < 0)
{
if(params->w_mut_elt[i] < 0) { goto err; }
total += params->w_mut_elt[i];
}
for(uint8_t i=0; i<3; i++)
{
rnd_t w;
if(total > 0)
{
w = to_weight(total, params->w_mut_elt[i]);
}
else
{
w = to_weight(3, 1);
}
params->_weights[i+7] = w;
if(i>0)
{
params->_weights[i+7] += params->_weights[i+6];
}
return -1;
}
return 0;
err:
errno = EINVAL;
return -1;
}
#undef to_weight
int rpn_mutation(rpn_tokenized_t *toks, rpn_mutation_params_t *params)
{
@ -335,11 +275,83 @@ int rpn_getrandom(rnd_t *rand)
return 0;
}
int rpnifs_fast_rnd_weights(size_t sz, const float *weights,
rnd_t *fast_weights)
{
long double total = 0;
for(size_t i=0; i<sz; i++)
{
if(weights[i] < 0)
{
errno = EDOM;
return -1;
}
total += weights[i];
}
for(size_t i=0; i<sz; i++)
{
rnd_t w;
if(total == 0)
{
w = (rnd_t)((rnd_t_max / sz)*(i+1));
}
else
{
w = ((((long double)weights[i]) * rnd_t_max)/total);
if(i>0)
{
w += fast_weights[i-1];
}
}
fast_weights[i] = w;
}
return 0;
}
int rpnifs_rnd_float(float range[2], float *res)
{
#if DEBUG
if(range[0] >= range[1])
{
errno = EINVAL;
return -1;
}
#endif
const rnd_t mid = rnd_t_max / 2;
rnd_t rand;
short neg;
if(rpn_getrandom(&rand) < 0) { return -1; }
if(rand >= mid)
{
neg = 1;
rand -= mid;
}
else
{
neg = 0;
}
*res = ((rand*(range[1]-range[0]))/mid) + range[0];
*res *= neg?-1:1;
return 0;
}
int _rpn_random_choice(size_t sz, rnd_t *weights, size_t *res)
{
if(weights)
{
rnd_t rand;
if(rpn_getrandom(&rand) < 0) { return -1; }
__rpn_random_choice(sz, weights, rand, res);
return 0;
}
// unweighted choice
rnd_t rand;
unsigned long long div = rnd_t_max;
div++;
div /= sz;
if(rpn_getrandom(&rand) < 0) { return -1; }
__rpn_random_choice(sz, weights, rand, res);
*res = (rnd_t)(((unsigned long long)rand)/div);
return 0;
}

View file

@ -31,14 +31,18 @@
* @brief Expression mutation headers
*/
/**@brief Mutation parameters */
/**@brief RPN expression mutation parameters */
typedef struct rpn_mutation_params_s rpn_mutation_params_t;
/**@brief Type of random values used by mutations */
typedef uint16_t rnd_t;
/**@brief maximum value for rnd_t */
extern const rnd_t rnd_t_max;
/** @brief Mutation parameters */
/**@brief Default values for mutations parameters */
extern rpn_mutation_params_t rpn_mutation_params_default;
/**@brief RPN expression mutation parameters */
struct rpn_mutation_params_s
{
/**@brief Minimum expression length */
@ -71,6 +75,7 @@ struct rpn_mutation_params_s
rnd_t _weights[10];
};
/**@brief Initialize mutation parameters
*
* pre-process integers threshold by group
@ -135,11 +140,26 @@ int rpn_getrandom(rnd_t *rand);
* @return -1 on error else 0 */
int rpn_rand_limit(size_t max, size_t *res);
/**@brief Conver an array of float weights to an array for fast random
* choices (see @ref _rpn_random_choices() )
* @param sz The number of items in to choose from
* @param weights Array of size sz with items weights (positive values)
* @param fast_weights Array of size sz pointing on result
* @return -1 on error else 0 */
int rpnifs_fast_rnd_weights(size_t sz, const float *weights,
rnd_t *fast_weights);
/**
* @return A float between [-range[1] .. -range[0]] or [range[0] .. range[1]]
*/
int rpnifs_rnd_float(float range[2], float *res);
/**@brief Given a size return an random element with regards to given weights
* @param sz The given size
* @param weights Weights coming from @ref rpn_mutation_params_s._weights
* @param res Points on result
* @return 0 or -1 on error */
* @return 0 or -1 on error
* @todo refactor rename without rpn prefix */
int _rpn_random_choice(size_t sz, rnd_t *weights, size_t *res);
/**@brief Given a size and a (random) value, returns an element with regards

View file

@ -288,3 +288,74 @@ class TestRPNIFSCoordinates(unittest.TestCase):
self.assertEqual(result, pos)
class TestRPNIFSMutation(unittest.TestCase):
""" Testing mutation of IFS """
def test_set_params_empty(self):
""" Testing set_mutation_params() method giving 'params' argument
only without any existing IFS
"""
ifs=pyrpn.RPNIFS(pyrpn.const.POS_XY, pyrpn.const.RESULT_RGB, (100,100,))
params = list(ifs.mutation_params)
# Note : precision loss using float avoided using "exact" float values
params[0] = (.25,.75)
ifs.set_mutation_params(params)
self.assertEqual(params, list(ifs.mutation_params))
def test_set_params(self):
""" Testing set_mutation_params() method giving 'params' argument
only without any existing IFS
"""
ifs=pyrpn.RPNIFS(pyrpn.const.POS_XY, pyrpn.const.RESULT_RGB, (100,100,))
ifs.weights([1,3])
params = list(ifs.mutation_params)
params[0] = (.25,.75)
params[5] = tuple([params[5]] * 2)
ifs.set_mutation_params(params)
self.assertEqual(params, list(ifs.mutation_params))
def test_set_params_kwds(self):
""" Testing set_mutation_params() method giving keywords arguments
"""
kwds = {
'weight_type': (0, (0.25, 0.75)),
'mutation_weight_range': (1, (.5,.75)),
'if_weights': (3, (.125, .75)),
'if_component_weights': (4, (.125,.25,.75,.375,1/16)),
'if_mut_params': (5, (1,1,0,0,0,(1/16,1/32,1/64), (1/64,1/16,1/4)))
}
for name, elts in kwds.items():
idx, value = elts
ifs = pyrpn.RPNIFS(pyrpn.const.POS_XY, pyrpn.const.RESULT_RGB,
(100,100))
ifs.weights([1,3])
params = list(ifs.mutation_params)
with self.subTest(arg_name=name, value=value):
arg = {name:value}
ifs.set_mutation_params(**arg)
params[idx] = value
self.assertEqual(params, list(ifs.mutation_params))
def test_set_params_override(self):
""" Testing that params argument of set_mutation_params() is overwritten
by keywords argument values
"""
ifs = pyrpn.RPNIFS(pyrpn.const.POS_XY, pyrpn.const.RESULT_RGB, (100,100))
ifs.weights([1,3])
params = list(ifs.mutation_params)
params[0] = (.25,.75)
wanted = (.75,.25)
ifs.set_mutation_params(params, weight_type=wanted)
params[0] = wanted
self.assertEqual(params, list(ifs.mutation_params))