Starts implementing RPNIFS class

- __init__
- weights setter/getter
- __len__
This commit is contained in:
Yann Weber 2023-09-22 14:41:52 +02:00
commit 48b8f3fbad
5 changed files with 244 additions and 7 deletions

View file

@ -21,11 +21,21 @@
/**@file python_ifs.c */
static PyMethodDef RPNIFS_methods[] = {
PYRPN_method("weights", rpnifs_weights,
METH_FASTCALL,
"self, weights=None",
"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"),
{NULL} // Sentinel
};
static PyMemberDef RPNIFS_members[] = {
{"iter_expressions", T_OBJECT, offsetof(PyRPNIFS_t, rifs), READONLY,
{"expressions", T_OBJECT, offsetof(PyRPNIFS_t, rifs), READONLY,
"The tuple with RPNIterExpr instances"},
{"mmap", T_OBJECT, offsetof(PyRPNIFS_t, mmap), READONLY,
"The mmap storing data"},
@ -156,7 +166,7 @@ length %ld provided",
ifs_self->_mmap = ifs_self->mm_buff.buf;
ifs_self->ifs = rpn_ifs_new(rif_params, ifs_self->mm_buff.buf);
if(!ifs_self->rifs)
if(!ifs_self->ifs)
{
PyErr_Format(PyExc_RuntimeError,
"Error initializing ifs: %s", strerror(errno));
@ -185,6 +195,91 @@ void rpnifs_del(PyObject *self)
}
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
const Py_ssize_t len = PyObject_Length(args[0]);
if(PyErr_Occurred())
{
return NULL;
}
/*
if((size_t)len != ifs_self->ifs->if_sz)
{
PyErr_Format(PyExc_ValueError,
"Expected %ld weights but argument length is %ld",
ifs_self->ifs->if_sz, len);
return NULL;
}
*/
weights = malloc(sizeof(unsigned int)*len);
if(len && !weights)
{
PyErr_Format(PyExc_MemoryError,
"Unable to allocate weights : %s",
strerror(errno));
return NULL;
}
iter = PyObject_GetIter(args[0]);
if(PyErr_Occurred())
{
goto err_weights;
}
for(size_t i=0; i<(unsigned int)len; i++)
{
PyObject *item = PyIter_Next(iter);
if(PyErr_Occurred())
{
goto err_weights;
}
const unsigned long w = PyLong_AsUnsignedLong(item);
Py_DECREF(item);
if(PyErr_Occurred())
{
goto err_weights;
}
weights[i] = w;
}
Py_DECREF(iter);
rpn_ifs_set_if_count(ifs_self->ifs, len, weights);
free(weights);
}
// return weights in a new tuple
PyObject *ret = PyTuple_New(ifs_self->ifs->if_sz);
if(PyErr_Occurred())
{
return NULL;
}
for(size_t i=0; i<ifs_self->ifs->if_sz; i++)
{
const unsigned int weight = ifs_self->ifs->weight[i];
PyObject *item = PyLong_FromUnsignedLong(weight);
if(PyErr_Occurred()) { return NULL; }
PyTuple_SET_ITEM(ret, i, item);
}
return ret;
err_weights:
free(weights);
Py_DECREF(iter);
return NULL;
}
PyObject *rpnifs_weight(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
{
PyErr_SetString(PyExc_NotImplementedError, "TODO");
return NULL;
}
PyObject *rpnifs_str(PyObject *self)
{
PyErr_SetString(PyExc_NotImplementedError, "TODO");
@ -201,8 +296,8 @@ PyObject *rpnifs_repr(PyObject *self)
Py_ssize_t rpnifs_len(PyObject *self)
{
PyErr_SetString(PyExc_NotImplementedError, "TODO");
return -1;
PyRPNIFS_t *ifs_self = (PyRPNIFS_t*)self;
return (Py_ssize_t)ifs_self->ifs->if_sz;
}

View file

@ -80,6 +80,9 @@ PyObject *rpnifs_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds);
int rpnifs_init(PyObject *self, PyObject *args, PyObject *kwds);
void rpnifs_del(PyObject *self);
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_str(PyObject *self);
PyObject *rpnifs_repr(PyObject *self);
@ -87,7 +90,6 @@ Py_ssize_t rpnifs_len(PyObject *self);
PyObject *rpnifs_expr_item(PyObject *self, Py_ssize_t idx);
int rpnifs_expr_ass_item(PyObject *self, Py_ssize_t idx, PyObject *value);
int rpnifs_getbuffer(PyObject *self, Py_buffer *view, int flags);
void rpnifs_releasebuffer(PyObject *self, Py_buffer *view);

View file

@ -7,6 +7,7 @@ rpn_ifs_t* rpn_ifs_new(rpn_if_param_t *params, rpn_value_t *memmap)
if(!(res = malloc(sizeof(rpn_ifs_t))))
{
err = errno;
goto error;
}
bzero(res, sizeof(rpn_ifs_t));
@ -25,6 +26,7 @@ rpn_ifs_t* rpn_ifs_new(rpn_if_param_t *params, rpn_value_t *memmap)
-1, 0);
if(res->mem == (void*)-1)
{
err = errno;
goto mmap_err;
}
}
@ -32,10 +34,8 @@ rpn_ifs_t* rpn_ifs_new(rpn_if_param_t *params, rpn_value_t *memmap)
return res;
mmap_err:
err = errno;
free(res);
error:
err = errno;
errno = err;
return NULL;
}
@ -60,6 +60,72 @@ void rpn_ifs_free(rpn_ifs_t *rifs)
}
}
size_t rpn_ifs_set_if_count(rpn_ifs_t *rifs, size_t count, unsigned int *weights)
{
errno = 0;
const size_t old_sz = rifs->if_sz;
// free old functions if old_sz > count
if(old_sz)
{
for(size_t i=old_sz-1; i>=count; i--)
{
rpn_if_free(rifs->rpn_if[i]);
}
}
void *tmp;
if(!(tmp = realloc(rifs->rpn_if, sizeof(rpn_if_t*)*count)))
{
return 0;
}
rifs->rpn_if = tmp;
if(!(tmp = realloc(rifs->weight, sizeof(unsigned int) * count)))
{
return 0;
}
rifs->weight = tmp;
rifs->flat_sz = rifs->params.rpn_sz * count;
if(!(tmp = realloc(rifs->flat_rpn, sizeof(rpn_expr_t*) * rifs->flat_sz)))
{
rifs->flat_sz = rifs->params.rpn_sz * old_sz;
return 0;
}
rifs->flat_rpn = tmp;
rifs->if_sz = count;
// init new functions if old_sz < count
for(size_t i=old_sz; i<count; i++)
{
rifs->rpn_if[i] = rpn_if_new(&(rifs->params), rifs->mem, NULL);
if(!rifs->rpn_if[i])
{
return 0;
}
for(size_t j=0; j<rifs->params.rpn_sz; j++)
{
const size_t flat_idx = (i*rifs->params.rpn_sz) + j;
rifs->flat_rpn[flat_idx] = &(rifs->rpn_if[i]->rpn[j]);
}
}
// set all weights
memcpy(rifs->weight, weights, sizeof(unsigned int) * count);
if(rpn_ifs_weight_update(rifs) < 0)
{
return 0;
}
return rifs->if_sz;
}
size_t rpn_ifs_add_if(rpn_ifs_t *rifs, unsigned int weight)
{
size_t res, i, first_flat;

View file

@ -95,6 +95,15 @@ rpn_ifs_t* rpn_ifs_new(rpn_if_param_t *params, rpn_value_t *memmap);
*/
void rpn_ifs_free(rpn_ifs_t *rifs);
/**@brief Set the number and weights of functions in the system
* @note Do not re-init existing function
* @param rifs The iterated function system
* @param count The number of functions in the system
* @param weights The function's weight
* @return The number of function in the system
*/
size_t rpn_ifs_set_if_count(rpn_ifs_t *rifs, size_t count, unsigned int *weights);
/**@brief Add a new iterated function to the system
* @param rifs The iterated function system
* @param weight The new expression weight

65
tests/tests_rpn_ifs.py Executable file
View file

@ -0,0 +1,65 @@
#!/usr/bin/python3
# copyright 2023 weber yann
#
# this file is part of rpnifs.
#
# geneifs 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
# (at your option) any later version.
#
# geneifs 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 geneifs. if not, see <http://www.gnu.org/licenses/>.
#
import copy
import mmap
import pickle
import random
import sys
import unittest
try:
import pyrpn
except (ImportError, NameError) as e:
print("error importing pyrpn. try to run make.",
file=sys.stderr)
raise e
from utils import *
class TestRPNIFS(unittest.TestCase):
""" Testing RPNIFS features """
def test_init(self):
ifs = pyrpn.RPNIFS(pyrpn.const.POS_XY,
pyrpn.const.RESULT_COUNT,
(640,480))
self.assertEqual(len(ifs), 0)
def test_weights_set(self):
ifs = pyrpn.RPNIFS(pyrpn.const.POS_XY,
pyrpn.const.RESULT_COUNT,
(640,480))
self.assertEqual(len(ifs), 0)
w = ifs.weights([1,2,4])
self.assertEqual(len(ifs), 3)
self.assertEqual(w, (1,2,4))
w = ifs.weights()
self.assertEqual(w, (1,2,4))
for ifs_len in [random.randint(1,10) for _ in range(10)]:
for _ in range(10):
w = [random.randint(1,1000) for _ in range(ifs_len)]
ifs.weights(w)
self.assertEqual(len(ifs), ifs_len)
nw = ifs.weights()
self.assertEqual(tuple(w), nw)