diff --git a/python_if.c b/python_if.c index 523c417..8131504 100644 --- a/python_if.c +++ b/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; i0? 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; irif->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; diff --git a/python_if.h b/python_if.h index 0fb58af..695cbda 100644 --- a/python_if.h +++ b/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 diff --git a/python_ifs.c b/python_ifs.c index 27744b3..9606af8 100644 --- a/python_ifs.c +++ b/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; diff --git a/python_ifs.h b/python_ifs.h index 198eeca..08ec1c3 100644 --- a/python_ifs.h +++ b/python_ifs.h @@ -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); diff --git a/python_rpnexpr.c b/python_rpnexpr.c index 0732390..2fa9706 100644 --- a/python_rpnexpr.c +++ b/python_rpnexpr.c @@ -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; diff --git a/rpn_mutate.c b/rpn_mutate.c index 248ad95..dbd41c6 100644 --- a/rpn_mutate.c +++ b/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]; diff --git a/rpn_mutate.h b/rpn_mutate.h index db0c291..2718958 100644 --- a/rpn_mutate.h +++ b/rpn_mutate.h @@ -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 { diff --git a/tests/tests_rpniter.py b/tests/tests_rpniter.py index de5d91a..0759836 100755 --- a/tests/tests_rpniter.py +++ b/tests/tests_rpniter.py @@ -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()