Merge pull request #9 from jepler/merge-upstream-2

Merge upstream
This commit is contained in:
Jeff Epler 2020-02-13 13:31:11 -06:00 committed by GitHub
commit cf0180e05e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 496 additions and 293 deletions

View file

@ -17,7 +17,6 @@
#include "py/binary.h"
#include "py/obj.h"
#include "py/objarray.h"
#include "compat.h"
#include "ndarray.h"
#include "fft.h"
@ -79,11 +78,11 @@ void fft_kernel(mp_float_t *real, mp_float_t *imag, int n, int isign) {
}
mp_obj_t fft_fft_ifft_spectrum(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im, uint8_t type) {
if(!mp_obj_is_type(arg_re, &ulab_ndarray_type)) {
if(!MP_OBJ_IS_TYPE(arg_re, &ulab_ndarray_type)) {
mp_raise_NotImplementedError(translate("FFT is defined for ndarrays only"));
}
if(n_args == 2) {
if(!mp_obj_is_type(arg_im, &ulab_ndarray_type)) {
if(!MP_OBJ_IS_TYPE(arg_im, &ulab_ndarray_type)) {
mp_raise_NotImplementedError(translate("FFT is defined for ndarrays only"));
}
}

View file

@ -15,20 +15,19 @@
#include "py/obj.h"
#include "py/runtime.h"
#include "py/misc.h"
#include "compat.h"
#include "filter.h"
#if ULAB_FILTER_CONVOLVE
mp_obj_t filter_convolve(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_a, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_v, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_a, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
{ MP_QSTR_v, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(2, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type) || !mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
if(!MP_OBJ_IS_TYPE(args[0].u_obj, &ulab_ndarray_type) || !MP_OBJ_IS_TYPE(args[1].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("convolve arguments must be ndarrays"));
}
@ -48,23 +47,36 @@ mp_obj_t filter_convolve(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a
ndarray_obj_t *out = create_new_ndarray(1, len, NDARRAY_FLOAT);
mp_float_t *outptr = out->array->items;
int off = len_c-1;
for(int k=-off; k<len-off; k++) {
mp_float_t accum = (mp_float_t)0;
int top_n = MIN(len_c, len_a - k);
int bot_n = MAX(-k, 0);
for(int n=bot_n; n<top_n; n++) {
int idx_c = len_c - n - 1;
int idx_a = n+k;
mp_float_t ai = (mp_float_t)0, ci = (mp_float_t)0;
if(idx_a >= 0 && idx_a < len_a) {
ai = ndarray_get_float_value(a->array->items, a->array->typecode, idx_a);
if(a->array->typecode == NDARRAY_FLOAT && c->array->typecode == NDARRAY_FLOAT) {
mp_float_t* a_items = (mp_float_t*)a->array->items;
mp_float_t* c_items = (mp_float_t*)c->array->items;
for(int k=-off; k<len-off; k++) {
mp_float_t accum = (mp_float_t)0;
int top_n = MIN(len_c, len_a - k);
int bot_n = MAX(-k, 0);
mp_float_t* a_ptr = a_items + bot_n + k;
mp_float_t* a_end = a_ptr + (top_n - bot_n);
mp_float_t* c_ptr = c_items + len_c - bot_n - 1;
for(; a_ptr != a_end;) {
accum += *a_ptr++ * *c_ptr--;
}
if(idx_c >= 0 && idx_c < len_c) {
ci = ndarray_get_float_value(c->array->items, c->array->typecode, idx_c);
}
accum += ai * ci;
*outptr++ = accum;
}
} else {
for(int k=-off; k<len-off; k++) {
mp_float_t accum = (mp_float_t)0;
int top_n = MIN(len_c, len_a - k);
int bot_n = MAX(-k, 0);
for(int n=bot_n; n<top_n; n++) {
int idx_c = len_c - n - 1;
int idx_a = n+k;
mp_float_t ai = ndarray_get_float_value(a->array->items, a->array->typecode, idx_a);
mp_float_t ci = ndarray_get_float_value(c->array->items, c->array->typecode, idx_c);
accum += ai * ci;
}
*outptr++ = accum;
}
*outptr++ = accum;
}
return out;

View file

@ -15,7 +15,6 @@
#include "py/obj.h"
#include "py/runtime.h"
#include "py/misc.h"
#include "compat.h"
#include "linalg.h"
#if ULAB_LINALG_TRANSPOSE
@ -57,7 +56,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(linalg_transpose_obj, linalg_transpose);
#if ULAB_LINALG_RESHAPE
mp_obj_t linalg_reshape(mp_obj_t self_in, mp_obj_t shape) {
ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in);
if(!mp_obj_is_type(shape, &mp_type_tuple) || (MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(shape)) != 2)) {
if(!MP_OBJ_IS_TYPE(shape, &mp_type_tuple) || (MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(shape)) != 2)) {
mp_raise_ValueError(translate("shape must be a 2-tuple"));
}
@ -83,20 +82,20 @@ MP_DEFINE_CONST_FUN_OBJ_2(linalg_reshape_obj, linalg_reshape);
#if ULAB_LINALG_SIZE
mp_obj_t linalg_size(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
};
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);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
if(!MP_OBJ_IS_TYPE(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("size is defined for ndarrays only"));
} else {
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
if(args[1].u_obj == mp_const_none) {
return mp_obj_new_int(ndarray->array->len);
} else if(mp_obj_is_int(args[1].u_obj)) {
} else if(MP_OBJ_IS_INT(args[1].u_obj)) {
uint8_t ax = mp_obj_get_int(args[1].u_obj);
if(ax == 0) {
if(ndarray->m == 1) {
@ -169,11 +168,11 @@ bool linalg_invert_matrix(mp_float_t *data, size_t N) {
#if ULAB_LINALG_INV
mp_obj_t linalg_inv(mp_obj_t o_in) {
// since inv is not a class method, we have to inspect the input argument first
if(!mp_obj_is_type(o_in, &ulab_ndarray_type)) {
if(!MP_OBJ_IS_TYPE(o_in, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("only ndarrays can be inverted"));
}
ndarray_obj_t *o = MP_OBJ_TO_PTR(o_in);
if(!mp_obj_is_type(o_in, &ulab_ndarray_type)) {
if(!MP_OBJ_IS_TYPE(o_in, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("only ndarray objects can be inverted"));
}
if(o->m != o->n) {
@ -206,7 +205,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(linalg_inv_obj, linalg_inv);
#if ULAB_LINALG_DOT
mp_obj_t linalg_dot(mp_obj_t _m1, mp_obj_t _m2) {
// TODO: should the results be upcast?
if(!mp_obj_is_type(_m1, &ulab_ndarray_type) || !mp_obj_is_type(_m2, &ulab_ndarray_type)) {
if(!MP_OBJ_IS_TYPE(_m1, &ulab_ndarray_type) || !MP_OBJ_IS_TYPE(_m2, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("arguments must be ndarrays"));
}
ndarray_obj_t *m1 = MP_OBJ_TO_PTR(_m1);
@ -247,14 +246,14 @@ mp_obj_t linalg_zeros_ones(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
uint8_t dtype = args[1].u_int;
if(!mp_obj_is_int(args[0].u_obj) && !mp_obj_is_type(args[0].u_obj, &mp_type_tuple)) {
if(!MP_OBJ_IS_INT(args[0].u_obj) && !MP_OBJ_IS_TYPE(args[0].u_obj, &mp_type_tuple)) {
mp_raise_TypeError(translate("input argument must be an integer or a 2-tuple"));
}
ndarray_obj_t *ndarray = NULL;
if(mp_obj_is_int(args[0].u_obj)) {
if(MP_OBJ_IS_INT(args[0].u_obj)) {
size_t n = mp_obj_get_int(args[0].u_obj);
ndarray = create_new_ndarray(1, n, dtype);
} else if(mp_obj_is_type(args[0].u_obj, &mp_type_tuple)) {
} else if(MP_OBJ_IS_TYPE(args[0].u_obj, &mp_type_tuple)) {
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(args[0].u_obj);
if(tuple->len != 2) {
mp_raise_TypeError(translate("input argument must be an integer or a 2-tuple"));
@ -292,7 +291,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(linalg_ones_obj, 0, linalg_ones);
mp_obj_t linalg_eye(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_M, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_M, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
{ MP_QSTR_k, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = NDARRAY_FLOAT} },
};
@ -335,7 +334,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(linalg_eye_obj, 0, linalg_eye);
#if ULAB_LINALG_DET
mp_obj_t linalg_det(mp_obj_t oin) {
if(!mp_obj_is_type(oin, &ulab_ndarray_type)) {
if(!MP_OBJ_IS_TYPE(oin, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("function defined for ndarrays only"));
}
ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);
@ -376,7 +375,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(linalg_det_obj, linalg_det);
#if ULAB_LINALG_EIG
mp_obj_t linalg_eig(mp_obj_t oin) {
if(!mp_obj_is_type(oin, &ulab_ndarray_type)) {
if(!MP_OBJ_IS_TYPE(oin, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("function defined for ndarrays only"));
}
ndarray_obj_t *in = MP_OBJ_TO_PTR(oin);

View file

@ -17,7 +17,6 @@
#include "py/binary.h"
#include "py/obj.h"
#include "py/objtuple.h"
#include "compat.h"
#include "ndarray.h"
// This function is copied verbatim from objarray.c
@ -155,7 +154,7 @@ mp_obj_t ndarray_copy(mp_obj_t self_in) {
STATIC uint8_t ndarray_init_helper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = NDARRAY_FLOAT } },
};
@ -166,13 +165,7 @@ STATIC uint8_t ndarray_init_helper(size_t n_args, const mp_obj_t *pos_args, mp_m
return dtype;
}
mp_obj_t ndarray_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
mp_arg_check_num(n_args, kw_args, 1, 2, true);
size_t n_kw = 0;
if (kw_args != 0) {
n_kw = kw_args->used;
}
mp_map_init_fixed_table(kw_args, n_kw, args + n_args);
STATIC mp_obj_t ndarray_make_new_core(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args, mp_map_t *kw_args) {
uint8_t dtype = ndarray_init_helper(n_args, args, kw_args);
size_t len1, len2=0, i=0;
@ -219,6 +212,25 @@ mp_obj_t ndarray_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj
return MP_OBJ_FROM_PTR(self);
}
#ifdef CIRCUITPY
mp_obj_t ndarray_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
mp_arg_check_num(n_args, kw_args, 1, 2, true);
size_t n_kw = 0;
if (kw_args != 0) {
n_kw = kw_args->used;
}
mp_map_init_fixed_table(kw_args, n_kw, args + n_args);
return ndarray_make_new_core(type, n_args, n_kw, args, kw_args);
}
#else
mp_obj_t ndarray_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, 2, true);
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
return ndarray_make_new_core(type, n_args, n_kw, args, &kw_args);
}
#endif
size_t slice_length(mp_bound_slice_t slice) {
int32_t len, correction = 1;
if(slice.step > 0) correction = -1;
@ -234,7 +246,7 @@ size_t true_length(mp_obj_t bool_list) {
mp_obj_t item, iterable = mp_getiter(bool_list, &iter_buf);
size_t trues = 0;
while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
if(!mp_obj_is_type(item, &mp_type_bool)) {
if(!MP_OBJ_IS_TYPE(item, &mp_type_bool)) {
// numpy seems to be a little bit inconsistent in when an index is considered
// to be True/False. Bail out immediately, if the items are not True/False
return 0;
@ -249,9 +261,9 @@ size_t true_length(mp_obj_t bool_list) {
mp_bound_slice_t generate_slice(mp_uint_t n, mp_obj_t index) {
// micropython seems to have difficulties with negative steps
mp_bound_slice_t slice;
if(mp_obj_is_type(index, &mp_type_slice)) {
if(MP_OBJ_IS_TYPE(index, &mp_type_slice)) {
mp_seq_get_fast_slice_indexes(n, index, &slice);
} else if(mp_obj_is_int(index)) {
} else if(MP_OBJ_IS_INT(index)) {
int32_t _index = mp_obj_get_int(index);
if(_index < 0) {
_index += n;
@ -465,7 +477,7 @@ mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarray_obj_t
mp_bound_slice_t row_slice = simple_slice(0, 0, 1), column_slice = simple_slice(0, 0, 1);
size_t m = 0, n = 0;
if(mp_obj_is_int(index) && (ndarray->m == 1) && (values == NULL)) {
if(MP_OBJ_IS_INT(index) && (ndarray->m == 1) && (values == NULL)) {
// we have a row vector, and don't want to assign
column_slice = generate_slice(ndarray->n, index);
if(slice_length(column_slice) == 1) { // we were asked for a single item
@ -474,7 +486,7 @@ mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarray_obj_t
}
}
if(mp_obj_is_int(index) || mp_obj_is_type(index, &mp_type_slice)) {
if(MP_OBJ_IS_INT(index) || MP_OBJ_IS_TYPE(index, &mp_type_slice)) {
if(ndarray->m == 1) { // we have a row vector
column_slice = generate_slice(ndarray->n, index);
row_slice = simple_slice(0, 1, 1);
@ -485,7 +497,7 @@ mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarray_obj_t
m = slice_length(row_slice);
n = slice_length(column_slice);
return iterate_slice_list(ndarray, m, n, row_slice, column_slice, mp_const_none, mp_const_none, values);
} else if(mp_obj_is_type(index, &mp_type_list)) {
} else if(MP_OBJ_IS_TYPE(index, &mp_type_list)) {
n = true_length(index);
if(ndarray->m == 1) { // we have a flat array
// we might have to separate the n == 1 case
@ -500,17 +512,17 @@ mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarray_obj_t
if(tuple->len != 2) {
mp_raise_msg(&mp_type_IndexError, translate("too many indices"));
}
if(!(mp_obj_is_type(tuple->items[0], &mp_type_list) ||
mp_obj_is_type(tuple->items[0], &mp_type_slice) ||
mp_obj_is_int(tuple->items[0])) ||
!(mp_obj_is_type(tuple->items[1], &mp_type_list) ||
mp_obj_is_type(tuple->items[1], &mp_type_slice) ||
mp_obj_is_int(tuple->items[1]))) {
if(!(MP_OBJ_IS_TYPE(tuple->items[0], &mp_type_list) ||
MP_OBJ_IS_TYPE(tuple->items[0], &mp_type_slice) ||
MP_OBJ_IS_INT(tuple->items[0])) ||
!(MP_OBJ_IS_TYPE(tuple->items[1], &mp_type_list) ||
MP_OBJ_IS_TYPE(tuple->items[1], &mp_type_slice) ||
MP_OBJ_IS_INT(tuple->items[1]))) {
mp_raise_msg(&mp_type_IndexError, translate("indices must be integers, slices, or Boolean lists"));
}
if(mp_obj_is_type(tuple->items[0], &mp_type_list)) { // rows are indexed by Boolean list
if(MP_OBJ_IS_TYPE(tuple->items[0], &mp_type_list)) { // rows are indexed by Boolean list
m = true_length(tuple->items[0]);
if(mp_obj_is_type(tuple->items[1], &mp_type_list)) {
if(MP_OBJ_IS_TYPE(tuple->items[1], &mp_type_list)) {
n = true_length(tuple->items[1]);
return iterate_slice_list(ndarray, m, n, row_slice, column_slice,
tuple->items[0], tuple->items[1], values);
@ -524,7 +536,7 @@ mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarray_obj_t
} else { // rows are indexed by a slice, or an integer
row_slice = generate_slice(ndarray->m, tuple->items[0]);
m = slice_length(row_slice);
if(mp_obj_is_type(tuple->items[1], &mp_type_list)) { // columns are indexed by a Boolean list
if(MP_OBJ_IS_TYPE(tuple->items[1], &mp_type_list)) { // columns are indexed by a Boolean list
n = true_length(tuple->items[1]);
return iterate_slice_list(ndarray, m, n, row_slice, column_slice,
mp_const_none, tuple->items[1], values);
@ -545,12 +557,12 @@ mp_obj_t ndarray_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
if (value == MP_OBJ_SENTINEL) { // return value(s)
return ndarray_get_slice(self, index, NULL);
} else { // assignment to slices; the value must be an ndarray, or a scalar
if(!mp_obj_is_type(value, &ulab_ndarray_type) &&
!mp_obj_is_int(value) && !mp_obj_is_float(value)) {
if(!MP_OBJ_IS_TYPE(value, &ulab_ndarray_type) &&
!MP_OBJ_IS_INT(value) && !mp_obj_is_float(value)) {
mp_raise_ValueError(translate("right hand side must be an ndarray, or a scalar"));
} else {
ndarray_obj_t *values = NULL;
if(mp_obj_is_int(value)) {
if(MP_OBJ_IS_INT(value)) {
values = create_new_ndarray(1, 1, self->array->typecode);
mp_binary_set_val_array(values->array->typecode, values->array->items, 0, value);
} else if(mp_obj_is_float(value)) {
@ -628,22 +640,14 @@ mp_obj_t ndarray_shape(mp_obj_t self_in) {
return mp_obj_new_tuple(2, tuple);
}
mp_obj_t ndarray_rawsize(mp_obj_t self_in) {
// returns a 5-tuple with the
//
// 0. number of rows
// 1. number of columns
// 2. length of the storage (should be equal to the product of 1. and 2.)
// 3. length of the data storage in bytes
// 4. datum size in bytes
mp_obj_t ndarray_size(mp_obj_t self_in) {
ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(5, NULL));
tuple->items[0] = MP_OBJ_NEW_SMALL_INT(self->m);
tuple->items[1] = MP_OBJ_NEW_SMALL_INT(self->n);
tuple->items[2] = MP_OBJ_NEW_SMALL_INT(self->array->len);
tuple->items[3] = MP_OBJ_NEW_SMALL_INT(self->bytes);
tuple->items[4] = MP_OBJ_NEW_SMALL_INT(mp_binary_get_size('@', self->array->typecode, NULL));
return tuple;
return mp_obj_new_int(self->array->len);
}
mp_obj_t ndarray_itemsize(mp_obj_t self_in) {
ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in);
return MP_OBJ_NEW_SMALL_INT(mp_binary_get_size('@', self->array->typecode, NULL));
}
mp_obj_t ndarray_flatten(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
@ -692,7 +696,7 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) {
// TODO: implement in-place operators
mp_obj_t RHS = MP_OBJ_NULL;
bool rhs_is_scalar = true;
if(mp_obj_is_int(rhs)) {
if(MP_OBJ_IS_INT(rhs)) {
int32_t ivalue = mp_obj_get_int(rhs);
if((ivalue > 0) && (ivalue < 256)) {
CREATE_SINGLE_ITEM(RHS, uint8_t, NDARRAY_UINT8, ivalue);
@ -713,7 +717,7 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) {
rhs_is_scalar = false;
}
//else
if(mp_obj_is_type(lhs, &ulab_ndarray_type) && mp_obj_is_type(RHS, &ulab_ndarray_type)) {
if(MP_OBJ_IS_TYPE(lhs, &ulab_ndarray_type) && MP_OBJ_IS_TYPE(RHS, &ulab_ndarray_type)) {
// next, the ndarray stuff
ndarray_obj_t *ol = MP_OBJ_TO_PTR(lhs);
ndarray_obj_t *or = MP_OBJ_TO_PTR(RHS);
@ -913,3 +917,13 @@ mp_int_t ndarray_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint
// buffer_p.get_buffer() returns zero for success, while mp_get_buffer returns true for success
return !mp_get_buffer(self->array, bufinfo, flags);
}
void ndarray_attributes(mp_obj_t self_in, qstr attribute, mp_obj_t *destination) {
if(attribute == MP_QSTR_size) {
destination[0] = ndarray_size(self_in);
} else if(attribute == MP_QSTR_itemsize) {
destination[0] = ndarray_itemsize(self_in);
} else if(attribute == MP_QSTR_shape) {
destination[0] = ndarray_shape(self_in);
}
}

View file

@ -58,17 +58,23 @@ 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 );
#ifdef CIRCUITPY
mp_obj_t ndarray_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args);
#else
mp_obj_t ndarray_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *);
#endif
mp_obj_t ndarray_subscr(mp_obj_t , mp_obj_t , mp_obj_t );
mp_obj_t ndarray_getiter(mp_obj_t , mp_obj_iter_buf_t *);
mp_obj_t ndarray_binary_op(mp_binary_op_t , mp_obj_t , mp_obj_t );
mp_obj_t ndarray_unary_op(mp_unary_op_t , mp_obj_t );
mp_obj_t ndarray_shape(mp_obj_t );
mp_obj_t ndarray_rawsize(mp_obj_t );
mp_obj_t ndarray_size(mp_obj_t );
mp_obj_t ndarray_flatten(size_t , const mp_obj_t *, mp_map_t *);
mp_int_t ndarray_get_buffer(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags);
void ndarray_attributes(mp_obj_t , qstr , mp_obj_t *);
#define CREATE_SINGLE_ITEM(outarray, type, typecode, value) do {\
ndarray_obj_t *tmp = create_new_ndarray(1, 1, (typecode));\
type *tmparr = (type *)tmp->array->items;\

View file

@ -17,7 +17,6 @@
#include "py/runtime.h"
#include "py/builtin.h"
#include "py/misc.h"
#include "compat.h"
#include "numerical.h"
enum NUMERICAL_FUNCTION_TYPE {
@ -33,11 +32,11 @@ enum NUMERICAL_FUNCTION_TYPE {
#if ULAB_NUMERICAL_LINSPACE
mp_obj_t numerical_linspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
{ MP_QSTR_num, MP_ARG_INT, {.u_int = 50} },
{ MP_QSTR_endpoint, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_TRUE} },
{ MP_QSTR_retstep, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_FALSE} },
{ MP_QSTR_endpoint, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_true} },
{ MP_QSTR_retstep, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_false} },
{ MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = NDARRAY_FLOAT} },
};
@ -259,8 +258,8 @@ mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis,
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_NONE} } ,
{ MP_QSTR_axis, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} } ,
{ MP_QSTR_axis, MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@ -345,8 +344,8 @@ MP_DEFINE_CONST_FUN_OBJ_KW(numerical_mean_obj, 1, numerical_mean);
mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } } ,
{ MP_QSTR_axis, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } } ,
{ MP_QSTR_axis, MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
{ MP_QSTR_ddof, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
};
@ -376,9 +375,9 @@ MP_DEFINE_CONST_FUN_OBJ_KW(numerical_std_obj, 1, numerical_std);
#if ULAB_NUMERICAL_ROLL
mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@ -460,14 +459,14 @@ MP_DEFINE_CONST_FUN_OBJ_KW(numerical_roll_obj, 2, numerical_roll);
#if ULAB_NUMERICAL_FLIP
mp_obj_t numerical_flip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
};
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);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
if(!MP_OBJ_IS_TYPE(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("flip argument must be an ndarray"));
}
if((args[1].u_obj != mp_const_none) &&
@ -511,7 +510,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(numerical_flip_obj, 1, numerical_flip);
#if ULAB_NUMERICAL_DIFF
mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
{ MP_QSTR_n, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1 } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1 } },
};
@ -519,7 +518,7 @@ mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
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);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
if(!MP_OBJ_IS_TYPE(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("diff argument must be an ndarray"));
}
@ -582,7 +581,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(numerical_diff_obj, 1, numerical_diff);
#if ULAB_NUMERICAL_SORT
mp_obj_t numerical_sort_helper(mp_obj_t oin, mp_obj_t axis, uint8_t inplace) {
if(!mp_obj_is_type(oin, &ulab_ndarray_type)) {
if(!MP_OBJ_IS_TYPE(oin, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("sort argument must be an ndarray"));
}
@ -640,7 +639,7 @@ mp_obj_t numerical_sort_helper(mp_obj_t oin, mp_obj_t axis, uint8_t inplace) {
// numpy function
mp_obj_t numerical_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_int = -1 } },
};
@ -655,7 +654,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(numerical_sort_obj, 1, numerical_sort);
// method of an ndarray
mp_obj_t numerical_sort_inplace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_int = -1 } },
};
@ -671,12 +670,12 @@ MP_DEFINE_CONST_FUN_OBJ_KW(numerical_sort_inplace_obj, 1, numerical_sort_inplace
#if ULAB_NUMERICAL_ARGSORT
mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_int = -1 } },
};
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);
if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
if(!MP_OBJ_IS_TYPE(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError(translate("argsort argument must be an ndarray"));
}

View file

@ -12,24 +12,23 @@
#include "py/obj.h"
#include "py/runtime.h"
#include "py/objarray.h"
#include "compat.h"
#include "ndarray.h"
#include "linalg.h"
#include "poly.h"
#if ULAB_POLY_POLYVAL || ULAB_POLY_POLYFIT
bool object_is_nditerable(mp_obj_t o_in) {
if(mp_obj_is_type(o_in, &ulab_ndarray_type) ||
mp_obj_is_type(o_in, &mp_type_tuple) ||
mp_obj_is_type(o_in, &mp_type_list) ||
mp_obj_is_type(o_in, &mp_type_range)) {
if(MP_OBJ_IS_TYPE(o_in, &ulab_ndarray_type) ||
MP_OBJ_IS_TYPE(o_in, &mp_type_tuple) ||
MP_OBJ_IS_TYPE(o_in, &mp_type_list) ||
MP_OBJ_IS_TYPE(o_in, &mp_type_range)) {
return true;
}
return false;
}
size_t get_nditerable_len(mp_obj_t o_in) {
if(mp_obj_is_type(o_in, &ulab_ndarray_type)) {
if(MP_OBJ_IS_TYPE(o_in, &ulab_ndarray_type)) {
ndarray_obj_t *in = MP_OBJ_TO_PTR(o_in);
return in->array->len;
} else {
@ -44,7 +43,7 @@ mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) {
// TODO: there is a bug here: matrices won't work,
// because there is a single iteration loop
size_t m, n;
if(mp_obj_is_type(o_x, &ulab_ndarray_type)) {
if(MP_OBJ_IS_TYPE(o_x, &ulab_ndarray_type)) {
ndarray_obj_t *ndx = MP_OBJ_TO_PTR(o_x);
m = ndx->m;
n = ndx->n;

View file

@ -19,7 +19,6 @@
#include "py/objarray.h"
#include "ulab.h"
#include "compat.h"
#include "ndarray.h"
#include "linalg.h"
#include "vectorise.h"
@ -28,16 +27,13 @@
#include "filter.h"
#include "numerical.h"
STATIC MP_DEFINE_STR_OBJ(ulab_version_obj, "0.31.0");
STATIC MP_DEFINE_STR_OBJ(ulab_version_obj, "0.32.0");
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_shape_obj, ndarray_shape);
MP_DEFINE_CONST_FUN_OBJ_1(ndarray_rawsize_obj, ndarray_rawsize);
MP_DEFINE_CONST_FUN_OBJ_KW(ndarray_flatten_obj, 1, ndarray_flatten);
STATIC const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_shape), MP_ROM_PTR(&ndarray_shape_obj) },
{ MP_ROM_QSTR(MP_QSTR_rawsize), MP_ROM_PTR(&ndarray_rawsize_obj) },
{ MP_ROM_QSTR(MP_QSTR_flatten), MP_ROM_PTR(&ndarray_flatten_obj) },
{ MP_ROM_QSTR(MP_QSTR_flatten), MP_ROM_PTR(&ndarray_flatten_obj) },
#if ULAB_LINALG_TRANSPOSE
{ MP_ROM_QSTR(MP_QSTR_transpose), MP_ROM_PTR(&linalg_transpose_obj) },
#endif
@ -60,6 +56,7 @@ const mp_obj_type_t ulab_ndarray_type = {
.binary_op = ndarray_binary_op,
.buffer_p = { .get_buffer = ndarray_get_buffer, },
.locals_dict = (mp_obj_dict_t*)&ulab_ndarray_locals_dict,
.attr = ndarray_attributes,
};
STATIC const mp_map_elem_t ulab_globals_table[] = {

View file

@ -54,7 +54,6 @@
#define ULAB_POLY_POLYFIT (1)
//
#define ULAB_NUMERICAL_ARGSORT (1)
#define ULAB_NUMERICAL_LINSPACE (1)
#define ULAB_NUMERICAL_SUM (1)
#define ULAB_NUMERICAL_MEAN (1)
@ -67,6 +66,7 @@
#define ULAB_NUMERICAL_FLIP (1)
#define ULAB_NUMERICAL_DIFF (1)
#define ULAB_NUMERICAL_SORT (1)
#define ULAB_NUMERICAL_ARGSORT (1)
// FFT costs about 2 kB of flash space
#define ULAB_FFT_FFT (1)

View file

@ -16,7 +16,6 @@
#include "py/binary.h"
#include "py/obj.h"
#include "py/objarray.h"
#include "compat.h"
#include "vectorise.h"
#ifndef MP_PI
@ -25,11 +24,11 @@
mp_obj_t vectorise_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float_t)) {
// Return a single value, if o_in is not iterable
if(mp_obj_is_float(o_in) || mp_obj_is_integer(o_in)) {
if(mp_obj_is_float(o_in) || MP_OBJ_IS_INT(o_in)) {
return mp_obj_new_float(f(mp_obj_get_float(o_in)));
}
mp_float_t x;
if(mp_obj_is_type(o_in, &ulab_ndarray_type)) {
if(MP_OBJ_IS_TYPE(o_in, &ulab_ndarray_type)) {
ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in);
ndarray_obj_t *ndarray = create_new_ndarray(source->m, source->n, NDARRAY_FLOAT);
mp_float_t *dataout = (mp_float_t *)ndarray->array->items;
@ -45,8 +44,8 @@ mp_obj_t vectorise_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float_t)) {
ITERATE_VECTOR(mp_float_t, source, dataout);
}
return MP_OBJ_FROM_PTR(ndarray);
} else if(mp_obj_is_type(o_in, &mp_type_tuple) || mp_obj_is_type(o_in, &mp_type_list) ||
mp_obj_is_type(o_in, &mp_type_range)) { // i.e., the input is a generic iterable
} else if(MP_OBJ_IS_TYPE(o_in, &mp_type_tuple) || MP_OBJ_IS_TYPE(o_in, &mp_type_list) ||
MP_OBJ_IS_TYPE(o_in, &mp_type_range)) { // i.e., the input is a generic iterable
mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
ndarray_obj_t *out = create_new_ndarray(1, o->len, NDARRAY_FLOAT);
mp_float_t *dataout = (mp_float_t *)out->array->items;

View file

@ -22,7 +22,7 @@ copyright = '2019, Zoltán Vörös'
author = 'Zoltán Vörös'
# The full version, including alpha/beta/rc tags
release = '0.31'
release = '0.32'
# -- General configuration ---------------------------------------------------

View file

@ -1,10 +1,10 @@
Introduction
============
In
https://micropython-usermod.readthedocs.io/en/latest/usermods_14.html, I
mentioned that I have another story, for another day. The day has come,
so here is my story.
In the `last
chapter <https://micropython-usermod.readthedocs.io/en/latest/usermods_15.html>`__
of the usermod documentation, I mentioned that I have another story, for
another day. The day has come, so here is my story.
Enter ulab
----------
@ -68,9 +68,11 @@ The main points of ``ulab`` are
- polynomial fits to numerical data
- fast Fourier transforms
At the time of writing this manual (for version 0.26), the library adds
At the time of writing this manual (for version 0.32), the library adds
approximately 30 kB of extra compiled code to the micropython
(pyboard.v.11) firmware.
(pyboard.v.11) firmware. However, if you are tight with flash space, you
can easily shave off a couple of kB. See the section on `customising
ulab <#Custom_builds>`__.
Resources and legal matters
---------------------------
@ -143,6 +145,35 @@ can always be queried as
If you find a bug, please, include this number in your report!
Customising ``ulab``
--------------------
``ulab`` implements a great number of functions, and it is quite
possible that you do not need all of them in a particular application.
If you want to save some flash space, you can easily exclude arbitrary
functions from the firmware. The
`https://github.com/v923z/micropython-ulab/blob/master/code/ulab.h <ulab.h>`__
header file contains a pre-processor flag for all functions in ``ulab``.
The default setting is 1 for each of them, but if you change that to 0,
the corresponding function will not be part of the compiled firmware.
The first couple of lines of the file look like this
.. code:: c
// vectorise (all functions) takes approx. 3 kB of flash space
#define ULAB_VECTORISE_ACOS (1)
#define ULAB_VECTORISE_ACOSH (1)
#define ULAB_VECTORISE_ASIN (1)
#define ULAB_VECTORISE_ASINH (1)
#define ULAB_VECTORISE_ATAN (1)
#define ULAB_VECTORISE_ATANH (1)
In order to simplify navigation in the file, each flag begins with
``ULAB_``, continues with the sub-module, where the function itself is
implemented, and ends with the functions name. Each section displays a
hint as to how much space you can save by un-setting the flag.
Basic ndarray operations
------------------------
@ -166,8 +197,6 @@ Methods of ndarrays
`.reshape <#.reshape>`__
`.rawsize\*\* <#.rawsize>`__
`.transpose <#.transpose>`__
`.flatten\*\* <#.flatten>`__
@ -395,8 +424,8 @@ Methods of ndarrays
.shape
~~~~~~
The ``.shape`` method returns a 2-tuple with the number of rows, and
columns.
The ``.shape`` method (property) returns a 2-tuple with the number of
rows, and columns.
.. code::
@ -406,11 +435,11 @@ columns.
a = np.array([1, 2, 3, 4], dtype=np.int8)
print("a:\n", a)
print("shape of a:", a.shape())
print("shape of a:", a.shape)
b= np.array([[1, 2], [3, 4]], dtype=np.int8)
print("\nb:\n", b)
print("shape of b:", b.shape())
print("shape of b:", b.shape)
.. parsed-literal::
@ -426,6 +455,74 @@ columns.
.size
~~~~~
The ``.size`` method (property) returns an integer with the number of
elements in the array.
.. code::
# code to be run in micropython
import ulab as np
a = np.array([1, 2, 3], dtype=np.int8)
print("a:\n", a)
print("size of a:", a.size)
b= np.array([[1, 2], [3, 4]], dtype=np.int8)
print("\nb:\n", b)
print("size of b:", b.size)
.. parsed-literal::
a:
array([1, 2, 3], dtype=int8)
size of a: 3
b:
array([[1, 2],
[3, 4]], dtype=int8)
size of b: 4
.itemsize
~~~~~~~~~
The ``.itemsize`` method (property) returns an integer with the siz
enumber of elements in the array.
.. code::
# code to be run in micropython
import ulab as np
a = np.array([1, 2, 3], dtype=np.int8)
print("a:\n", a)
print("itemsize of a:", a.itemsize)
b= np.array([[1, 2], [3, 4]], dtype=np.float)
print("\nb:\n", b)
print("itemsize of b:", b.itemsize)
.. parsed-literal::
a:
array([1, 2, 3], dtype=int8)
itemsize of a: 1
b:
array([[1.0, 2.0],
[3.0, 4.0]], dtype=float)
itemsize of b: 8
.reshape
~~~~~~~~
@ -462,41 +559,6 @@ consistent with the old, a ``ValueError`` exception will be raised.
.rawsize
~~~~~~~~
The ``rawsize`` method of the ``ndarray`` returns a 5-tuple with the
following data
1. number of rows
2. number of columns
3. length of the storage (should be equal to the product of 1. and 2.)
4. length of the data storage in bytes
5. datum size in bytes (1 for ``uint8``/``int8``, 2 for
``uint16``/``int16``, and 4, or 8 for ``floats``, see `ndarray, the
basic container <#ndarray,-the-basic-container>`__)
**WARNING:** ``rawsize`` is a ``ulab``-only method; it has no equivalent
in ``numpy``.
.. code::
# code to be run in micropython
import ulab as np
a = np.array([1, 2, 3, 4], dtype=np.float)
print("a: \t\t", a)
print("rawsize of a: \t", a.rawsize())
.. parsed-literal::
a: array([1.0, 2.0, 3.0, 4.0], dtype=float)
rawsize of a: (1, 4, 4, 16, 4)
.flatten
~~~~~~~~

View file

@ -1,4 +1,10 @@
Tue, 11 Feb 2020
version 0.32.0
added itemsize, size and shape attributes to ndarrays, and removed rawsize
Mon, 10 Feb 2020
version 0.31.0

View file

@ -24,11 +24,11 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 14,
"metadata": {
"ExecuteTime": {
"end_time": "2020-02-10T18:31:42.227494Z",
"start_time": "2020-02-10T18:31:42.222100Z"
"end_time": "2020-02-11T19:06:35.427133Z",
"start_time": "2020-02-11T19:06:35.418598Z"
}
},
"outputs": [
@ -66,7 +66,7 @@
"author = 'Zoltán Vörös'\n",
"\n",
"# The full version, including alpha/beta/rc tags\n",
"release = '0.31'\n",
"release = '0.32'\n",
"\n",
"\n",
"# -- General configuration ---------------------------------------------------\n",
@ -120,11 +120,11 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 16,
"metadata": {
"ExecuteTime": {
"end_time": "2020-02-10T18:49:03.206016Z",
"start_time": "2020-02-10T18:49:00.047068Z"
"end_time": "2020-02-11T20:30:43.287360Z",
"start_time": "2020-02-11T20:30:40.308932Z"
}
},
"outputs": [],
@ -478,7 +478,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"In https://micropython-usermod.readthedocs.io/en/latest/usermods_14.html, I mentioned that I have another story, for another day. The day has come, so here is my story.\n",
"In the [last chapter](https://micropython-usermod.readthedocs.io/en/latest/usermods_15.html) of the usermod documentation, I mentioned that I have another story, for another day. The day has come, so here is my story.\n",
"\n",
"## Enter ulab\n",
"\n",
@ -506,7 +506,7 @@
"- polynomial fits to numerical data\n",
"- fast Fourier transforms\n",
"\n",
"At the time of writing this manual (for version 0.26), the library adds approximately 30 kB of extra compiled code to the micropython (pyboard.v.11) firmware. \n",
"At the time of writing this manual (for version 0.32), the library adds approximately 30 kB of extra compiled code to the micropython (pyboard.v.11) firmware. However, if you are tight with flash space, you can easily shave off a couple of kB. See the section on [customising ulab](#Custom_builds).\n",
"\n",
"## Resources and legal matters\n",
"\n",
@ -569,6 +569,29 @@
"If you find a bug, please, include this number in your report!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Customising `ulab`\n",
"\n",
"`ulab` implements a great number of functions, and it is quite possible that you do not need all of them in a particular application. If you want to save some flash space, you can easily exclude arbitrary functions from the firmware. The [https://github.com/v923z/micropython-ulab/blob/master/code/ulab.h](ulab.h) header file contains a pre-processor flag for all functions in `ulab`. The default setting is 1 for each of them, but if you change that to 0, the corresponding function will not be part of the compiled firmware. \n",
"\n",
"The first couple of lines of the file look like this\n",
"\n",
"```c\n",
"// vectorise (all functions) takes approx. 3 kB of flash space\n",
"#define ULAB_VECTORISE_ACOS (1)\n",
"#define ULAB_VECTORISE_ACOSH (1)\n",
"#define ULAB_VECTORISE_ASIN (1)\n",
"#define ULAB_VECTORISE_ASINH (1)\n",
"#define ULAB_VECTORISE_ATAN (1)\n",
"#define ULAB_VECTORISE_ATANH (1)\n",
"```\n",
"\n",
"In order to simplify navigation in the file, each flag begins with `ULAB_`, continues with the sub-module, where the function itself is implemented, and ends with the function's name. Each section displays a hint as to how much space you can save by un-setting the flag."
]
},
{
"cell_type": "markdown",
"metadata": {},
@ -594,8 +617,6 @@
"\n",
"[.reshape](#.reshape)\n",
"\n",
"[.rawsize<sup>**</sup>](#.rawsize)\n",
"\n",
"[.transpose](#.transpose)\n",
"\n",
"[.flatten<sup>**</sup>](#.flatten)\n",
@ -845,16 +866,16 @@
"source": [
"### .shape\n",
"\n",
"The `.shape` method returns a 2-tuple with the number of rows, and columns."
"The `.shape` method (property) returns a 2-tuple with the number of rows, and columns."
]
},
{
"cell_type": "code",
"execution_count": 283,
"execution_count": 12,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-16T15:30:33.810628Z",
"start_time": "2019-10-16T15:30:33.796088Z"
"end_time": "2020-02-11T19:01:40.377272Z",
"start_time": "2020-02-11T19:01:40.364448Z"
}
},
"outputs": [
@ -882,11 +903,111 @@
"\n",
"a = np.array([1, 2, 3, 4], dtype=np.int8)\n",
"print(\"a:\\n\", a)\n",
"print(\"shape of a:\", a.shape())\n",
"print(\"shape of a:\", a.shape)\n",
"\n",
"b= np.array([[1, 2], [3, 4]], dtype=np.int8)\n",
"print(\"\\nb:\\n\", b)\n",
"print(\"shape of b:\", b.shape())"
"print(\"shape of b:\", b.shape)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### .size\n",
"\n",
"The `.size` method (property) returns an integer with the number of elements in the array."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"ExecuteTime": {
"end_time": "2020-02-11T06:32:22.721112Z",
"start_time": "2020-02-11T06:32:22.713111Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"a:\n",
" array([1, 2, 3], dtype=int8)\n",
"size of a: 3\n",
"\n",
"b:\n",
" array([[1, 2],\n",
"\t [3, 4]], dtype=int8)\n",
"size of b: 4\n",
"\n",
"\n"
]
}
],
"source": [
"%%micropython -unix 1\n",
"\n",
"import ulab as np\n",
"\n",
"a = np.array([1, 2, 3], dtype=np.int8)\n",
"print(\"a:\\n\", a)\n",
"print(\"size of a:\", a.size)\n",
"\n",
"b= np.array([[1, 2], [3, 4]], dtype=np.int8)\n",
"print(\"\\nb:\\n\", b)\n",
"print(\"size of b:\", b.size)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### .itemsize\n",
"\n",
"The `.itemsize` method (property) returns an integer with the siz enumber of elements in the array."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"ExecuteTime": {
"end_time": "2020-02-11T19:05:04.296601Z",
"start_time": "2020-02-11T19:05:04.280669Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"a:\n",
" array([1, 2, 3], dtype=int8)\n",
"itemsize of a: 1\n",
"\n",
"b:\n",
" array([[1.0, 2.0],\n",
"\t [3.0, 4.0]], dtype=float)\n",
"itemsize of b: 8\n",
"\n",
"\n"
]
}
],
"source": [
"%%micropython -unix 1\n",
"\n",
"import ulab as np\n",
"\n",
"a = np.array([1, 2, 3], dtype=np.int8)\n",
"print(\"a:\\n\", a)\n",
"print(\"itemsize of a:\", a.itemsize)\n",
"\n",
"b= np.array([[1, 2], [3, 4]], dtype=np.float)\n",
"print(\"\\nb:\\n\", b)\n",
"print(\"itemsize of b:\", b.itemsize)"
]
},
{
@ -937,54 +1058,6 @@
"print('a (1 by 16):', a.reshape((1, 16)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### .rawsize\n",
"\n",
"The `rawsize` method of the `ndarray` returns a 5-tuple with the following data\n",
"\n",
"1. number of rows\n",
"2. number of columns\n",
"3. length of the storage (should be equal to the product of 1. and 2.)\n",
"4. length of the data storage in bytes \n",
"5. datum size in bytes (1 for `uint8`/`int8`, 2 for `uint16`/`int16`, and 4, or 8 for `floats`, see [ndarray, the basic container](#ndarray,-the-basic-container))\n",
"\n",
"**WARNING:** `rawsize` is a `ulab`-only method; it has no equivalent in `numpy`."
]
},
{
"cell_type": "code",
"execution_count": 510,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-19T17:44:26.983908Z",
"start_time": "2019-10-19T17:44:26.764912Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"a: \t\t array([1.0, 2.0, 3.0, 4.0], dtype=float)\n",
"rawsize of a: \t (1, 4, 4, 16, 4)\n",
"\n",
"\n"
]
}
],
"source": [
"%%micropython -unix 1\n",
"\n",
"import ulab as np\n",
"\n",
"a = np.array([1, 2, 3, 4], dtype=np.float)\n",
"print(\"a: \\t\\t\", a)\n",
"print(\"rawsize of a: \\t\", a.rawsize())"
]
},
{
"cell_type": "markdown",
"metadata": {},

View file

@ -65,11 +65,11 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2020-02-10T18:20:10.944642Z",
"start_time": "2020-02-10T18:20:10.924517Z"
"end_time": "2020-02-11T20:33:18.577747Z",
"start_time": "2020-02-11T20:33:18.573974Z"
}
},
"outputs": [
@ -87,11 +87,11 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 3,
"metadata": {
"ExecuteTime": {
"end_time": "2020-02-10T18:20:13.006014Z",
"start_time": "2020-02-10T18:20:13.002944Z"
"end_time": "2020-02-11T20:33:20.962959Z",
"start_time": "2020-02-11T20:33:20.959323Z"
}
},
"outputs": [],
@ -137,8 +137,8 @@
"execution_count": 4,
"metadata": {
"ExecuteTime": {
"end_time": "2020-02-10T18:20:20.164916Z",
"start_time": "2020-02-10T18:20:20.148842Z"
"end_time": "2020-02-11T20:33:23.430858Z",
"start_time": "2020-02-11T20:33:23.371937Z"
}
},
"outputs": [],
@ -328,8 +328,8 @@
"execution_count": 5,
"metadata": {
"ExecuteTime": {
"end_time": "2020-02-10T18:20:23.136432Z",
"start_time": "2020-02-10T18:20:23.131323Z"
"end_time": "2020-02-11T20:33:26.033162Z",
"start_time": "2020-02-11T20:33:26.022590Z"
}
},
"outputs": [],
@ -485,11 +485,11 @@
},
{
"cell_type": "code",
"execution_count": 146,
"execution_count": 6,
"metadata": {
"ExecuteTime": {
"end_time": "2020-02-08T09:29:05.558859Z",
"start_time": "2020-02-08T09:29:05.554463Z"
"end_time": "2020-02-11T20:33:32.082924Z",
"start_time": "2020-02-11T20:33:32.077298Z"
}
},
"outputs": [
@ -497,7 +497,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"written 2222 bytes to ulab.h\n"
"written 2257 bytes to ulab.h\n"
]
}
],
@ -571,6 +571,7 @@
"#define ULAB_NUMERICAL_FLIP (1)\n",
"#define ULAB_NUMERICAL_DIFF (1)\n",
"#define ULAB_NUMERICAL_SORT (1)\n",
"#define ULAB_NUMERICAL_ARGSORT (1)\n",
"\n",
"// FFT costs about 2 kB of flash space\n",
"#define ULAB_FFT_FFT (1)\n",
@ -3257,11 +3258,11 @@
},
{
"cell_type": "code",
"execution_count": 47,
"execution_count": 94,
"metadata": {
"ExecuteTime": {
"end_time": "2020-02-10T18:53:20.754983Z",
"start_time": "2020-02-10T18:53:20.750119Z"
"end_time": "2020-02-11T18:55:55.931364Z",
"start_time": "2020-02-11T18:55:55.925214Z"
}
},
"outputs": [
@ -3269,7 +3270,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"written 5176 bytes to ndarray.h\n"
"written 5229 bytes to ndarray.h\n"
]
}
],
@ -3342,10 +3343,12 @@
"mp_obj_t ndarray_unary_op(mp_unary_op_t , mp_obj_t );\n",
"\n",
"mp_obj_t ndarray_shape(mp_obj_t );\n",
"mp_obj_t ndarray_rawsize(mp_obj_t );\n",
"mp_obj_t ndarray_size(mp_obj_t );\n",
"mp_obj_t ndarray_flatten(size_t , const mp_obj_t *, mp_map_t *);\n",
"mp_int_t ndarray_get_buffer(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags);\n",
"\n",
"void ndarray_attributes(mp_obj_t , qstr , mp_obj_t *);\n",
"\n",
"#define CREATE_SINGLE_ITEM(outarray, type, typecode, value) do {\\\n",
" ndarray_obj_t *tmp = create_new_ndarray(1, 1, (typecode));\\\n",
" type *tmparr = (type *)tmp->array->items;\\\n",
@ -3415,11 +3418,11 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 95,
"metadata": {
"ExecuteTime": {
"end_time": "2020-02-10T18:25:25.962312Z",
"start_time": "2020-02-10T18:25:25.949802Z"
"end_time": "2020-02-11T18:56:36.562847Z",
"start_time": "2020-02-11T18:56:36.550136Z"
},
"code_folding": []
},
@ -3428,7 +3431,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"written 41633 bytes to ndarray.c\n"
"written 41568 bytes to ndarray.c\n"
]
}
],
@ -4060,22 +4063,14 @@
" return mp_obj_new_tuple(2, tuple);\n",
"}\n",
"\n",
"mp_obj_t ndarray_rawsize(mp_obj_t self_in) {\n",
" // returns a 5-tuple with the \n",
" // \n",
" // 0. number of rows\n",
" // 1. number of columns\n",
" // 2. length of the storage (should be equal to the product of 1. and 2.)\n",
" // 3. length of the data storage in bytes\n",
" // 4. datum size in bytes\n",
"mp_obj_t ndarray_size(mp_obj_t self_in) {\n",
" ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in);\n",
" mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(5, NULL));\n",
" tuple->items[0] = MP_OBJ_NEW_SMALL_INT(self->m);\n",
" tuple->items[1] = MP_OBJ_NEW_SMALL_INT(self->n);\n",
" tuple->items[2] = MP_OBJ_NEW_SMALL_INT(self->array->len);\n",
" tuple->items[3] = MP_OBJ_NEW_SMALL_INT(self->bytes);\n",
" tuple->items[4] = MP_OBJ_NEW_SMALL_INT(mp_binary_get_size('@', self->array->typecode, NULL));\n",
" return tuple;\n",
" return mp_obj_new_int(self->array->len);\n",
"}\n",
"\n",
"mp_obj_t ndarray_itemsize(mp_obj_t self_in) {\n",
" ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in);\n",
" return MP_OBJ_NEW_SMALL_INT(mp_binary_get_size('@', self->array->typecode, NULL));\n",
"}\n",
"\n",
"mp_obj_t ndarray_flatten(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {\n",
@ -4344,6 +4339,16 @@
" ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in);\n",
" // buffer_p.get_buffer() returns zero for success, while mp_get_buffer returns true for success\n",
" return !mp_get_buffer(self->array, bufinfo, flags);\n",
"}\n",
"\n",
"void ndarray_attributes(mp_obj_t self_in, qstr attribute, mp_obj_t *destination) {\n",
" if(attribute == MP_QSTR_size) {\n",
" destination[0] = ndarray_size(self_in);\n",
" } else if(attribute == MP_QSTR_itemsize) {\n",
" destination[0] = ndarray_itemsize(self_in);\n",
" } else if(attribute == MP_QSTR_shape) {\n",
" destination[0] = ndarray_shape(self_in);\n",
" }\n",
"}"
]
},
@ -8689,11 +8694,11 @@
},
{
"cell_type": "code",
"execution_count": 12,
"execution_count": 7,
"metadata": {
"ExecuteTime": {
"end_time": "2020-02-10T18:26:42.212459Z",
"start_time": "2020-02-10T18:26:42.206522Z"
"end_time": "2020-02-11T20:34:46.008810Z",
"start_time": "2020-02-11T20:34:46.001076Z"
}
},
"outputs": [
@ -8701,7 +8706,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"written 8310 bytes to ulab.c\n"
"written 8137 bytes to ulab.c\n"
]
}
],
@ -8736,16 +8741,13 @@
"#include \"filter.h\"\n",
"#include \"numerical.h\"\n",
"\n",
"STATIC MP_DEFINE_STR_OBJ(ulab_version_obj, \"0.31.0\");\n",
"STATIC MP_DEFINE_STR_OBJ(ulab_version_obj, \"0.32.0\");\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",
"MP_DEFINE_CONST_FUN_OBJ_KW(ndarray_flatten_obj, 1, ndarray_flatten);\n",
"\n",
"STATIC const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = {\n",
" { MP_ROM_QSTR(MP_QSTR_shape), MP_ROM_PTR(&ndarray_shape_obj) },\n",
" { MP_ROM_QSTR(MP_QSTR_rawsize), MP_ROM_PTR(&ndarray_rawsize_obj) },\n",
" { MP_ROM_QSTR(MP_QSTR_flatten), MP_ROM_PTR(&ndarray_flatten_obj) }, \n",
" { MP_ROM_QSTR(MP_QSTR_flatten), MP_ROM_PTR(&ndarray_flatten_obj) },\n",
" #if ULAB_LINALG_TRANSPOSE\n",
" { MP_ROM_QSTR(MP_QSTR_transpose), MP_ROM_PTR(&linalg_transpose_obj) },\n",
" #endif\n",
@ -8768,6 +8770,7 @@
" .binary_op = ndarray_binary_op,\n",
" .buffer_p = { .get_buffer = ndarray_get_buffer, },\n",
" .locals_dict = (mp_obj_dict_t*)&ulab_ndarray_locals_dict,\n",
" .attr = ndarray_attributes,\n",
"};\n",
"\n",
"STATIC const mp_map_elem_t ulab_globals_table[] = {\n",
@ -8855,7 +8858,7 @@
" #if ULAB_VECTORISE_SIN\n",
" { MP_OBJ_NEW_QSTR(MP_QSTR_sin), (mp_obj_t)&vectorise_sin_obj },\n",
" #endif\n",
" #if ULAB_VECTORISE_\n",
" #if ULAB_VECTORISE_SINH\n",
" { MP_OBJ_NEW_QSTR(MP_QSTR_sinh), (mp_obj_t)&vectorise_sinh_obj },\n",
" #endif\n",
" #if ULAB_VECTORISE_SQRT\n",
@ -8864,7 +8867,7 @@
" #if ULAB_VECTORISE_TAN\n",
" { MP_OBJ_NEW_QSTR(MP_QSTR_tan), (mp_obj_t)&vectorise_tan_obj },\n",
" #endif\n",
" #if ULAB_VECTORISE_TAHN\n",
" #if ULAB_VECTORISE_TANH\n",
" { MP_OBJ_NEW_QSTR(MP_QSTR_tanh), (mp_obj_t)&vectorise_tanh_obj },\n",
" #endif\n",
" #if ULAB_NUMERICAL_LINSPACE\n",
@ -9028,11 +9031,11 @@
},
{
"cell_type": "code",
"execution_count": 22,
"execution_count": 87,
"metadata": {
"ExecuteTime": {
"end_time": "2020-02-10T18:38:17.217214Z",
"start_time": "2020-02-10T18:38:16.985221Z"
"end_time": "2020-02-11T18:47:50.796938Z",
"start_time": "2020-02-11T18:47:50.497781Z"
}
},
"outputs": [
@ -9056,8 +9059,8 @@
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2020-02-10T18:52:42.290540Z",
"start_time": "2020-02-10T18:52:39.726501Z"
"end_time": "2020-02-11T18:57:44.969887Z",
"start_time": "2020-02-11T18:57:26.383775Z"
},
"scrolled": true
},
@ -9066,6 +9069,35 @@
"!make USER_C_MODULES=../../../ulab CFLAGS_EXTRA=-DMODULE_ULAB_ENABLED=1 all"
]
},
{
"cell_type": "code",
"execution_count": 92,
"metadata": {
"ExecuteTime": {
"end_time": "2020-02-11T18:55:17.045304Z",
"start_time": "2020-02-11T18:55:17.034858Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"8 3\n",
"\n",
"\n"
]
}
],
"source": [
"%%micropython -unix 1\n",
"\n",
"import ulab\n",
"\n",
"a = ulab.array([1, 2, 3])\n",
"print(a.itemsize, a.size)"
]
},
{
"cell_type": "markdown",
"metadata": {},
@ -9119,11 +9151,11 @@
},
{
"cell_type": "code",
"execution_count": 45,
"execution_count": 100,
"metadata": {
"ExecuteTime": {
"end_time": "2020-02-10T18:46:01.354625Z",
"start_time": "2020-02-10T18:46:01.346144Z"
"end_time": "2020-02-11T18:57:50.102055Z",
"start_time": "2020-02-11T18:57:50.096070Z"
}
},
"outputs": [
@ -9138,6 +9170,12 @@
"source": [
"%%writefile ../../../ulab/docs/ulab-change-log.md\n",
"\n",
"Tue, 11 Feb 2020\n",
"\n",
"version 0.32.0\n",
"\n",
" added itemsize, size and shape attributes to ndarrays, and removed rawsize\n",
"\n",
"Mon, 10 Feb 2020\n",
"\n",
"version 0.31.0\n",