fixed glitch in std/mean functions
This commit is contained in:
parent
992e48b84e
commit
ac1111c251
5 changed files with 505 additions and 424 deletions
382
code/numerical.c
382
code/numerical.c
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
* Copyright (c) 2019-2020 Zoltán Vörös
|
||||
*/
|
||||
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -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;
|
||||
|
|
@ -281,32 +266,31 @@ STATIC mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_m
|
|||
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)) {
|
||||
switch(type) {
|
||||
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(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) {
|
||||
} else if(MP_OBJ_IS_TYPE(oin, &ulab_ndarray_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) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Zoltán Vörös
|
||||
* Copyright (c) 2019-2020 Zoltán Vörös
|
||||
*/
|
||||
|
||||
#ifndef _NUMERICAL_
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
#include "fft.h"
|
||||
#include "numerical.h"
|
||||
|
||||
STATIC MP_DEFINE_STR_OBJ(ulab_version_obj, "0.26.4");
|
||||
STATIC MP_DEFINE_STR_OBJ(ulab_version_obj, "0.26.5");
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_shape_obj, ndarray_shape);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_rawsize_obj, ndarray_rawsize);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
|
||||
Tue, 7 Jan 2020
|
||||
|
||||
version 0.26.5
|
||||
|
||||
fixed glitch in numerical.c, numerical.h
|
||||
|
||||
Mon, 6 Jan 2020
|
||||
|
||||
version 0.26.4
|
||||
|
||||
|
|
|
|||
493
docs/ulab.ipynb
493
docs/ulab.ipynb
|
|
@ -68,8 +68,8 @@
|
|||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2020-01-05T20:18:02.700773Z",
|
||||
"start_time": "2020-01-05T20:18:02.695726Z"
|
||||
"end_time": "2020-01-07T20:29:29.327256Z",
|
||||
"start_time": "2020-01-07T20:29:29.211644Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
|
|
@ -87,11 +87,11 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2020-01-05T20:18:30.766901Z",
|
||||
"start_time": "2020-01-05T20:18:30.759404Z"
|
||||
"end_time": "2020-01-07T20:29:31.701926Z",
|
||||
"start_time": "2020-01-07T20:29:31.698817Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
|
|
@ -134,11 +134,11 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2020-01-05T20:21:58.371317Z",
|
||||
"start_time": "2020-01-05T20:21:58.357586Z"
|
||||
"end_time": "2020-01-07T20:29:35.150872Z",
|
||||
"start_time": "2020-01-07T20:29:35.124059Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
|
|
@ -328,8 +328,8 @@
|
|||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2020-01-05T20:18:37.351688Z",
|
||||
"start_time": "2020-01-05T20:18:37.343794Z"
|
||||
"end_time": "2020-01-07T20:29:38.889453Z",
|
||||
"start_time": "2020-01-07T20:29:38.883666Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
|
|
@ -6965,11 +6965,11 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 157,
|
||||
"execution_count": 7,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2019-11-06T06:20:29.556126Z",
|
||||
"start_time": "2019-11-06T06:20:29.547543Z"
|
||||
"end_time": "2020-01-07T20:46:49.563652Z",
|
||||
"start_time": "2020-01-07T20:46:49.544742Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
|
|
@ -6977,7 +6977,7 @@
|
|||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"written 4779 bytes to numerical.h\n"
|
||||
"written 5721 bytes to numerical.h\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
@ -7009,19 +7009,44 @@
|
|||
"mp_obj_t numerical_sort_inplace(size_t , const mp_obj_t *, mp_map_t *);\n",
|
||||
"mp_obj_t numerical_argsort(size_t , const mp_obj_t *, mp_map_t *);\n",
|
||||
"\n",
|
||||
"// this macro could be tighter, if we moved the ifs to the argmin function, assigned <, as well as >\n",
|
||||
"#define ARG_MIN_LOOP(in, type, start, stop, stride, op) do {\\\n",
|
||||
" type *array = (type *)(in)->array->items;\\\n",
|
||||
"#define RUN_ARGMIN(in, out, typein, typeout, len, start, increment, op, pos) do {\\\n",
|
||||
" typein *array = (typein *)(in)->array->items;\\\n",
|
||||
" typeout *outarray = (typeout *)(out)->array->items;\\\n",
|
||||
" size_t best_index = 0;\\\n",
|
||||
" if(((op) == NUMERICAL_MAX) || ((op) == NUMERICAL_ARGMAX)) {\\\n",
|
||||
" for(size_t i=(start)+(stride); i < (stop); i+=(stride)) {\\\n",
|
||||
" if((array)[i] > (array)[best_idx]) {\\\n",
|
||||
" best_idx = i;\\\n",
|
||||
" }\\\n",
|
||||
" for(size_t i=1; i < (len); i++) {\\\n",
|
||||
" if(array[(start)+i*(increment)] > array[(start)+best_index*(increment)]) best_index = i;\\\n",
|
||||
" }\\\n",
|
||||
" if((op) == NUMERICAL_MAX) outarray[(pos)] = array[(start)+best_index*(increment)];\\\n",
|
||||
" else outarray[(pos)] = best_index;\\\n",
|
||||
" } else{\\\n",
|
||||
" for(size_t i=(start)+(stride); i < (stop); i+=(stride)) {\\\n",
|
||||
" if((array)[i] < (array)[best_idx]) best_idx = i;\\\n",
|
||||
" for(size_t i=1; i < (len); i++) {\\\n",
|
||||
" if(array[(start)+i*(increment)] < array[(start)+best_index*(increment)]) best_index = i;\\\n",
|
||||
" }\\\n",
|
||||
" if((op) == NUMERICAL_MIN) outarray[(pos)] = array[(start)+best_index*(increment)];\\\n",
|
||||
" else outarray[(pos)] = best_index;\\\n",
|
||||
" }\\\n",
|
||||
"} while(0)\n",
|
||||
"\n",
|
||||
"#define RUN_SUM(ndarray, type, optype, len, start, increment) do {\\\n",
|
||||
" type *array = (type *)(ndarray)->array->items;\\\n",
|
||||
" type value;\\\n",
|
||||
" for(size_t j=0; j < (len); j++) {\\\n",
|
||||
" value = array[(start)+j*(increment)];\\\n",
|
||||
" sum += value;\\\n",
|
||||
" }\\\n",
|
||||
"} while(0)\n",
|
||||
"\n",
|
||||
"#define RUN_STD(ndarray, type, len, start, increment) do {\\\n",
|
||||
" type *array = (type *)(ndarray)->array->items;\\\n",
|
||||
" mp_float_t value;\\\n",
|
||||
" for(size_t j=0; j < (len); j++) {\\\n",
|
||||
" sum += array[(start)+j*(increment)];\\\n",
|
||||
" }\\\n",
|
||||
" sum /= (len);\\\n",
|
||||
" for(size_t j=0; j < (len); j++) {\\\n",
|
||||
" value = (array[(start)+j*(increment)] - sum);\\\n",
|
||||
" sum_sq += value * value;\\\n",
|
||||
" }\\\n",
|
||||
"} while(0)\n",
|
||||
"\n",
|
||||
|
|
@ -7119,16 +7144,17 @@
|
|||
"\n",
|
||||
"### Argument parsing\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Since most of these functions operate on matrices along an axis, it might make sense to factor out the parsing of arguments and keyword arguments. The void function `numerical_parse_args` fills in the pointer for the matrix/array, and the axis."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"execution_count": 8,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2020-01-01T19:31:51.602146Z",
|
||||
"start_time": "2020-01-01T19:31:51.594464Z"
|
||||
"end_time": "2020-01-07T20:49:34.247209Z",
|
||||
"start_time": "2020-01-07T20:49:34.223304Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
|
|
@ -7136,13 +7162,13 @@
|
|||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"written 28604 bytes to numerical.c\n"
|
||||
"written 28402 bytes to numerical.c\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%ccode numerical.c\n",
|
||||
"\n",
|
||||
" \n",
|
||||
"#include <math.h>\n",
|
||||
"#include <stdlib.h>\n",
|
||||
"#include <string.h>\n",
|
||||
|
|
@ -7212,7 +7238,33 @@
|
|||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mp_obj_t numerical_sum_mean_std_array(mp_obj_t oin, uint8_t optype) {\n",
|
||||
"void axis_sorter(ndarray_obj_t *ndarray, mp_obj_t axis, size_t *m, size_t *n, size_t *N, \n",
|
||||
" size_t *increment, size_t *len, size_t *start_inc) {\n",
|
||||
" if(axis == mp_const_none) { // flatten the array\n",
|
||||
" *m = 1;\n",
|
||||
" *n = 1;\n",
|
||||
" *len = ndarray->array->len;\n",
|
||||
" *N = 1;\n",
|
||||
" *increment = 1;\n",
|
||||
" *start_inc = ndarray->array->len;\n",
|
||||
" } else if((mp_obj_get_int(axis) == 1)) { // along the horizontal axis\n",
|
||||
" *m = ndarray->m;\n",
|
||||
" *n = 1;\n",
|
||||
" *len = ndarray->n;\n",
|
||||
" *N = ndarray->m;\n",
|
||||
" *increment = 1;\n",
|
||||
" *start_inc = ndarray->n;\n",
|
||||
" } else { // along vertical axis\n",
|
||||
" *m = 1;\n",
|
||||
" *n = ndarray->n;\n",
|
||||
" *len = ndarray->m;\n",
|
||||
" *N = ndarray->n;\n",
|
||||
" *increment = ndarray->n;\n",
|
||||
" *start_inc = 1;\n",
|
||||
" } \n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mp_obj_t numerical_sum_mean_std_iterable(mp_obj_t oin, uint8_t optype, size_t ddof) {\n",
|
||||
" mp_float_t value, sum = 0.0, sq_sum = 0.0;\n",
|
||||
" mp_obj_iter_buf_t iter_buf;\n",
|
||||
" mp_obj_t item, iterable = mp_getiter(oin, &iter_buf);\n",
|
||||
|
|
@ -7220,194 +7272,153 @@
|
|||
" while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {\n",
|
||||
" value = mp_obj_get_float(item);\n",
|
||||
" sum += value;\n",
|
||||
" if(optype == NUMERICAL_STD) {\n",
|
||||
" sq_sum += value*value;\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" if(optype == NUMERICAL_SUM) {\n",
|
||||
" return mp_obj_new_float(sum);\n",
|
||||
" } else if(optype == NUMERICAL_MEAN) {\n",
|
||||
" return mp_obj_new_float(sum/len);\n",
|
||||
" } else {\n",
|
||||
" } else { // this should be the case of the standard deviation\n",
|
||||
" // TODO: note that we could get away with a single pass, if we used the Weldorf algorithm\n",
|
||||
" // That should save a fair amount of time, because we would have to extract the values only once\n",
|
||||
" iterable = mp_getiter(oin, &iter_buf);\n",
|
||||
" sum /= len; // this is now the mean!\n",
|
||||
" return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(sq_sum/len-sum*sum));\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"STATIC mp_float_t numerical_sum_mean_std_single_line(void *data, size_t start, size_t stop, \n",
|
||||
" size_t stride, uint8_t typecode, uint8_t optype) {\n",
|
||||
" \n",
|
||||
" mp_float_t sum = 0.0, sq_sum = 0.0, value;\n",
|
||||
" size_t len = 0;\n",
|
||||
" for(size_t i=start; i < stop; i+=stride, len++) {\n",
|
||||
" value = ndarray_get_float_value(data, typecode, i); \n",
|
||||
" sum += value;\n",
|
||||
" if(optype == NUMERICAL_STD) {\n",
|
||||
" sq_sum += value*value;\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" if(len == 0) {\n",
|
||||
" mp_raise_ValueError(\"data length is 0!\");\n",
|
||||
" }\n",
|
||||
" if(optype == NUMERICAL_SUM) {\n",
|
||||
" return sum;\n",
|
||||
" } else if(optype == NUMERICAL_MEAN) {\n",
|
||||
" return sum/len;\n",
|
||||
" } else {\n",
|
||||
" sum /= len; // this is now the mean!\n",
|
||||
" return MICROPY_FLOAT_C_FUN(sqrt)(sq_sum/len-sum*sum);\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"STATIC mp_obj_t numerical_sum_mean_std_matrix(mp_obj_t oin, mp_obj_t axis, uint8_t optype) {\n",
|
||||
" ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);\n",
|
||||
" if((axis == mp_const_none) || (in->m == 1) || (in->n == 1)) { \n",
|
||||
" // return the value for the flattened array\n",
|
||||
" return mp_obj_new_float(numerical_sum_mean_std_single_line(in->array->items, 0, \n",
|
||||
" in->array->len, 1, in->array->typecode, optype));\n",
|
||||
" } else {\n",
|
||||
" uint8_t _axis = mp_obj_get_int(axis);\n",
|
||||
" size_t m = (_axis == 0) ? 1 : in->m;\n",
|
||||
" size_t n = (_axis == 0) ? in->n : 1;\n",
|
||||
" size_t len = in->array->len;\n",
|
||||
" mp_float_t sms;\n",
|
||||
" // TODO: pass in->array->typcode to create_new_ndarray\n",
|
||||
" ndarray_obj_t *out = create_new_ndarray(m, n, NDARRAY_FLOAT);\n",
|
||||
"\n",
|
||||
" // TODO: these two cases could probably be combined in a more elegant fashion...\n",
|
||||
" if(_axis == 0) { // vertical\n",
|
||||
" for(size_t i=0; i < n; i++) {\n",
|
||||
" sms = numerical_sum_mean_std_single_line(in->array->items, i, len, \n",
|
||||
" n, in->array->typecode, optype);\n",
|
||||
" ((float_t *)out->array->items)[i] = sms;\n",
|
||||
" }\n",
|
||||
" } else { // horizontal\n",
|
||||
" for(size_t i=0; i < m; i++) {\n",
|
||||
" sms = numerical_sum_mean_std_single_line(in->array->items, i*in->n, \n",
|
||||
" (i+1)*in->n, 1, in->array->typecode, optype);\n",
|
||||
" ((float_t *)out->array->items)[i] = sms;\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" return MP_OBJ_FROM_PTR(out);\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"size_t numerical_argmin_argmax_array(ndarray_obj_t *in, size_t start, \n",
|
||||
" size_t stop, size_t stride, uint8_t op) {\n",
|
||||
" size_t best_idx = start;\n",
|
||||
" if(in->array->typecode == NDARRAY_UINT8) {\n",
|
||||
" ARG_MIN_LOOP(in, uint8_t, start, stop, stride, op);\n",
|
||||
" } else if(in->array->typecode == NDARRAY_INT8) {\n",
|
||||
" ARG_MIN_LOOP(in, int8_t, start, stop, stride, op);\n",
|
||||
" } else if(in->array->typecode == NDARRAY_UINT16) {\n",
|
||||
" ARG_MIN_LOOP(in, uint16_t, start, stop, stride, op);\n",
|
||||
" } else if(in->array->typecode == NDARRAY_INT16) {\n",
|
||||
" ARG_MIN_LOOP(in, uint16_t, start, stop, stride, op);\n",
|
||||
" } else if(in->array->typecode == NDARRAY_FLOAT) {\n",
|
||||
" ARG_MIN_LOOP(in, mp_float_t, start, stop, stride, op);\n",
|
||||
" }\n",
|
||||
" return best_idx;\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"void copy_value_into_ndarray(ndarray_obj_t *target, ndarray_obj_t *source, size_t target_idx, size_t source_idx) {\n",
|
||||
" // since we are simply copying, it doesn't matter, whether the arrays are signed or unsigned, \n",
|
||||
" // we can cast them in any way we like\n",
|
||||
" // This could also be done with byte copies. I don't know, whether that would have any benefits\n",
|
||||
" if((target->array->typecode == NDARRAY_UINT8) || (target->array->typecode == NDARRAY_INT8)) {\n",
|
||||
" ((uint8_t *)target->array->items)[target_idx] = ((uint8_t *)source->array->items)[source_idx];\n",
|
||||
" } else if((target->array->typecode == NDARRAY_UINT16) || (target->array->typecode == NDARRAY_INT16)) {\n",
|
||||
" ((uint16_t *)target->array->items)[target_idx] = ((uint16_t *)source->array->items)[source_idx];\n",
|
||||
" } else { \n",
|
||||
" ((float *)target->array->items)[target_idx] = ((float *)source->array->items)[source_idx];\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
" \n",
|
||||
"STATIC mp_obj_t numerical_argmin_argmax(mp_obj_t oin, mp_obj_t axis, uint8_t optype) {\n",
|
||||
" if(mp_obj_is_type(oin, &mp_type_tuple) || mp_obj_is_type(oin, &mp_type_list) || \n",
|
||||
" mp_obj_is_type(oin, &mp_type_range)) {\n",
|
||||
" // This case will work for single iterables only \n",
|
||||
" size_t idx = 0, best_idx = 0;\n",
|
||||
" mp_obj_iter_buf_t iter_buf;\n",
|
||||
" mp_obj_t iterable = mp_getiter(oin, &iter_buf);\n",
|
||||
" mp_obj_t best_obj = MP_OBJ_NULL;\n",
|
||||
" mp_obj_t item;\n",
|
||||
" mp_uint_t op = MP_BINARY_OP_LESS;\n",
|
||||
" if((optype == NUMERICAL_ARGMAX) || (optype == NUMERICAL_MAX)) op = MP_BINARY_OP_MORE;\n",
|
||||
" while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {\n",
|
||||
" if ((best_obj == MP_OBJ_NULL) || (mp_binary_op(op, item, best_obj) == mp_const_true)) {\n",
|
||||
" best_obj = item;\n",
|
||||
" best_idx = idx;\n",
|
||||
" }\n",
|
||||
" idx++;\n",
|
||||
" value = mp_obj_get_float(item) - sum;\n",
|
||||
" sq_sum += value * value;\n",
|
||||
" }\n",
|
||||
" if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) {\n",
|
||||
" return MP_OBJ_NEW_SMALL_INT(best_idx);\n",
|
||||
" } else {\n",
|
||||
" return best_obj;\n",
|
||||
" }\n",
|
||||
" } else if(mp_obj_is_type(oin, &ulab_ndarray_type)) {\n",
|
||||
" ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);\n",
|
||||
" size_t best_idx;\n",
|
||||
" if((axis == mp_const_none) || (in->m == 1) || (in->n == 1)) {\n",
|
||||
" // return the value for the flattened array \n",
|
||||
" best_idx = numerical_argmin_argmax_array(in, 0, in->array->len, 1, optype);\n",
|
||||
" if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) {\n",
|
||||
" return MP_OBJ_NEW_SMALL_INT(best_idx);\n",
|
||||
" } else {\n",
|
||||
" if(in->array->typecode == NDARRAY_FLOAT) {\n",
|
||||
" return mp_obj_new_float(ndarray_get_float_value(in->array->items, in->array->typecode, best_idx));\n",
|
||||
" } else {\n",
|
||||
" return mp_binary_get_val_array(in->array->typecode, in->array->items, best_idx);\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" } else { // we have to work with a full matrix here\n",
|
||||
" uint8_t _axis = mp_obj_get_int(axis);\n",
|
||||
" size_t m = (_axis == 0) ? 1 : in->m;\n",
|
||||
" size_t n = (_axis == 0) ? in->n : 1;\n",
|
||||
" size_t len = in->array->len;\n",
|
||||
" ndarray_obj_t *ndarray = NULL;\n",
|
||||
" if((optype == NUMERICAL_MAX) || (optype == NUMERICAL_MIN)) {\n",
|
||||
" ndarray = create_new_ndarray(m, n, in->array->typecode);\n",
|
||||
" } else { // argmin/argmax\n",
|
||||
" // TODO: one might get away with uint8_t, if both m, and n < 255\n",
|
||||
" ndarray = create_new_ndarray(m, n, NDARRAY_UINT16);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" // TODO: these two cases could probably be combined in a more elegant fashion...\n",
|
||||
" if(_axis == 0) { // vertical\n",
|
||||
" for(size_t i=0; i < n; i++) {\n",
|
||||
" best_idx = numerical_argmin_argmax_array(in, i, len, n, optype);\n",
|
||||
" if((optype == NUMERICAL_MIN) || (optype == NUMERICAL_MAX)) {\n",
|
||||
" copy_value_into_ndarray(ndarray, in, i, best_idx);\n",
|
||||
" } else {\n",
|
||||
" ((uint16_t *)ndarray->array->items)[i] = (uint16_t)(best_idx / n);\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" } else { // horizontal\n",
|
||||
" for(size_t i=0; i < m; i++) {\n",
|
||||
" best_idx = numerical_argmin_argmax_array(in, i*in->n, (i+1)*in->n, 1, optype);\n",
|
||||
" if((optype == NUMERICAL_MIN) || (optype == NUMERICAL_MAX)) {\n",
|
||||
" copy_value_into_ndarray(ndarray, in, i, best_idx);\n",
|
||||
" } else {\n",
|
||||
" ((uint16_t *)ndarray->array->items)[i] = (uint16_t)(best_idx - i*in->n);\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" return MP_OBJ_FROM_PTR(ndarray);\n",
|
||||
" }\n",
|
||||
" return mp_const_none;\n",
|
||||
" }\n",
|
||||
" mp_raise_TypeError(\"input type is not supported\");\n",
|
||||
" return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(sq_sum/(len-ddof)));\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"STATIC mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, uint8_t type) {\n",
|
||||
"STATIC mp_obj_t numerical_sum_mean_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, uint8_t optype) {\n",
|
||||
" size_t m, n, increment, start, start_inc, N, len; \n",
|
||||
" axis_sorter(ndarray, axis, &m, &n, &N, &increment, &len, &start_inc);\n",
|
||||
" ndarray_obj_t *results = create_new_ndarray(m, n, NDARRAY_FLOAT);\n",
|
||||
" mp_float_t sum, sq_sum;\n",
|
||||
" mp_float_t *farray = (mp_float_t *)results->array->items;\n",
|
||||
" for(size_t j=0; j < N; j++) { // result index\n",
|
||||
" start = j * start_inc;\n",
|
||||
" sum = sq_sum = 0.0;\n",
|
||||
" if(ndarray->array->typecode == NDARRAY_UINT8) {\n",
|
||||
" RUN_SUM(ndarray, uint8_t, optype, len, start, increment);\n",
|
||||
" } else if(ndarray->array->typecode == NDARRAY_INT8) {\n",
|
||||
" RUN_SUM(ndarray, int8_t, optype, len, start, increment);\n",
|
||||
" } else if(ndarray->array->typecode == NDARRAY_UINT16) {\n",
|
||||
" RUN_SUM(ndarray, uint16_t, optype, len, start, increment);\n",
|
||||
" } else if(ndarray->array->typecode == NDARRAY_INT16) {\n",
|
||||
" RUN_SUM(ndarray, int16_t, optype, len, start, increment);\n",
|
||||
" } else { // this will be mp_float_t, no need to check\n",
|
||||
" RUN_SUM(ndarray, mp_float_t, optype, len, start, increment);\n",
|
||||
" }\n",
|
||||
" if(optype == NUMERICAL_SUM) {\n",
|
||||
" farray[j] = sum;\n",
|
||||
" } else { // this is the case of the mean\n",
|
||||
" farray[j] = sum / len;\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" if(results->array->len == 1) {\n",
|
||||
" return mp_obj_new_float(farray[0]);\n",
|
||||
" }\n",
|
||||
" return MP_OBJ_FROM_PTR(results);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mp_obj_t numerical_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, size_t ddof) {\n",
|
||||
" size_t m, n, increment, start, start_inc, N, len; \n",
|
||||
" mp_float_t sum, sum_sq;\n",
|
||||
" \n",
|
||||
" axis_sorter(ndarray, axis, &m, &n, &N, &increment, &len, &start_inc);\n",
|
||||
" if(ddof > len) {\n",
|
||||
" mp_raise_ValueError(\"ddof must be smaller than length of data set\");\n",
|
||||
" }\n",
|
||||
" ndarray_obj_t *results = create_new_ndarray(m, n, NDARRAY_FLOAT);\n",
|
||||
" mp_float_t *farray = (mp_float_t *)results->array->items;\n",
|
||||
" for(size_t j=0; j < N; j++) { // result index\n",
|
||||
" start = j * start_inc;\n",
|
||||
" sum = 0.0;\n",
|
||||
" sum_sq = 0.0;\n",
|
||||
" if(ndarray->array->typecode == NDARRAY_UINT8) {\n",
|
||||
" RUN_STD(ndarray, uint8_t, len, start, increment);\n",
|
||||
" } else if(ndarray->array->typecode == NDARRAY_INT8) {\n",
|
||||
" RUN_STD(ndarray, int8_t, len, start, increment);\n",
|
||||
" } else if(ndarray->array->typecode == NDARRAY_UINT16) {\n",
|
||||
" RUN_STD(ndarray, uint16_t, len, start, increment);\n",
|
||||
" } else if(ndarray->array->typecode == NDARRAY_INT16) {\n",
|
||||
" RUN_STD(ndarray, int16_t, len, start, increment);\n",
|
||||
" } else { // this will be mp_float_t, no need to check\n",
|
||||
" RUN_STD(ndarray, mp_float_t, len, start, increment);\n",
|
||||
" }\n",
|
||||
" farray[j] = MICROPY_FLOAT_C_FUN(sqrt)(sum_sq/(len - ddof));\n",
|
||||
" }\n",
|
||||
" if(results->array->len == 1) {\n",
|
||||
" return mp_obj_new_float(farray[0]);\n",
|
||||
" }\n",
|
||||
" return MP_OBJ_FROM_PTR(results);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mp_obj_t numerical_argmin_argmax_iterable(mp_obj_t oin, mp_obj_t axis, uint8_t optype) {\n",
|
||||
" size_t idx = 0, best_idx = 0;\n",
|
||||
" mp_obj_iter_buf_t iter_buf;\n",
|
||||
" mp_obj_t iterable = mp_getiter(oin, &iter_buf);\n",
|
||||
" mp_obj_t best_obj = MP_OBJ_NULL;\n",
|
||||
" mp_obj_t item;\n",
|
||||
" mp_uint_t op = MP_BINARY_OP_LESS;\n",
|
||||
" if((optype == NUMERICAL_ARGMAX) || (optype == NUMERICAL_MAX)) op = MP_BINARY_OP_MORE;\n",
|
||||
" while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {\n",
|
||||
" if ((best_obj == MP_OBJ_NULL) || (mp_binary_op(op, item, best_obj) == mp_const_true)) {\n",
|
||||
" best_obj = item;\n",
|
||||
" best_idx = idx;\n",
|
||||
" }\n",
|
||||
" idx++;\n",
|
||||
" }\n",
|
||||
" if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) {\n",
|
||||
" return MP_OBJ_NEW_SMALL_INT(best_idx);\n",
|
||||
" } else {\n",
|
||||
" return best_obj;\n",
|
||||
" } \n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, uint8_t optype) {\n",
|
||||
" size_t m, n, increment, start, start_inc, N, len;\n",
|
||||
" axis_sorter(ndarray, axis, &m, &n, &N, &increment, &len, &start_inc);\n",
|
||||
" ndarray_obj_t *results;\n",
|
||||
" if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) {\n",
|
||||
" // we could save some RAM by taking NDARRAY_UINT8, if the dimensions \n",
|
||||
" // are smaller than 256, but the code would become more involving \n",
|
||||
" // (we would also need extra flash space)\n",
|
||||
" results = create_new_ndarray(m, n, NDARRAY_UINT16);\n",
|
||||
" } else {\n",
|
||||
" results = create_new_ndarray(m, n, ndarray->array->typecode);\n",
|
||||
" }\n",
|
||||
" \n",
|
||||
" for(size_t j=0; j < N; j++) { // result index\n",
|
||||
" start = j * start_inc;\n",
|
||||
" if((ndarray->array->typecode == NDARRAY_UINT8) || (ndarray->array->typecode == NDARRAY_INT8)) {\n",
|
||||
" if((optype == NUMERICAL_MAX) || (optype == NUMERICAL_MIN)) {\n",
|
||||
" RUN_ARGMIN(ndarray, results, uint8_t, uint8_t, len, start, increment, optype, j);\n",
|
||||
" } else {\n",
|
||||
" RUN_ARGMIN(ndarray, results, uint8_t, uint16_t, len, start, increment, optype, j); \n",
|
||||
" }\n",
|
||||
" } else if((ndarray->array->typecode == NDARRAY_UINT16) || (ndarray->array->typecode == NDARRAY_INT16)) {\n",
|
||||
" RUN_ARGMIN(ndarray, results, uint16_t, uint16_t, len, start, increment, optype, j);\n",
|
||||
" } else {\n",
|
||||
" if((optype == NUMERICAL_MAX) || (optype == NUMERICAL_MIN)) {\n",
|
||||
" RUN_ARGMIN(ndarray, results, mp_float_t, mp_float_t, len, start, increment, optype, j);\n",
|
||||
" } else {\n",
|
||||
" RUN_ARGMIN(ndarray, results, mp_float_t, uint16_t, len, start, increment, optype, j); \n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" return MP_OBJ_FROM_PTR(results);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"STATIC mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, uint8_t optype) {\n",
|
||||
" static const mp_arg_t allowed_args[] = {\n",
|
||||
" { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} } ,\n",
|
||||
" { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },\n",
|
||||
" { MP_QSTR_axis, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },\n",
|
||||
" };\n",
|
||||
"\n",
|
||||
" mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];\n",
|
||||
" mp_arg_parse_all(1, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);\n",
|
||||
" mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);\n",
|
||||
" \n",
|
||||
" mp_obj_t oin = args[0].u_obj;\n",
|
||||
" mp_obj_t axis = args[1].u_obj;\n",
|
||||
|
|
@ -7416,32 +7427,31 @@
|
|||
" mp_raise_ValueError(\"axis must be None, 0, or 1\");\n",
|
||||
" }\n",
|
||||
" \n",
|
||||
" if(mp_obj_is_type(oin, &mp_type_tuple) || mp_obj_is_type(oin, &mp_type_list) || \n",
|
||||
" mp_obj_is_type(oin, &mp_type_range)) {\n",
|
||||
" switch(type) {\n",
|
||||
" if(MP_OBJ_IS_TYPE(oin, &mp_type_tuple) || MP_OBJ_IS_TYPE(oin, &mp_type_list) || \n",
|
||||
" MP_OBJ_IS_TYPE(oin, &mp_type_range)) {\n",
|
||||
" switch(optype) {\n",
|
||||
" case NUMERICAL_MIN:\n",
|
||||
" case NUMERICAL_ARGMIN:\n",
|
||||
" case NUMERICAL_MAX:\n",
|
||||
" case NUMERICAL_ARGMAX:\n",
|
||||
" return numerical_argmin_argmax(oin, axis, type);\n",
|
||||
" return numerical_argmin_argmax_iterable(oin, axis, optype);\n",
|
||||
" case NUMERICAL_SUM:\n",
|
||||
" case NUMERICAL_MEAN:\n",
|
||||
" case NUMERICAL_STD:\n",
|
||||
" return numerical_sum_mean_std_array(oin, type);\n",
|
||||
" return numerical_sum_mean_std_iterable(oin, optype, 0);\n",
|
||||
" default: // we should never reach this point, but whatever\n",
|
||||
" return mp_const_none;\n",
|
||||
" }\n",
|
||||
" } else if(mp_obj_is_type(oin, &ulab_ndarray_type)) {\n",
|
||||
" switch(type) {\n",
|
||||
" } else if(MP_OBJ_IS_TYPE(oin, &ulab_ndarray_type)) {\n",
|
||||
" ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin);\n",
|
||||
" switch(optype) {\n",
|
||||
" case NUMERICAL_MIN:\n",
|
||||
" case NUMERICAL_MAX:\n",
|
||||
" case NUMERICAL_ARGMIN:\n",
|
||||
" case NUMERICAL_ARGMAX:\n",
|
||||
" return numerical_argmin_argmax(oin, axis, type);\n",
|
||||
" return numerical_argmin_argmax_ndarray(ndarray, axis, optype);\n",
|
||||
" case NUMERICAL_SUM:\n",
|
||||
" case NUMERICAL_MEAN:\n",
|
||||
" case NUMERICAL_STD:\n",
|
||||
" return numerical_sum_mean_std_matrix(oin, axis, type); \n",
|
||||
" return numerical_sum_mean_ndarray(ndarray, axis, optype);\n",
|
||||
" default:\n",
|
||||
" mp_raise_NotImplementedError(\"operation is not implemented on ndarrays\");\n",
|
||||
" }\n",
|
||||
|
|
@ -7476,7 +7486,31 @@
|
|||
"}\n",
|
||||
"\n",
|
||||
"mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {\n",
|
||||
" return numerical_function(n_args, pos_args, kw_args, NUMERICAL_STD);\n",
|
||||
" static const mp_arg_t allowed_args[] = {\n",
|
||||
" { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} } ,\n",
|
||||
" { MP_QSTR_axis, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },\n",
|
||||
" { MP_QSTR_ddof, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },\n",
|
||||
" };\n",
|
||||
"\n",
|
||||
" mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];\n",
|
||||
" mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);\n",
|
||||
" \n",
|
||||
" mp_obj_t oin = args[0].u_obj;\n",
|
||||
" mp_obj_t axis = args[1].u_obj;\n",
|
||||
" size_t ddof = args[2].u_int;\n",
|
||||
" if((axis != mp_const_none) && (mp_obj_get_int(axis) != 0) && (mp_obj_get_int(axis) != 1)) {\n",
|
||||
" // this seems to pass with False, and True...\n",
|
||||
" mp_raise_ValueError(\"axis must be None, 0, or 1\");\n",
|
||||
" }\n",
|
||||
" 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)) {\n",
|
||||
" return numerical_sum_mean_std_iterable(oin, NUMERICAL_STD, ddof);\n",
|
||||
" } else if(MP_OBJ_IS_TYPE(oin, &ulab_ndarray_type)) {\n",
|
||||
" ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin);\n",
|
||||
" return numerical_std_ndarray(ndarray, axis, ddof);\n",
|
||||
" } else {\n",
|
||||
" mp_raise_TypeError(\"input must be tuple, list, range, or ndarray\");\n",
|
||||
" }\n",
|
||||
" return mp_const_none;\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {\n",
|
||||
|
|
@ -7844,11 +7878,11 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 105,
|
||||
"execution_count": 12,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2020-01-07T19:59:33.514774Z",
|
||||
"start_time": "2020-01-07T19:59:33.506709Z"
|
||||
"end_time": "2020-01-07T20:51:32.722886Z",
|
||||
"start_time": "2020-01-07T20:51:32.713985Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
|
|
@ -7879,7 +7913,7 @@
|
|||
"#include \"fft.h\"\n",
|
||||
"#include \"numerical.h\"\n",
|
||||
"\n",
|
||||
"STATIC MP_DEFINE_STR_OBJ(ulab_version_obj, \"0.26.4\");\n",
|
||||
"STATIC MP_DEFINE_STR_OBJ(ulab_version_obj, \"0.26.5\");\n",
|
||||
"\n",
|
||||
"MP_DEFINE_CONST_FUN_OBJ_1(ndarray_shape_obj, ndarray_shape);\n",
|
||||
"MP_DEFINE_CONST_FUN_OBJ_1(ndarray_rawsize_obj, ndarray_rawsize);\n",
|
||||
|
|
@ -8123,11 +8157,11 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 102,
|
||||
"execution_count": 9,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2020-01-07T19:53:37.999138Z",
|
||||
"start_time": "2020-01-07T19:53:37.196162Z"
|
||||
"end_time": "2020-01-07T20:49:53.299366Z",
|
||||
"start_time": "2020-01-07T20:49:51.912441Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
|
|
@ -8151,8 +8185,8 @@
|
|||
"execution_count": null,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2020-01-07T19:59:39.979093Z",
|
||||
"start_time": "2020-01-07T19:59:38.833117Z"
|
||||
"end_time": "2020-01-07T20:50:29.269290Z",
|
||||
"start_time": "2020-01-07T20:49:55.946789Z"
|
||||
},
|
||||
"scrolled": false
|
||||
},
|
||||
|
|
@ -8214,11 +8248,11 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 108,
|
||||
"execution_count": 13,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2020-01-07T20:00:33.493890Z",
|
||||
"start_time": "2020-01-07T20:00:33.436113Z"
|
||||
"end_time": "2020-01-07T20:52:26.547974Z",
|
||||
"start_time": "2020-01-07T20:52:26.542000Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
|
|
@ -8233,6 +8267,13 @@
|
|||
"source": [
|
||||
"%%writefile ../../../ulab/docs/ulab-change-log.md\n",
|
||||
"\n",
|
||||
"Tue, 7 Jan 2020\n",
|
||||
"\n",
|
||||
"version 0.26.5\n",
|
||||
"\n",
|
||||
" fixed glitch in numerical.c, numerical.h\n",
|
||||
"\n",
|
||||
"Mon, 6 Jan 2020\n",
|
||||
"\n",
|
||||
"version 0.26.4\n",
|
||||
"\n",
|
||||
|
|
|
|||
Loading…
Reference in a new issue