Implement RpnIterExpr.mutate method
- adds basic tests of the method - bugfix 0 weights handling
This commit is contained in:
parent
684bb12614
commit
5b2b9844e8
8 changed files with 214 additions and 15 deletions
134
python_if.c
134
python_if.c
|
|
@ -60,6 +60,10 @@ flag, a result flag and size limits"),
|
|||
METH_O,
|
||||
"self, position, /",
|
||||
"Run an IF given a position and return a new position"),
|
||||
PYRPN_method("mutate", rpnif_mutate,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"self, n_mutations=1, params=None",
|
||||
"Mutate the expressions in the IF"),
|
||||
PYRPN_method("to_pos", rpnif_to_pos,
|
||||
METH_FASTCALL,
|
||||
"self, *args, /",
|
||||
|
|
@ -514,6 +518,136 @@ PyObject *rpnif_step(PyObject *self, PyObject* opos)
|
|||
}
|
||||
|
||||
|
||||
PyObject* rpnif_mutate(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
PyRPNIterExpr_t *expr_self = (PyRPNIterExpr_t*)self;
|
||||
size_t rpn_sz = expr_self->rif->params->rpn_sz;
|
||||
|
||||
char *str_args = "|IOO:RPNExpr.mutate";
|
||||
char *names[] = {"n_mutations", "params", "weights", NULL};
|
||||
|
||||
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);
|
||||
|
||||
if(!PyArg_ParseTupleAndKeywords(args, kwargs, str_args, names,
|
||||
&n_mutations, &py_params, &py_weights))
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
|
||||
if(!py_weights || py_weights == Py_None)
|
||||
{
|
||||
for(size_t i=0; i<rpn_sz; i++)
|
||||
{
|
||||
weights[i] = (rnd_t)((rnd_t_max / rpn_sz)*(i+1));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Parsing weights sequence into a weights array suitable
|
||||
// for rpn_mutate utility function
|
||||
PyObject *seq = PySequence_Fast(py_weights,
|
||||
"weights argument must be a sequence");
|
||||
if(!seq)
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
if((size_t)PySequence_Fast_GET_SIZE(seq) != rpn_sz)
|
||||
{
|
||||
Py_DECREF(seq);
|
||||
char err[] = "expected weights to be a sequence of \
|
||||
%ld elements, but got %ld elements";
|
||||
PyErr_Format(PyExc_TypeError, err,
|
||||
rpn_sz,
|
||||
PySequence_Fast_GET_SIZE(seq));
|
||||
goto err;
|
||||
|
||||
}
|
||||
long double total = 0;
|
||||
for(size_t i=0; i<rpn_sz; i++)
|
||||
{
|
||||
PyObject *elt = PySequence_Fast_GET_ITEM(seq, i);
|
||||
if(!PyNumber_Check(elt))
|
||||
{
|
||||
Py_DECREF(seq);
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"weights must contains float values");
|
||||
goto err;
|
||||
}
|
||||
total += PyFloat_AsDouble(elt);
|
||||
if(PyErr_Occurred())
|
||||
{
|
||||
Py_DECREF(seq);
|
||||
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;
|
||||
}
|
||||
Py_DECREF(seq);
|
||||
}
|
||||
|
||||
if(!py_params || py_params == Py_None)
|
||||
{
|
||||
if(py_params == Py_None) { Py_DECREF(Py_None); }
|
||||
memcpy(¶ms, &rpn_mutation_params_default,
|
||||
sizeof(rpn_mutation_params_t));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(rpnexpr_pyobj_to_mutation_params(py_params, ¶ms) < 0)
|
||||
{
|
||||
if(!PyErr_Occurred())
|
||||
{
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Bad value for params arguments : %s",
|
||||
strerror(errno));
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t i=0; i<n_mutations; i++)
|
||||
{
|
||||
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),
|
||||
¶ms) < 0)
|
||||
{
|
||||
PyErr_Format(PyExc_RuntimeError,
|
||||
"Mutation failed : %s",
|
||||
strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
rpn_expr_tokens_updated(&(expr_self->rif->rpn[rpn_num]));
|
||||
}
|
||||
|
||||
free(weights);
|
||||
Py_RETURN_NONE;
|
||||
|
||||
err:
|
||||
free(weights);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
PyObject *rpnif_to_pos(PyObject *self, PyObject** argv, Py_ssize_t argc)
|
||||
{
|
||||
PyRPNIterExpr_t *expr_self = (PyRPNIterExpr_t*)self;
|
||||
|
|
|
|||
10
python_if.h
10
python_if.h
|
|
@ -198,6 +198,16 @@ PyObject *rpnif_shape(PyObject *self);
|
|||
*/
|
||||
PyObject *rpnif_step(PyObject *self, PyObject* pos);
|
||||
|
||||
/**@brief Mutate the expressions in the IF
|
||||
* @note For X mutations, an expression will be choosen randomly and mutate between
|
||||
* 1 and X times, if mutations left to do we loop on expression choice.
|
||||
* @param self
|
||||
* @param args
|
||||
* @param kwargs
|
||||
* @return None
|
||||
*/
|
||||
PyObject* rpnif_mutate(PyObject *self, PyObject *args, PyObject *kwargs);
|
||||
|
||||
/**@brief Convert given arguments coordinates to position
|
||||
* @param self RPNIterExpr instance
|
||||
* @param argv Pointer on the array of arguments
|
||||
|
|
|
|||
11
python_ifs.c
11
python_ifs.c
|
|
@ -27,10 +27,6 @@ static PyMethodDef RPNIFS_methods[] = {
|
|||
"Get or set the weights list.\n\n\
|
||||
When setting the weight list, the weight list len sets the number of functions \
|
||||
in the system."),
|
||||
PYRPN_method("weight", rpnifs_weight,
|
||||
METH_FASTCALL,
|
||||
"self, idx, weight=None",
|
||||
"Get or set a single weight"),
|
||||
PYRPN_method("position", rpnifs_position,
|
||||
METH_FASTCALL,
|
||||
"self, position=None",
|
||||
|
|
@ -318,13 +314,6 @@ err_weights:
|
|||
}
|
||||
|
||||
|
||||
PyObject *rpnifs_weight(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
|
||||
{
|
||||
PyErr_SetString(PyExc_NotImplementedError, "TODO");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
PyObject *rpnifs_position(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
|
||||
{
|
||||
PyRPNIFS_t *ifs_self = (PyRPNIFS_t*)self;
|
||||
|
|
|
|||
|
|
@ -86,7 +86,6 @@ 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_weight(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);
|
||||
|
||||
|
|
|
|||
|
|
@ -548,7 +548,7 @@ PyObject* rpnexpr_mutate(PyObject* slf, PyObject *args, PyObject *kwds)
|
|||
{
|
||||
PyRPNExpr_t *self = (PyRPNExpr_t*)slf;
|
||||
|
||||
char *str_args = "|IO:RPNIterExpr.mutate";
|
||||
char *str_args = "|IO:RPNExpr.mutate";
|
||||
char *names[] = {"n_mutations", "params", NULL};
|
||||
|
||||
PyObject *py_params = NULL;
|
||||
|
|
|
|||
22
rpn_mutate.c
22
rpn_mutate.c
|
|
@ -58,7 +58,16 @@ dprintf(2, "summed weights : %d %d %d %d\n",
|
|||
}
|
||||
for(uint8_t i=0; i<3; i++)
|
||||
{
|
||||
params->_weights[i+4] = to_weight(total, params->w_add_elt[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];
|
||||
|
|
@ -73,7 +82,16 @@ dprintf(2, "summed weights : %d %d %d %d\n",
|
|||
}
|
||||
for(uint8_t i=0; i<3; i++)
|
||||
{
|
||||
params->_weights[i+7] = to_weight(total, params->w_mut_elt[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];
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ typedef struct rpn_mutation_params_s rpn_mutation_params_t;
|
|||
/**@brief Type of random values used by mutations */
|
||||
typedef uint16_t rnd_t;
|
||||
|
||||
extern const rnd_t rnd_t_max;
|
||||
|
||||
/** @brief Mutation parameters */
|
||||
struct rpn_mutation_params_s
|
||||
{
|
||||
|
|
|
|||
|
|
@ -627,6 +627,53 @@ class TestRPNIterConst(unittest.TestCase):
|
|||
self.assertEqual(colors, [7, 9, 10, 11])
|
||||
|
||||
|
||||
class TestRpnIterMutate(unittest.TestCase):
|
||||
""" @todo Complete tests
|
||||
"""
|
||||
|
||||
def test_mutate(self):
|
||||
""" Testing default mutation parameters """
|
||||
rif = pyrpn.RPNIterExpr(pyrpn.const.POS_LINEAR,
|
||||
pyrpn.const.RESULT_RGBA,
|
||||
(512,))
|
||||
|
||||
rif.mutate()
|
||||
rif.mutate(10)
|
||||
|
||||
def test_mutate_weights(self):
|
||||
""" Testing mutation expression weights """
|
||||
params = [1,1,0,0,0,(0,0,0),(0,0,0)]
|
||||
param = pyrpn.RPNMutationParamsTuple(params)
|
||||
for n_mut in range(0,100,10):
|
||||
for i in range(4):
|
||||
rif = pyrpn.RPNIterExpr(pyrpn.const.POS_LINEAR,
|
||||
pyrpn.const.RESULT_RGB,
|
||||
(512,))
|
||||
weights = [1 if i == j else 0 for j in range(4)]
|
||||
rif.mutate(n_mut, params=param, weights=weights)
|
||||
rpn_exprs = list(rif.values())
|
||||
for j in range(4):
|
||||
with self.subTest(params=param, weights=weights, expr=j):
|
||||
if j == i:
|
||||
self.assertEqual(len(rpn_exprs[j]), n_mut)
|
||||
else:
|
||||
self.assertEqual(len(rpn_exprs[j]), 0)
|
||||
n_mut = 400
|
||||
for w in Progress(range(100)):
|
||||
weights = [w for _ in range(4)]
|
||||
rif = pyrpn.RPNIterExpr(pyrpn.const.POS_LINEAR,
|
||||
pyrpn.const.RESULT_RGB,
|
||||
(512, ))
|
||||
|
||||
rif.mutate(n_mut, params=param, weights=weights)
|
||||
rpn_exprs = list(rif.values())
|
||||
for rpnexpr in rpn_exprs:
|
||||
expr_len = len(rpnexpr)
|
||||
expt_len = n_mut / 4
|
||||
score = abs(1 - (expt_len / expr_len))
|
||||
self.assertLess(score, 0.5)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue