Compare commits

...

12 commits

Author SHA1 Message Date
Zoltán Vörös
2a725d6067 removed printf in numerical.c 2019-11-14 20:17:33 +01:00
Zoltán Vörös
2851d3007a sum, mean, std return a single number, if the result array is of length 1 2019-11-13 18:54:27 +01:00
Zoltán Vörös
ad0531e35e attempt at fixing std problem 2019-11-12 19:59:18 +01:00
Zoltán Vörös
50df00546b initialised deg with 0 in poly.c 2019-11-09 17:54:17 +01:00
Zoltán Vörös
6e0fcd0f6f rectified small compiler complaint in ndarray.c, and poly.c 2019-11-09 17:48:08 +01:00
Zoltán Vörös
20e97ce7c7 rectified small compiler complaint in ndarray.c, and poly.c 2019-11-09 17:46:55 +01:00
Zoltán Vörös
03609727ce removed test.c from micropython.mk 2019-11-09 10:52:23 +01:00
Zoltán Vörös
f34cbe885d removed test.c from micropython.mk 2019-11-09 10:50:35 +01:00
Zoltán Vörös
6e4f691a1a removed extraneous paranthesis 2019-11-08 22:28:28 +01:00
Zoltán Vörös
b62ba12237 replaced abs with MICROPY_FLOAT_C_FUN(fabs) 2019-11-08 22:19:52 +01:00
Zoltán Vörös
a2bafa981d simplified numerical sum/std/mean functions, added boolean type 2019-11-08 20:08:45 +01:00
Zoltán Vörös
039d7ec866 simplified numerical sum/std/mean functions, added boolean type 2019-11-08 19:52:43 +01:00
10 changed files with 888 additions and 543 deletions

View file

@ -124,7 +124,7 @@ bool linalg_invert_matrix(mp_float_t *data, size_t N) {
}
for(size_t m=0; m < N; m++){
// this could be faster with ((c < epsilon) && (c > -epsilon))
if(abs(data[m*(N+1)]) < epsilon) {
if(MICROPY_FLOAT_C_FUN(fabs)(data[m*(N+1)]) < epsilon) {
m_del(mp_float_t, unit, N*N);
return false;
}
@ -307,7 +307,7 @@ mp_obj_t linalg_det(mp_obj_t oin) {
}
mp_float_t c;
for(size_t m=0; m < in->m-1; m++){
if(abs(tmp[m*(in->n+1)]) < epsilon) {
if(MICROPY_FLOAT_C_FUN(fabs)(tmp[m*(in->n+1)]) < epsilon) {
m_del(mp_float_t, tmp, in->n*in->n);
return mp_obj_new_float(0.0);
}
@ -346,7 +346,7 @@ mp_obj_t linalg_eig(mp_obj_t oin) {
for(size_t n=m+1; n < in->n; n++) {
// compare entry (m, n) to (n, m)
// TODO: this must probably be scaled!
if(epsilon < abs(array[m*in->n + n] - array[n*in->n + m])) {
if(epsilon < MICROPY_FLOAT_C_FUN(fabs)(array[m*in->n + n] - array[n*in->n + m])) {
mp_raise_ValueError("input matrix is asymmetric");
}
}
@ -371,7 +371,7 @@ mp_obj_t linalg_eig(mp_obj_t oin) {
largest = 0.0;
for(size_t m=0; m < in->m-1; m++) { // -1: no need to inspect last row
for(size_t n=m+1; n < in->n; n++) {
w = fabs(array[m*in->n + n]);
w = MICROPY_FLOAT_C_FUN(fabs)(array[m*in->n + n]);
if((largest < w) && (epsilon < w)) {
M = m;
N = n;

View file

@ -61,26 +61,40 @@ void fill_array_iterable(mp_float_t *array, mp_obj_t iterable) {
}
}
void ndarray_print_row(const mp_print_t *print, mp_obj_array_t *data, size_t n0, size_t n) {
void ndarray_print_value(const mp_print_t *print, mp_obj_array_t *data,
size_t n, uint8_t boolean) {
if(!boolean) {
mp_obj_print_helper(print, mp_binary_get_val_array(data->typecode, data->items, n), PRINT_REPR);
} else { // the array is a Boolean array, it must be of uint8_t type
if(((uint8_t *)data->items)[n]) {
mp_print_str(print, "True");
} else {
mp_print_str(print, "False");
}
}
}
void ndarray_print_row(const mp_print_t *print, mp_obj_array_t *data,
size_t n0, size_t n, uint8_t boolean) {
mp_print_str(print, "[");
size_t i;
if(n < PRINT_MAX) { // if the array is short, print everything
mp_obj_print_helper(print, mp_binary_get_val_array(data->typecode, data->items, n0), PRINT_REPR);
ndarray_print_value(print, data, n0, boolean);
for(i=1; i<n; i++) {
mp_print_str(print, ", ");
mp_obj_print_helper(print, mp_binary_get_val_array(data->typecode, data->items, n0+i), PRINT_REPR);
ndarray_print_value(print, data, n0+i, boolean);
}
} else {
mp_obj_print_helper(print, mp_binary_get_val_array(data->typecode, data->items, n0), PRINT_REPR);
ndarray_print_value(print, data, n0, boolean);
for(i=1; i<3; i++) {
mp_print_str(print, ", ");
mp_obj_print_helper(print, mp_binary_get_val_array(data->typecode, data->items, n0+i), PRINT_REPR);
ndarray_print_value(print, data, n0+i, boolean);
}
mp_printf(print, ", ..., ");
mp_obj_print_helper(print, mp_binary_get_val_array(data->typecode, data->items, n0+n-3), PRINT_REPR);
ndarray_print_value(print, data, n0-3, boolean);
for(size_t i=1; i<3; i++) {
mp_print_str(print, ", ");
mp_obj_print_helper(print, mp_binary_get_val_array(data->typecode, data->items, n0+n-3+i), PRINT_REPR);
ndarray_print_value(print, data, n0-3+i, boolean);
}
}
mp_print_str(print, "]");
@ -95,19 +109,21 @@ void ndarray_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t ki
mp_print_str(print, "[]");
} else {
if((self->m == 1) || (self->n == 1)) {
ndarray_print_row(print, self->array, 0, self->array->len);
ndarray_print_row(print, self->array, 0, self->array->len, self->boolean);
} else {
// TODO: add vertical ellipses for the case, when self->m > PRINT_MAX
mp_print_str(print, "[");
ndarray_print_row(print, self->array, 0, self->n);
ndarray_print_row(print, self->array, 0, self->n, self->boolean);
for(size_t i=1; i < self->m; i++) {
mp_print_str(print, ",\n\t ");
ndarray_print_row(print, self->array, i*self->n, self->n);
ndarray_print_row(print, self->array, i*self->n, self->n, self->boolean);
}
mp_print_str(print, "]");
}
}
if(self->array->typecode == NDARRAY_UINT8) {
if(self->boolean) {
mp_print_str(print, ", dtype=bool)");
} else if(self->array->typecode == NDARRAY_UINT8) {
mp_print_str(print, ", dtype=uint8)");
} else if(self->array->typecode == NDARRAY_INT8) {
mp_print_str(print, ", dtype=int8)");
@ -120,11 +136,22 @@ void ndarray_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t ki
}
}
void ndarray_assign_elements(mp_obj_array_t *data, mp_obj_t iterable, uint8_t typecode, size_t *idx) {
void ndarray_assign_row(mp_obj_array_t *data, mp_obj_t iterable, uint8_t typecode, size_t *idx) {
// assigns a single row in the matrix
mp_obj_t item;
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
mp_binary_set_val_array(typecode, data->items, (*idx)++, item);
if(typecode == NDARRAY_BOOL) {
uint8_t *array = (uint8_t *)data->items;
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
if(mp_obj_is_true(item)) {
array[(*idx)] = 1;
}
// we don't need to set False, because the array is initialised with straight zeros
(*idx)++;
}
} else {
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
mp_binary_set_val_array(typecode, data->items, (*idx)++, item);
}
}
}
@ -134,7 +161,13 @@ ndarray_obj_t *create_new_ndarray(size_t m, size_t n, uint8_t typecode) {
ndarray->base.type = &ulab_ndarray_type;
ndarray->m = m;
ndarray->n = n;
mp_obj_array_t *array = array_new(typecode, m*n);
if(typecode == NDARRAY_BOOL) {
typecode = NDARRAY_UINT8;
ndarray->boolean = NDARRAY_BOOLEAN;
} else {
ndarray->boolean = NDARRAY_NUMERIC;
}
mp_obj_array_t *array = array_new(typecode, m*n);
ndarray->bytes = m * n * mp_binary_get_size('@', typecode, NULL);
// this should set all elements to 0, irrespective of the of the typecode (all bits are zero)
// we could, perhaps, leave this step out, and initialise the array only, when needed
@ -161,6 +194,7 @@ STATIC uint8_t ndarray_init_helper(size_t n_args, const mp_obj_t *pos_args, mp_m
mp_arg_parse_all(1, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
uint8_t dtype = args[1].u_int;
// at this point, dtype can still be `?` for Boolean arrays
return dtype;
}
@ -170,7 +204,7 @@ mp_obj_t ndarray_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
uint8_t dtype = ndarray_init_helper(n_args, args, &kw_args);
size_t len1, len2=0, i=0;
size_t len1, len2 = 0, i = 0;
mp_obj_t len_in = mp_obj_len_maybe(args[0]);
if (len_in == MP_OBJ_NULL) {
mp_raise_ValueError("first argument must be an iterable");
@ -201,14 +235,14 @@ mp_obj_t ndarray_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
iterable1 = mp_getiter(args[0], &iter_buf1);
i = 0;
if(len2 == 0) { // the first argument is a single iterable
ndarray_assign_elements(self->array, iterable1, dtype, &i);
ndarray_assign_row(self->array, iterable1, dtype, &i);
} else {
mp_obj_iter_buf_t iter_buf2;
mp_obj_t iterable2;
while ((item1 = mp_iternext(iterable1)) != MP_OBJ_STOP_ITERATION) {
iterable2 = mp_getiter(item1, &iter_buf2);
ndarray_assign_elements(self->array, iterable2, dtype, &i);
ndarray_assign_row(self->array, iterable2, dtype, &i);
}
}
return MP_OBJ_FROM_PTR(self);
@ -277,7 +311,7 @@ void insert_binary_value(ndarray_obj_t *ndarray, size_t nd_index, ndarray_obj_t
// there is probably a more elegant implementation...
mp_obj_t tmp = mp_binary_get_val_array(values->array->typecode, values->array->items, value_index);
if((values->array->typecode == NDARRAY_FLOAT) && (ndarray->array->typecode != NDARRAY_FLOAT)) {
// workaround: rounding seems not to work in the arm compiler
// this is a workaround: rounding seems not to work in the arm compiler
int32_t x = (int32_t)floorf(mp_obj_get_float(tmp)+0.5);
tmp = mp_obj_new_int(x);
}
@ -580,7 +614,7 @@ mp_obj_t ndarray_iternext(mp_obj_t self_in) {
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(self->ndarray);
// TODO: in numpy, ndarrays are iterated with respect to the first axis.
size_t iter_end = 0;
if((ndarray->m == 1)) {
if(ndarray->m == 1) {
iter_end = ndarray->array->len;
} else {
iter_end = ndarray->m;
@ -888,12 +922,12 @@ mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
return ndarray_copy(self_in);
}
ndarray = MP_OBJ_TO_PTR(ndarray_copy(self_in));
if((self->array->typecode == NDARRAY_INT8)) {
if(self->array->typecode == NDARRAY_INT8) {
int8_t *array = (int8_t *)ndarray->array->items;
for(size_t i=0; i < self->array->len; i++) {
if(array[i] < 0) array[i] = -array[i];
}
} else if((self->array->typecode == NDARRAY_INT16)) {
} else if(self->array->typecode == NDARRAY_INT16) {
int16_t *array = (int16_t *)ndarray->array->items;
for(size_t i=0; i < self->array->len; i++) {
if(array[i] < 0) array[i] = -array[i];

View file

@ -18,6 +18,9 @@
#define PRINT_MAX 10
#define NDARRAY_NUMERIC 0
#define NDARRAY_BOOLEAN 1
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
#define FLOAT_TYPECODE 'f'
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
@ -27,6 +30,7 @@
const mp_obj_type_t ulab_ndarray_type;
enum NDARRAY_TYPE {
NDARRAY_BOOL = '?', // this must never be assigned to the typecode!
NDARRAY_UINT8 = 'B',
NDARRAY_INT8 = 'b',
NDARRAY_UINT16 = 'H',
@ -36,6 +40,7 @@ enum NDARRAY_TYPE {
typedef struct _ndarray_obj_t {
mp_obj_base_t base;
uint8_t boolean;
size_t m, n;
size_t len;
mp_obj_array_t *array;
@ -47,9 +52,7 @@ mp_obj_t mp_obj_new_ndarray_iterator(mp_obj_t , size_t , mp_obj_iter_buf_t *);
mp_float_t ndarray_get_float_value(void *, uint8_t , size_t );
void fill_array_iterable(mp_float_t *, mp_obj_t );
void ndarray_print_row(const mp_print_t *, mp_obj_array_t *, size_t , size_t );
void ndarray_print(const mp_print_t *, mp_obj_t , mp_print_kind_t );
void ndarray_assign_elements(mp_obj_array_t *, mp_obj_t , uint8_t , size_t *);
ndarray_obj_t *create_new_ndarray(size_t , size_t , uint8_t );
mp_obj_t ndarray_copy(mp_obj_t );
@ -97,27 +100,25 @@ mp_obj_t ndarray_asbytearray(mp_obj_t );
return MP_OBJ_FROM_PTR(out);\
} else if(((op) == MP_BINARY_OP_LESS) || ((op) == MP_BINARY_OP_LESS_EQUAL) || \
((op) == MP_BINARY_OP_MORE) || ((op) == MP_BINARY_OP_MORE_EQUAL)) {\
mp_obj_t out_list = mp_obj_new_list(0, NULL);\
size_t m = (ol)->m, n = (ol)->n;\
ndarray_obj_t *out = create_new_ndarray((ol)->m, (ol)->n, NDARRAY_UINT8);\
size_t m = out->m, n = out->n;\
out->boolean = NDARRAY_BOOLEAN;\
uint8_t *barray = (uint8_t *)out->array->items;\
for(size_t i=0, r=0; i < m; i++, r+=inc) {\
mp_obj_t row = mp_obj_new_list(n, NULL);\
mp_obj_list_t *row_ptr = MP_OBJ_TO_PTR(row);\
for(size_t j=0, s=0; j < n; j++, s+=inc) {\
row_ptr->items[j] = mp_const_false;\
barray[i*n + j] = 0;\
if((op) == MP_BINARY_OP_LESS) {\
if(left[i*n+j] < right[r*n+s]) row_ptr->items[j] = mp_const_true;\
if(left[i*n+j] < right[r*n+s]) barray[i*n + j] = 1;\
} else if((op) == MP_BINARY_OP_LESS_EQUAL) {\
if(left[i*n+j] <= right[r*n+s]) row_ptr->items[j] = mp_const_true;\
if(left[i*n+j] <= right[r*n+s]) barray[i*n + j] = 1;\
} else if((op) == MP_BINARY_OP_MORE) {\
if(left[i*n+j] > right[r*n+s]) row_ptr->items[j] = mp_const_true;\
if(left[i*n+j] > right[r*n+s]) barray[i*n + j] = 1;\
} else if((op) == MP_BINARY_OP_MORE_EQUAL) {\
if(left[i*n+j] >= right[r*n+s]) row_ptr->items[j] = mp_const_true;\
if(left[i*n+j] >= right[r*n+s]) barray[i*n + j] = 1;\
}\
}\
if(m == 1) return row;\
mp_obj_list_append(out_list, row);\
}\
return out_list;\
return MP_OBJ_FROM_PTR(out);\
}\
} while(0)

View file

@ -77,7 +77,33 @@ mp_obj_t numerical_linspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k
}
}
mp_obj_t numerical_sum_mean_std_array(mp_obj_t oin, uint8_t optype) {
void axis_sorter(ndarray_obj_t *ndarray, mp_obj_t axis, size_t *m, size_t *n, size_t *N,
size_t *increment, size_t *len, size_t *start_inc) {
if(axis == mp_const_none) { // flatten the array
*m = 1;
*n = 1;
*len = ndarray->array->len;
*N = 1;
*increment = 1;
*start_inc = ndarray->array->len;
} else if((mp_obj_get_int(axis) == 1)) { // along the horizontal axis
*m = ndarray->m;
*n = 1;
*len = ndarray->n;
*N = ndarray->m;
*increment = 1;
*start_inc = ndarray->n;
} else { // along vertical axis
*m = 1;
*n = ndarray->n;
*len = ndarray->m;
*N = ndarray->n;
*increment = ndarray->n;
*start_inc = 1;
}
}
mp_obj_t numerical_sum_mean_std_iterable(mp_obj_t oin, uint8_t optype, size_t ddof) {
mp_float_t value, sum = 0.0, sq_sum = 0.0;
mp_obj_iter_buf_t iter_buf;
mp_obj_t item, iterable = mp_getiter(oin, &iter_buf);
@ -85,194 +111,153 @@ mp_obj_t numerical_sum_mean_std_array(mp_obj_t oin, uint8_t optype) {
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
value = mp_obj_get_float(item);
sum += value;
if(optype == NUMERICAL_STD) {
sq_sum += value*value;
}
}
if(optype == NUMERICAL_SUM) {
return mp_obj_new_float(sum);
} else if(optype == NUMERICAL_MEAN) {
return mp_obj_new_float(sum/len);
} else {
} else { // this should be the case of the standard deviation
// TODO: note that we could get away with a single pass, if we used the Weldorf algorithm
// That should save a fair amount of time, because we would have to extract the values only once
iterable = mp_getiter(oin, &iter_buf);
sum /= len; // this is now the mean!
return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(sq_sum/len-sum*sum));
}
}
STATIC mp_float_t numerical_sum_mean_std_single_line(void *data, size_t start, size_t stop,
size_t stride, uint8_t typecode, uint8_t optype) {
mp_float_t sum = 0.0, sq_sum = 0.0, value;
size_t len = 0;
for(size_t i=start; i < stop; i+=stride, len++) {
value = ndarray_get_float_value(data, typecode, i);
sum += value;
if(optype == NUMERICAL_STD) {
sq_sum += value*value;
}
}
if(len == 0) {
mp_raise_ValueError("data length is 0!");
}
if(optype == NUMERICAL_SUM) {
return sum;
} else if(optype == NUMERICAL_MEAN) {
return sum/len;
} else {
sum /= len; // this is now the mean!
return MICROPY_FLOAT_C_FUN(sqrt)(sq_sum/len-sum*sum);
}
}
STATIC mp_obj_t numerical_sum_mean_std_matrix(mp_obj_t oin, mp_obj_t axis, uint8_t optype) {
ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);
if((axis == mp_const_none) || (in->m == 1) || (in->n == 1)) {
// return the value for the flattened array
return mp_obj_new_float(numerical_sum_mean_std_single_line(in->array->items, 0,
in->array->len, 1, in->array->typecode, optype));
} else {
uint8_t _axis = mp_obj_get_int(axis);
size_t m = (_axis == 0) ? 1 : in->m;
size_t n = (_axis == 0) ? in->n : 1;
size_t len = in->array->len;
mp_float_t sms;
// TODO: pass in->array->typcode to create_new_ndarray
ndarray_obj_t *out = create_new_ndarray(m, n, NDARRAY_FLOAT);
// TODO: these two cases could probably be combined in a more elegant fashion...
if(_axis == 0) { // vertical
for(size_t i=0; i < n; i++) {
sms = numerical_sum_mean_std_single_line(in->array->items, i, len,
n, in->array->typecode, optype);
((float_t *)out->array->items)[i] = sms;
}
} else { // horizontal
for(size_t i=0; i < m; i++) {
sms = numerical_sum_mean_std_single_line(in->array->items, i*in->n,
(i+1)*in->n, 1, in->array->typecode, optype);
((float_t *)out->array->items)[i] = sms;
}
}
return MP_OBJ_FROM_PTR(out);
}
}
size_t numerical_argmin_argmax_array(ndarray_obj_t *in, size_t start,
size_t stop, size_t stride, uint8_t op) {
size_t best_idx = start;
if(in->array->typecode == NDARRAY_UINT8) {
ARG_MIN_LOOP(in, uint8_t, start, stop, stride, op);
} else if(in->array->typecode == NDARRAY_INT8) {
ARG_MIN_LOOP(in, int8_t, start, stop, stride, op);
} else if(in->array->typecode == NDARRAY_UINT16) {
ARG_MIN_LOOP(in, uint16_t, start, stop, stride, op);
} else if(in->array->typecode == NDARRAY_INT16) {
ARG_MIN_LOOP(in, uint16_t, start, stop, stride, op);
} else if(in->array->typecode == NDARRAY_FLOAT) {
ARG_MIN_LOOP(in, mp_float_t, start, stop, stride, op);
}
return best_idx;
}
void copy_value_into_ndarray(ndarray_obj_t *target, ndarray_obj_t *source, size_t target_idx, size_t source_idx) {
// since we are simply copying, it doesn't matter, whether the arrays are signed or unsigned,
// we can cast them in any way we like
// This could also be done with byte copies. I don't know, whether that would have any benefits
if((target->array->typecode == NDARRAY_UINT8) || (target->array->typecode == NDARRAY_INT8)) {
((uint8_t *)target->array->items)[target_idx] = ((uint8_t *)source->array->items)[source_idx];
} else if((target->array->typecode == NDARRAY_UINT16) || (target->array->typecode == NDARRAY_INT16)) {
((uint16_t *)target->array->items)[target_idx] = ((uint16_t *)source->array->items)[source_idx];
} else {
((float *)target->array->items)[target_idx] = ((float *)source->array->items)[source_idx];
}
}
STATIC mp_obj_t numerical_argmin_argmax(mp_obj_t oin, mp_obj_t axis, uint8_t optype) {
if(MP_OBJ_IS_TYPE(oin, &mp_type_tuple) || MP_OBJ_IS_TYPE(oin, &mp_type_list) ||
MP_OBJ_IS_TYPE(oin, &mp_type_range)) {
// This case will work for single iterables only
size_t idx = 0, best_idx = 0;
mp_obj_iter_buf_t iter_buf;
mp_obj_t iterable = mp_getiter(oin, &iter_buf);
mp_obj_t best_obj = MP_OBJ_NULL;
mp_obj_t item;
mp_uint_t op = MP_BINARY_OP_LESS;
if((optype == NUMERICAL_ARGMAX) || (optype == NUMERICAL_MAX)) op = MP_BINARY_OP_MORE;
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
if ((best_obj == MP_OBJ_NULL) || (mp_binary_op(op, item, best_obj) == mp_const_true)) {
best_obj = item;
best_idx = idx;
}
idx++;
value = mp_obj_get_float(item) - sum;
sq_sum += value * value;
}
if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) {
return MP_OBJ_NEW_SMALL_INT(best_idx);
} else {
return best_obj;
}
} else if(mp_obj_is_type(oin, &ulab_ndarray_type)) {
ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);
size_t best_idx;
if((axis == mp_const_none) || (in->m == 1) || (in->n == 1)) {
// return the value for the flattened array
best_idx = numerical_argmin_argmax_array(in, 0, in->array->len, 1, optype);
if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) {
return MP_OBJ_NEW_SMALL_INT(best_idx);
} else {
if(in->array->typecode == NDARRAY_FLOAT) {
return mp_obj_new_float(ndarray_get_float_value(in->array->items, in->array->typecode, best_idx));
} else {
return mp_binary_get_val_array(in->array->typecode, in->array->items, best_idx);
}
}
} else { // we have to work with a full matrix here
uint8_t _axis = mp_obj_get_int(axis);
size_t m = (_axis == 0) ? 1 : in->m;
size_t n = (_axis == 0) ? in->n : 1;
size_t len = in->array->len;
ndarray_obj_t *ndarray = NULL;
if((optype == NUMERICAL_MAX) || (optype == NUMERICAL_MIN)) {
ndarray = create_new_ndarray(m, n, in->array->typecode);
} else { // argmin/argmax
// TODO: one might get away with uint8_t, if both m, and n < 255
ndarray = create_new_ndarray(m, n, NDARRAY_UINT16);
}
// TODO: these two cases could probably be combined in a more elegant fashion...
if(_axis == 0) { // vertical
for(size_t i=0; i < n; i++) {
best_idx = numerical_argmin_argmax_array(in, i, len, n, optype);
if((optype == NUMERICAL_MIN) || (optype == NUMERICAL_MAX)) {
copy_value_into_ndarray(ndarray, in, i, best_idx);
} else {
((uint16_t *)ndarray->array->items)[i] = (uint16_t)(best_idx / n);
}
}
} else { // horizontal
for(size_t i=0; i < m; i++) {
best_idx = numerical_argmin_argmax_array(in, i*in->n, (i+1)*in->n, 1, optype);
if((optype == NUMERICAL_MIN) || (optype == NUMERICAL_MAX)) {
copy_value_into_ndarray(ndarray, in, i, best_idx);
} else {
((uint16_t *)ndarray->array->items)[i] = (uint16_t)(best_idx - i*in->n);
}
}
}
return MP_OBJ_FROM_PTR(ndarray);
}
return mp_const_none;
}
mp_raise_TypeError("input type is not supported");
return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(sq_sum/(len-ddof)));
}
}
STATIC mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, uint8_t type) {
STATIC mp_obj_t numerical_sum_mean_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, uint8_t optype) {
size_t m, n, increment, start, start_inc, N, len;
axis_sorter(ndarray, axis, &m, &n, &N, &increment, &len, &start_inc);
ndarray_obj_t *results = create_new_ndarray(m, n, NDARRAY_FLOAT);
mp_float_t sum, sq_sum;
mp_float_t *farray = (mp_float_t *)results->array->items;
for(size_t j=0; j < N; j++) { // result index
start = j * start_inc;
sum = sq_sum = 0.0;
if(ndarray->array->typecode == NDARRAY_UINT8) {
RUN_SUM(ndarray, uint8_t, optype, len, start, increment);
} else if(ndarray->array->typecode == NDARRAY_INT8) {
RUN_SUM(ndarray, int8_t, optype, len, start, increment);
} else if(ndarray->array->typecode == NDARRAY_UINT16) {
RUN_SUM(ndarray, uint16_t, optype, len, start, increment);
} else if(ndarray->array->typecode == NDARRAY_INT16) {
RUN_SUM(ndarray, int16_t, optype, len, start, increment);
} else { // this will be mp_float_t, no need to check
RUN_SUM(ndarray, mp_float_t, optype, len, start, increment);
}
if(optype == NUMERICAL_SUM) {
farray[j] = sum;
} else { // this is the case of the mean
farray[j] = sum / len;
}
}
if(results->array->len == 1) {
return mp_obj_new_float(farray[0]);
}
return MP_OBJ_FROM_PTR(results);
}
mp_obj_t numerical_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, size_t ddof) {
size_t m, n, increment, start, start_inc, N, len;
mp_float_t sum, sum_sq;
axis_sorter(ndarray, axis, &m, &n, &N, &increment, &len, &start_inc);
if(ddof > len) {
mp_raise_ValueError("ddof must be smaller than length of data set");
}
ndarray_obj_t *results = create_new_ndarray(m, n, NDARRAY_FLOAT);
mp_float_t *farray = (mp_float_t *)results->array->items;
for(size_t j=0; j < N; j++) { // result index
start = j * start_inc;
sum = 0.0;
sum_sq = 0.0;
if(ndarray->array->typecode == NDARRAY_UINT8) {
RUN_STD(ndarray, uint8_t, len, start, increment);
} else if(ndarray->array->typecode == NDARRAY_INT8) {
RUN_STD(ndarray, int8_t, len, start, increment);
} else if(ndarray->array->typecode == NDARRAY_UINT16) {
RUN_STD(ndarray, uint16_t, len, start, increment);
} else if(ndarray->array->typecode == NDARRAY_INT16) {
RUN_STD(ndarray, int16_t, len, start, increment);
} else { // this will be mp_float_t, no need to check
RUN_STD(ndarray, mp_float_t, len, start, increment);
}
farray[j] = MICROPY_FLOAT_C_FUN(sqrt)(sum_sq/(len - ddof));
}
if(results->array->len == 1) {
return mp_obj_new_float(farray[0]);
}
return MP_OBJ_FROM_PTR(results);
}
mp_obj_t numerical_argmin_argmax_iterable(mp_obj_t oin, mp_obj_t axis, uint8_t optype) {
size_t idx = 0, best_idx = 0;
mp_obj_iter_buf_t iter_buf;
mp_obj_t iterable = mp_getiter(oin, &iter_buf);
mp_obj_t best_obj = MP_OBJ_NULL;
mp_obj_t item;
mp_uint_t op = MP_BINARY_OP_LESS;
if((optype == NUMERICAL_ARGMAX) || (optype == NUMERICAL_MAX)) op = MP_BINARY_OP_MORE;
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
if ((best_obj == MP_OBJ_NULL) || (mp_binary_op(op, item, best_obj) == mp_const_true)) {
best_obj = item;
best_idx = idx;
}
idx++;
}
if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) {
return MP_OBJ_NEW_SMALL_INT(best_idx);
} else {
return best_obj;
}
}
mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, uint8_t optype) {
size_t m, n, increment, start, start_inc, N, len;
axis_sorter(ndarray, axis, &m, &n, &N, &increment, &len, &start_inc);
ndarray_obj_t *results;
if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) {
// we could save some RAM by taking NDARRAY_UINT8, if the dimensions
// are smaller than 256, but the code would become more involving
// (we would also need extra flash space)
results = create_new_ndarray(m, n, NDARRAY_UINT16);
} else {
results = create_new_ndarray(m, n, ndarray->array->typecode);
}
for(size_t j=0; j < N; j++) { // result index
start = j * start_inc;
if((ndarray->array->typecode == NDARRAY_UINT8) || (ndarray->array->typecode == NDARRAY_INT8)) {
if((optype == NUMERICAL_MAX) || (optype == NUMERICAL_MIN)) {
RUN_ARGMIN(ndarray, results, uint8_t, uint8_t, len, start, increment, optype, j);
} else {
RUN_ARGMIN(ndarray, results, uint8_t, uint16_t, len, start, increment, optype, j);
}
} else if((ndarray->array->typecode == NDARRAY_UINT16) || (ndarray->array->typecode == NDARRAY_INT16)) {
RUN_ARGMIN(ndarray, results, uint16_t, uint16_t, len, start, increment, optype, j);
} else {
if((optype == NUMERICAL_MAX) || (optype == NUMERICAL_MIN)) {
RUN_ARGMIN(ndarray, results, mp_float_t, mp_float_t, len, start, increment, optype, j);
} else {
RUN_ARGMIN(ndarray, results, mp_float_t, uint16_t, len, start, increment, optype, j);
}
}
}
return MP_OBJ_FROM_PTR(results);
}
STATIC mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, uint8_t optype) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} } ,
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
{ MP_QSTR_axis, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(1, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mp_obj_t oin = args[0].u_obj;
mp_obj_t axis = args[1].u_obj;
@ -283,30 +268,29 @@ STATIC mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m
if(MP_OBJ_IS_TYPE(oin, &mp_type_tuple) || MP_OBJ_IS_TYPE(oin, &mp_type_list) ||
MP_OBJ_IS_TYPE(oin, &mp_type_range)) {
switch(type) {
switch(optype) {
case NUMERICAL_MIN:
case NUMERICAL_ARGMIN:
case NUMERICAL_MAX:
case NUMERICAL_ARGMAX:
return numerical_argmin_argmax(oin, axis, type);
return numerical_argmin_argmax_iterable(oin, axis, optype);
case NUMERICAL_SUM:
case NUMERICAL_MEAN:
case NUMERICAL_STD:
return numerical_sum_mean_std_array(oin, type);
return numerical_sum_mean_std_iterable(oin, optype, 0);
default: // we should never reach this point, but whatever
return mp_const_none;
}
} else if(MP_OBJ_IS_TYPE(oin, &ulab_ndarray_type)) {
switch(type) {
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin);
switch(optype) {
case NUMERICAL_MIN:
case NUMERICAL_MAX:
case NUMERICAL_ARGMIN:
case NUMERICAL_ARGMAX:
return numerical_argmin_argmax(oin, axis, type);
return numerical_argmin_argmax_ndarray(ndarray, axis, optype);
case NUMERICAL_SUM:
case NUMERICAL_MEAN:
case NUMERICAL_STD:
return numerical_sum_mean_std_matrix(oin, axis, type);
return numerical_sum_mean_ndarray(ndarray, axis, optype);
default:
mp_raise_NotImplementedError("operation is not implemented on ndarrays");
}
@ -341,7 +325,31 @@ mp_obj_t numerical_mean(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
}
mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
return numerical_function(n_args, pos_args, kw_args, NUMERICAL_STD);
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} } ,
{ MP_QSTR_axis, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
{ MP_QSTR_ddof, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mp_obj_t oin = args[0].u_obj;
mp_obj_t axis = args[1].u_obj;
size_t ddof = args[2].u_int;
if((axis != mp_const_none) && (mp_obj_get_int(axis) != 0) && (mp_obj_get_int(axis) != 1)) {
// this seems to pass with False, and True...
mp_raise_ValueError("axis must be None, 0, or 1");
}
if(MP_OBJ_IS_TYPE(oin, &mp_type_tuple) || MP_OBJ_IS_TYPE(oin, &mp_type_list) || MP_OBJ_IS_TYPE(oin, &mp_type_range)) {
return numerical_sum_mean_std_iterable(oin, NUMERICAL_STD, ddof);
} else if(MP_OBJ_IS_TYPE(oin, &ulab_ndarray_type)) {
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin);
return numerical_std_ndarray(ndarray, axis, ddof);
} else {
mp_raise_TypeError("input must be tuple, list, range, or ndarray");
}
return mp_const_none;
}
mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {

View file

@ -33,19 +33,44 @@ mp_obj_t numerical_sort(size_t , const mp_obj_t *, mp_map_t *);
mp_obj_t numerical_sort_inplace(size_t , const mp_obj_t *, mp_map_t *);
mp_obj_t numerical_argsort(size_t , const mp_obj_t *, mp_map_t *);
// this macro could be tighter, if we moved the ifs to the argmin function, assigned <, as well as >
#define ARG_MIN_LOOP(in, type, start, stop, stride, op) do {\
type *array = (type *)(in)->array->items;\
#define RUN_ARGMIN(in, out, typein, typeout, len, start, increment, op, pos) do {\
typein *array = (typein *)(in)->array->items;\
typeout *outarray = (typeout *)(out)->array->items;\
size_t best_index = 0;\
if(((op) == NUMERICAL_MAX) || ((op) == NUMERICAL_ARGMAX)) {\
for(size_t i=(start)+(stride); i < (stop); i+=(stride)) {\
if((array)[i] > (array)[best_idx]) {\
best_idx = i;\
}\
for(size_t i=1; i < (len); i++) {\
if(array[(start)+i*(increment)] > array[(start)+best_index*(increment)]) best_index = i;\
}\
if((op) == NUMERICAL_MAX) outarray[(pos)] = array[(start)+best_index*(increment)];\
else outarray[(pos)] = best_index;\
} else{\
for(size_t i=(start)+(stride); i < (stop); i+=(stride)) {\
if((array)[i] < (array)[best_idx]) best_idx = i;\
for(size_t i=1; i < (len); i++) {\
if(array[(start)+i*(increment)] < array[(start)+best_index*(increment)]) best_index = i;\
}\
if((op) == NUMERICAL_MIN) outarray[(pos)] = array[(start)+best_index*(increment)];\
else outarray[(pos)] = best_index;\
}\
} while(0)
#define RUN_SUM(ndarray, type, optype, len, start, increment) do {\
type *array = (type *)(ndarray)->array->items;\
type value;\
for(size_t j=0; j < (len); j++) {\
value = array[(start)+j*(increment)];\
sum += value;\
}\
} while(0)
#define RUN_STD(ndarray, type, len, start, increment) do {\
type *array = (type *)(ndarray)->array->items;\
mp_float_t value;\
for(size_t j=0; j < (len); j++) {\
sum += array[(start)+j*(increment)];\
}\
sum /= (len);\
for(size_t j=0; j < (len); j++) {\
value = (array[(start)+j*(increment)] - sum);\
sum_sq += value * value;\
}\
} while(0)

View file

@ -89,8 +89,8 @@ mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) {
if(!object_is_nditerable(args[0])) {
mp_raise_ValueError("input data must be an iterable");
}
uint16_t lenx, leny;
uint8_t deg;
uint16_t lenx = 0, leny = 0;
uint8_t deg = 0;
mp_float_t *x, *XT, *y, *prod;
if(n_args == 2) { // only the y values are supplied
@ -108,7 +108,7 @@ mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) {
}
y = m_new(mp_float_t, leny);
fill_array_iterable(y, args[0]);
} else if(n_args == 3) {
} else { // n_args == 3
lenx = (uint16_t)mp_obj_get_int(mp_obj_len_maybe(args[0]));
leny = (uint16_t)mp_obj_get_int(mp_obj_len_maybe(args[0]));
if(lenx != leny) {

View file

@ -24,7 +24,7 @@
#include "fft.h"
#include "numerical.h"
#define ULAB_VERSION 0.26
#define ULAB_VERSION 0.27
typedef struct _mp_obj_float_t {
mp_obj_base_t base;
@ -173,6 +173,7 @@ STATIC const mp_map_elem_t ulab_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_ifft), (mp_obj_t)&fft_ifft_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_spectrum), (mp_obj_t)&fft_spectrum_obj },
// class constants
{ MP_ROM_QSTR(MP_QSTR_bool), MP_ROM_INT(NDARRAY_BOOL) },
{ MP_ROM_QSTR(MP_QSTR_uint8), MP_ROM_INT(NDARRAY_UINT8) },
{ MP_ROM_QSTR(MP_QSTR_int8), MP_ROM_INT(NDARRAY_INT8) },
{ MP_ROM_QSTR(MP_QSTR_uint16), MP_ROM_INT(NDARRAY_UINT16) },

View file

@ -1,4 +1,10 @@
Fri, 8 Nov 2019
version 0.27
cleaned up the sum/mean/std functions in numerical.c, and changed the output of relational operators
Tue, 6 Nov 2019
version 0.26

View file

@ -22,6 +22,65 @@
"%pylab inline"
]
},
{
"cell_type": "code",
"execution_count": 87,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-07T17:11:51.529183Z",
"start_time": "2019-11-07T17:11:51.522117Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"(array([ True, True, False, True]),\n",
" array([1, 1, 1, 1], dtype=int8),\n",
" dtype('int16'))"
]
},
"execution_count": 87,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a = array([1, 2, 3, 255], dtype=int16)\n",
"b = array([1, 2, 0, 1], dtype=bool)\n",
"c = array([1, 2, 1, 1], dtype=bool)\n",
"b, c**b, (a+b).dtype"
]
},
{
"cell_type": "code",
"execution_count": 100,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-08T05:52:22.454175Z",
"start_time": "2019-11-08T05:52:22.436159Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"((2,), (2, 4))"
]
},
"execution_count": 100,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a = array([[1, 2, 3, 4], [5, 6,7, 8]], dtype=uint8)\n",
"b = array([3, 2, 1, 0], dtype=uint8)\n",
"c = array([5, 6], dtype=uint8)\n",
"c = transpose(c)\n",
"c.shape, a.shape"
]
},
{
"cell_type": "code",
"execution_count": 23,
@ -120,10 +179,11 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 47,
"metadata": {
"ExecuteTime": {
"start_time": "2019-11-06T17:37:29.723Z"
"end_time": "2019-11-06T17:37:33.012936Z",
"start_time": "2019-11-06T17:37:29.726772Z"
}
},
"outputs": [],
@ -696,7 +756,7 @@
"\n",
"If the iterable is one-dimensional, i.e., one whose elements are numbers, then a row vector will be created and returned. If the iterable is two-dimensional, i.e., one whose elements are again iterables, a matrix will be created. If the lengths of the iterables is not consistent, a `ValueError` will be raised. Iterables of different types can be mixed in the initialisation function. \n",
"\n",
"If the `dtype` keyword with the possible `uint8/int8/uint16/int16/float` values is supplied, the new `ndarray` will have that type, otherwise, it assumes `float` as default. "
"If the `dtype` keyword with the possible `bool/uint8/int8/uint16/int16/float` values is supplied, the new `ndarray` will have that type, otherwise, it assumes `float` as default. "
]
},
{
@ -1522,31 +1582,29 @@
},
{
"cell_type": "code",
"execution_count": 188,
"execution_count": 116,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-11T18:59:35.810060Z",
"start_time": "2019-10-11T18:59:35.804683Z"
"end_time": "2019-11-14T06:33:04.536515Z",
"start_time": "2019-11-14T06:33:04.530675Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"array([[11, 22, 33],\n",
" [14, 25, 36],\n",
" [17, 28, 36]])"
"((24, 8), 8)"
]
},
"execution_count": 188,
"execution_count": 116,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a = array([[1, 2, 3], [4, 5, 6], [7, 8, 6]])\n",
"b = array([10, 20, 30])\n",
"a+b"
"a = array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n",
"b = a.reshape((9, ))\n",
"a.strides, a.itemsize"
]
},
{
@ -1559,16 +1617,23 @@
"\n",
"`ulab` observes the following upcasting rules:\n",
"\n",
"1. Operations with two `ndarray`s of the same `dtype` preserve their `dtype`, even when the results overflow.\n",
"1. If one of the operands is a `bool` array, the result inherits the `dtype` of the other.\n",
"\n",
"2. if either of the operands is a float, the result is automatically a float\n",
"2. Operations with two `ndarray`s of the same `dtype` preserve their `dtype`, even when the results overflow.\n",
"\n",
"3. When the right hand side of a binary operator is a micropython variable, `mp_obj_int`, or `mp_obj_float`, then the result will be promoted to `dtype` `float`. This is necessary, because a micropython integer can be 31 bites wide. Other micropython types (e.g., lists, tuples, etc.) raise a `TypeError` exception. \n",
"3. if either of the operands is a float, the result is automatically a float\n",
"\n",
"4. \n",
"4. When the right hand side of a binary operator is a micropython variable, `mp_obj_int`, or `mp_obj_float`, then the result will be promoted to a `dtype` that can still contain the numerical value, i.e., positive integers between 0, and 255 are converted to `dtype=uint8`, negative integers that are larger than -128 are converted to `dtype=int8` and so on. If the integer cannot fit into the constraints of `uint16/int16`, then the scalar will be converted to a `float`. Other micropython types (e.g., lists, tuples, etc.) raise a `TypeError` exception. \n",
"\n",
"5. \n",
" \n",
"| left hand side | right hand side | ulab result | numpy result |\n",
"|----------------|-----------------|-------------|--------------|\n",
"|`bool` |`bool` |`bool` |`bool` |\n",
"|`bool` |`uint8` |`uint8` |`uint8` |\n",
"|`bool` |`int8` |`int8` |`int8 ` |\n",
"|`bool` |`uint16` |`uint16` |`uint16` |\n",
"|`bool` |`int16` |`int16` |`int16` |\n",
"|`uint8` |`int8` |`int16` |`int16` |\n",
"|`uint8` |`int16` |`int16` |`int16` |\n",
"|`uint8` |`uint16` |`uint16` |`uint16` |\n",

File diff suppressed because it is too large Load diff