From 1ce56916e8455b812310b8ed28d2f1c3d95dae3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 22 Jul 2023 11:47:52 +0200 Subject: [PATCH 01/15] add random module skeleton --- code/micropython.mk | 1 + code/numpy/numpy.c | 4 ++++ code/numpy/random/random.c | 41 ++++++++++++++++++++++++++++++++++++++ code/numpy/random/random.h | 19 ++++++++++++++++++ code/ulab.h | 10 ++++++++++ 5 files changed, 75 insertions(+) create mode 100644 code/numpy/random/random.c create mode 100644 code/numpy/random/random.h diff --git a/code/micropython.mk b/code/micropython.mk index eb517926..4aa6f615 100644 --- a/code/micropython.mk +++ b/code/micropython.mk @@ -25,6 +25,7 @@ SRC_USERMOD += $(USERMODULES_DIR)/numpy/linalg/linalg.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/linalg/linalg_tools.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/numerical.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/poly.c +SRC_USERMOD += $(USERMODULES_DIR)/numpy/random/random.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/stats.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/transform.c SRC_USERMOD += $(USERMODULES_DIR)/numpy/vector.c diff --git a/code/numpy/numpy.c b/code/numpy/numpy.c index d530638e..06d0174c 100644 --- a/code/numpy/numpy.c +++ b/code/numpy/numpy.c @@ -27,6 +27,7 @@ #include "io/io.h" #include "linalg/linalg.h" #include "numerical.h" +#include "random/random.h" #include "stats.h" #include "transform.h" #include "poly.h" @@ -110,6 +111,9 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { #if ULAB_NUMPY_HAS_LINALG_MODULE { MP_ROM_QSTR(MP_QSTR_linalg), MP_ROM_PTR(&ulab_linalg_module) }, #endif + #if ULAB_NUMPY_HAS_RANDOM_MODULE + { MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&ulab_numpy_random_module) }, + #endif #if ULAB_HAS_PRINTOPTIONS { MP_ROM_QSTR(MP_QSTR_set_printoptions), MP_ROM_PTR(&ndarray_set_printoptions_obj) }, { MP_ROM_QSTR(MP_QSTR_get_printoptions), MP_ROM_PTR(&ndarray_get_printoptions_obj) }, diff --git a/code/numpy/random/random.c b/code/numpy/random/random.c new file mode 100644 index 00000000..2ef051b5 --- /dev/null +++ b/code/numpy/random/random.c @@ -0,0 +1,41 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Zoltán Vörös +*/ + +#include "py/builtin.h" +#include "py/obj.h" +#include "py/runtime.h" + +#include "../../ndarray.h" +#include "random.h" + +#if ULAB_NUMPY_RANDOM_HAS_UNIFORM +static mp_obj_t random_uniform(mp_obj_t oin) { + (void)oin; + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_1(random_uniform_obj, random_uniform); +#endif /* ULAB_NUMPY_RANDOM_HAS_UNIFORM */ + + +STATIC const mp_rom_map_elem_t ulab_numpy_random_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_random) }, + #if ULAB_NUMPY_RANDOM_HAS_UNIFORM + { MP_ROM_QSTR(MP_QSTR_uniform), MP_ROM_PTR(&random_uniform_obj) }, + #endif +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_numpy_random_globals, ulab_numpy_random_globals_table); + +const mp_obj_module_t ulab_numpy_random_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_ulab_numpy_random_globals, +}; + diff --git a/code/numpy/random/random.h b/code/numpy/random/random.h new file mode 100644 index 00000000..9c2cf52d --- /dev/null +++ b/code/numpy/random/random.h @@ -0,0 +1,19 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Zoltán Vörös +*/ + +#ifndef _NUMPY_RANDOM_ +#define _NUMPY_RANDOM_ + + +extern const mp_obj_module_t ulab_numpy_random_module; + +MP_DECLARE_CONST_FUN_OBJ_1(random_uniform_obj); + +#endif diff --git a/code/ulab.h b/code/ulab.h index 26df82fb..da51a509 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -697,6 +697,16 @@ #define ULAB_NUMPY_HAS_SORT_COMPLEX (1) #endif +// random module +#ifndef ULAB_NUMPY_HAS_RANDOM_MODULE +#define ULAB_NUMPY_HAS_RANDOM_MODULE (1) +#endif + +#ifndef ULAB_NUMPY_RANDOM_HAS_UNIFORM +#define ULAB_NUMPY_RANDOM_HAS_UNIFORM (1) +#endif + + // scipy modules #ifndef ULAB_SCIPY_HAS_LINALG_MODULE #define ULAB_SCIPY_HAS_LINALG_MODULE (1) From f0f565280935540ba17536b9a9c5d98bf3a2e766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 22 Jul 2023 20:23:10 +0200 Subject: [PATCH 02/15] add Generator object --- code/numpy/random/random.c | 48 ++++++++++++++++++++++++++++++++++++++ code/numpy/random/random.h | 15 ++++++++++++ 2 files changed, 63 insertions(+) diff --git a/code/numpy/random/random.c b/code/numpy/random/random.c index 2ef051b5..ec3ed02a 100644 --- a/code/numpy/random/random.c +++ b/code/numpy/random/random.c @@ -15,6 +15,53 @@ #include "../../ndarray.h" #include "random.h" + +// random's Generator object is defined here +#if defined(MP_DEFINE_CONST_OBJ_TYPE) +MP_DEFINE_CONST_OBJ_TYPE( + random_generator_type, + MP_QSTR_generator, + MP_TYPE_FLAG_NONE, + print, random_generator_print, + make_new, random_generator_make_new +); +#else +const mp_obj_type_t random_generator_type = { + { &mp_type_type }, + .name = MP_QSTR_generator, + .print = random_generator_print, + .make_new = random_generator_make_new, +}; +#endif + +void random_generator_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + random_generator_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(MP_PYTHON_PRINTER, "Gnerator() at 0x%p", self); +} + +mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void) type; + mp_arg_check_num(n_args, n_kw, 0, 1, true); + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + }; + mp_arg_val_t _args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, _args); + + random_generator_obj_t *generator = m_new_obj(random_generator_obj_t); + generator->base.type = &random_generator_type; + // we should add the seed here! + generator->value = 1; + + return MP_OBJ_FROM_PTR(generator); +} + +// END OF GENERATOR COMPONENTS + #if ULAB_NUMPY_RANDOM_HAS_UNIFORM static mp_obj_t random_uniform(mp_obj_t oin) { (void)oin; @@ -27,6 +74,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(random_uniform_obj, random_uniform); STATIC const mp_rom_map_elem_t ulab_numpy_random_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_random) }, + { MP_ROM_QSTR(MP_QSTR_Generator), MP_ROM_PTR(&random_generator_type) }, #if ULAB_NUMPY_RANDOM_HAS_UNIFORM { MP_ROM_QSTR(MP_QSTR_uniform), MP_ROM_PTR(&random_uniform_obj) }, #endif diff --git a/code/numpy/random/random.h b/code/numpy/random/random.h index 9c2cf52d..fa98690a 100644 --- a/code/numpy/random/random.h +++ b/code/numpy/random/random.h @@ -14,6 +14,21 @@ extern const mp_obj_module_t ulab_numpy_random_module; +extern const mp_obj_type_t random_generator_type; + +typedef struct _random_generator_obj_t { + mp_obj_base_t base; + uint32_t value; + // const mp_obj_type_t *type; + +} random_generator_obj_t; + +MP_DECLARE_CONST_FUN_OBJ_KW(random_generator_constructor_obj); +mp_obj_t random_generator_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *); +void random_generator_print(const mp_print_t *, mp_obj_t , mp_print_kind_t ); + + + MP_DECLARE_CONST_FUN_OBJ_1(random_uniform_obj); #endif From 29f193b808c6629b1908b58ae549d96e687b4bd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sun, 23 Jul 2023 20:31:53 +0200 Subject: [PATCH 03/15] add placeholder for random.random method --- code/numpy/random/random.c | 43 ++++++++++++++++++++++++++++++++------ code/numpy/random/random.h | 5 +---- code/ulab.h | 4 ++++ 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/code/numpy/random/random.c b/code/numpy/random/random.c index ec3ed02a..2e5ef8a3 100644 --- a/code/numpy/random/random.c +++ b/code/numpy/random/random.c @@ -15,6 +15,17 @@ #include "../../ndarray.h" #include "random.h" +// methods of the Generator object +static const mp_rom_map_elem_t random_generator_locals_dict_table[] = { + #if ULAB_NUMPY_RANDOM_HAS_RANDOM + { MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&random_random_obj) }, + #endif + #if ULAB_NUMPY_RANDOM_HAS_UNIFORM + { MP_ROM_QSTR(MP_QSTR_uniform), MP_ROM_PTR(&random_uniform_obj) }, + #endif +}; + +static MP_DEFINE_CONST_DICT(random_generator_locals_dict, random_generator_locals_dict_table); // random's Generator object is defined here #if defined(MP_DEFINE_CONST_OBJ_TYPE) @@ -23,7 +34,8 @@ MP_DEFINE_CONST_OBJ_TYPE( MP_QSTR_generator, MP_TYPE_FLAG_NONE, print, random_generator_print, - make_new, random_generator_make_new + make_new, random_generator_make_new, + locals_dict, &random_generator_locals_dict ); #else const mp_obj_type_t random_generator_type = { @@ -31,6 +43,7 @@ const mp_obj_type_t random_generator_type = { .name = MP_QSTR_generator, .print = random_generator_print, .make_new = random_generator_make_new, + .locals_dict = (mp_obj_dict_t*)&random_generator_locals_dict }; #endif @@ -62,6 +75,27 @@ mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, siz // END OF GENERATOR COMPONENTS + +#if ULAB_NUMPY_RANDOM_HAS_RANDOM +static mp_obj_t random_random(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_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + }; + + 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); + if(args[0].u_obj != mp_const_none) { + } + if(args[1].u_obj != mp_const_none) { + } + + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(random_random_obj, 1, random_random); +#endif /* ULAB_NUMPY_RANDOM_HAS_RANDOM */ + #if ULAB_NUMPY_RANDOM_HAS_UNIFORM static mp_obj_t random_uniform(mp_obj_t oin) { (void)oin; @@ -72,15 +106,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(random_uniform_obj, random_uniform); #endif /* ULAB_NUMPY_RANDOM_HAS_UNIFORM */ -STATIC const mp_rom_map_elem_t ulab_numpy_random_globals_table[] = { +static const mp_rom_map_elem_t ulab_numpy_random_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_random) }, { MP_ROM_QSTR(MP_QSTR_Generator), MP_ROM_PTR(&random_generator_type) }, - #if ULAB_NUMPY_RANDOM_HAS_UNIFORM - { MP_ROM_QSTR(MP_QSTR_uniform), MP_ROM_PTR(&random_uniform_obj) }, - #endif }; -STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_numpy_random_globals, ulab_numpy_random_globals_table); +static MP_DEFINE_CONST_DICT(mp_module_ulab_numpy_random_globals, ulab_numpy_random_globals_table); const mp_obj_module_t ulab_numpy_random_module = { .base = { &mp_type_module }, diff --git a/code/numpy/random/random.h b/code/numpy/random/random.h index fa98690a..ffb1be34 100644 --- a/code/numpy/random/random.h +++ b/code/numpy/random/random.h @@ -19,16 +19,13 @@ extern const mp_obj_type_t random_generator_type; typedef struct _random_generator_obj_t { mp_obj_base_t base; uint32_t value; - // const mp_obj_type_t *type; - } random_generator_obj_t; -MP_DECLARE_CONST_FUN_OBJ_KW(random_generator_constructor_obj); mp_obj_t random_generator_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *); void random_generator_print(const mp_print_t *, mp_obj_t , mp_print_kind_t ); - +MP_DECLARE_CONST_FUN_OBJ_KW(random_random_obj); MP_DECLARE_CONST_FUN_OBJ_1(random_uniform_obj); #endif diff --git a/code/ulab.h b/code/ulab.h index da51a509..d5844dcd 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -702,6 +702,10 @@ #define ULAB_NUMPY_HAS_RANDOM_MODULE (1) #endif +#ifndef ULAB_NUMPY_RANDOM_HAS_RANDOM +#define ULAB_NUMPY_RANDOM_HAS_RANDOM (1) +#endif + #ifndef ULAB_NUMPY_RANDOM_HAS_UNIFORM #define ULAB_NUMPY_RANDOM_HAS_UNIFORM (1) #endif From 6478ea9a979edc9e0aa3d4bd73a305c835b1fa30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sun, 23 Jul 2023 21:57:19 +0200 Subject: [PATCH 04/15] add rudimentary random.random implementation --- code/ndarray.c | 6 ++-- code/numpy/random/random.c | 69 ++++++++++++++++++++++++++++++++++---- code/numpy/random/random.h | 5 ++- 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/code/ndarray.c b/code/ndarray.c index dd4161f1..0baf9fca 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -651,11 +651,11 @@ ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *_shape, uint8_t dt // creates a dense array from a tuple // the function should work in the general n-dimensional case size_t *shape = m_new(size_t, ULAB_MAX_DIMS); - for(size_t i=0; i < ULAB_MAX_DIMS; i++) { + for(size_t i = 0; i < ULAB_MAX_DIMS; i++) { if(i >= _shape->len) { - shape[ULAB_MAX_DIMS - i] = 0; + shape[ULAB_MAX_DIMS - 1 - i] = 0; } else { - shape[ULAB_MAX_DIMS - i] = mp_obj_get_int(_shape->items[i]); + shape[ULAB_MAX_DIMS - 1 - i] = mp_obj_get_int(_shape->items[i]); } } return ndarray_new_dense_ndarray(_shape->len, shape, dtype); diff --git a/code/numpy/random/random.c b/code/numpy/random/random.c index 2e5ef8a3..40bd3317 100644 --- a/code/numpy/random/random.c +++ b/code/numpy/random/random.c @@ -68,7 +68,7 @@ mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, siz random_generator_obj_t *generator = m_new_obj(random_generator_obj_t); generator->base.type = &random_generator_type; // we should add the seed here! - generator->value = 1; + generator->state = 1; return MP_OBJ_FROM_PTR(generator); } @@ -76,21 +76,76 @@ mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, siz // END OF GENERATOR COMPONENTS +static inline uint32_t pcg32_next(uint64_t *state) { + uint64_t old_state = *state; + *state = old_state * PCG_MULTIPLIER_64 + PCG_INCREMENT_64; + uint32_t value = (uint32_t)((old_state ^ (old_state >> 18)) >> 27); + int rot = old_state >> 59; + return rot ? (value >> rot) | (value << (32 - rot)) : value; +} + +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +static inline uint64_t pcg32_next64(uint64_t *state) { + uint64_t value = pcg32_next(state); + value <<= 32; + value |= pcg32_next(state); + return value; +} +#endif + #if ULAB_NUMPY_RANDOM_HAS_RANDOM static mp_obj_t random_random(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_OBJ, {.u_rom_obj = MP_ROM_NONE} }, - { MP_QSTR_out, 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_ROM_NONE } }, + { MP_QSTR_size, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, }; 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); - if(args[0].u_obj != mp_const_none) { - } - if(args[1].u_obj != mp_const_none) { + + random_generator_obj_t *self = MP_OBJ_TO_PTR(args[0].u_obj); + + mp_obj_t size = args[1].u_obj; + mp_obj_t out = args[2].u_obj; + + if((size == mp_const_none) && (out == mp_const_none)) { + mp_raise_ValueError(translate("cannot determine output shape")); } - return mp_const_none; + ndarray_obj_t *ndarray = NULL; + + if(size != mp_const_none) { + if(!mp_obj_is_type(size, &mp_type_tuple) && !mp_obj_is_int(size)) { + mp_raise_TypeError(translate("shape must be integer or tuple of integers")); + } + if(mp_obj_is_int(size)) { + size_t len = (size_t)mp_obj_get_int(size); + ndarray = ndarray_new_linear_array(len, NDARRAY_FLOAT); + } else { // at this point, size must be a tuple + mp_obj_tuple_t *shape = MP_OBJ_TO_PTR(size); + if(shape->len > ULAB_MAX_DIMS) { + mp_raise_ValueError(translate("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); + } + ndarray = ndarray_new_ndarray_from_tuple(shape, NDARRAY_FLOAT); + } + } + + mp_float_t *array = (mp_float_t *)ndarray->array; + + for(size_t i = 0; i < ndarray->len; i++) { + + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + uint32_t x = pcg32_next(&self->state); + *array = (float)(int32_t)(x >> 8) * 0x1.0p-24f; + #else + uint64_t x = pcg32_next64(&self->state); + *array = (double)(int64_t)(x >> 11) * 0x1.0p-53; + #endif + + array++; + } + return ndarray; } MP_DEFINE_CONST_FUN_OBJ_KW(random_random_obj, 1, random_random); diff --git a/code/numpy/random/random.h b/code/numpy/random/random.h index ffb1be34..71df18fa 100644 --- a/code/numpy/random/random.h +++ b/code/numpy/random/random.h @@ -12,13 +12,16 @@ #define _NUMPY_RANDOM_ +#define PCG_MULTIPLIER_64 6364136223846793005ULL +#define PCG_INCREMENT_64 1442695040888963407ULL + extern const mp_obj_module_t ulab_numpy_random_module; extern const mp_obj_type_t random_generator_type; typedef struct _random_generator_obj_t { mp_obj_base_t base; - uint32_t value; + uint64_t state; } random_generator_obj_t; mp_obj_t random_generator_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *); From ae189d359fb07dde1916fd90bbaa0bc4c99badc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 25 Jul 2023 22:24:18 +0200 Subject: [PATCH 05/15] generator object accept seed(s) argument --- code/numpy/random/random.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/code/numpy/random/random.c b/code/numpy/random/random.c index 40bd3317..f11f35df 100644 --- a/code/numpy/random/random.c +++ b/code/numpy/random/random.c @@ -65,12 +65,29 @@ mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, siz mp_arg_val_t _args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, _args); - random_generator_obj_t *generator = m_new_obj(random_generator_obj_t); - generator->base.type = &random_generator_type; - // we should add the seed here! - generator->state = 1; + if(!mp_obj_is_int(args[0]) && !mp_obj_is_type(args[0], &mp_type_tuple)) { + mp_raise_TypeError(translate("argument must be an integer or a tuple of integers")); + } - return MP_OBJ_FROM_PTR(generator); + if(mp_obj_is_int(args[0])) { + random_generator_obj_t *generator = m_new_obj(random_generator_obj_t); + generator->base.type = &random_generator_type; + generator->state = (size_t)mp_obj_get_int(args[0]); + return MP_OBJ_FROM_PTR(generator); + } else { + mp_obj_tuple_t *seeds = MP_OBJ_TO_PTR(args[0]); + mp_obj_t *items = m_new(mp_obj_t, seeds->len); + + for(uint8_t i = 0; i < seeds->len; i++) { + random_generator_obj_t *generator = m_new_obj(random_generator_obj_t); + generator->base.type = &random_generator_type; + generator->state = (size_t)mp_obj_get_int(seeds->items[i]); + items[i] = generator; + } + return mp_obj_new_tuple(seeds->len, items); + } + // we should never end up here + return mp_const_none; } // END OF GENERATOR COMPONENTS From 63aac6c2a24d5b029d5173b80b2f7e906435cd95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Thu, 28 Sep 2023 20:39:09 +0200 Subject: [PATCH 06/15] add out keyword --- code/numpy/random/random.c | 46 +++++++++++++++++++--------- code/ulab_tools.c | 61 ++++++++++++++++++++++++++++++++++++++ code/ulab_tools.h | 4 +++ 3 files changed, 97 insertions(+), 14 deletions(-) diff --git a/code/numpy/random/random.c b/code/numpy/random/random.c index f11f35df..cdf77a4b 100644 --- a/code/numpy/random/random.c +++ b/code/numpy/random/random.c @@ -65,16 +65,21 @@ mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, siz mp_arg_val_t _args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, _args); - if(!mp_obj_is_int(args[0]) && !mp_obj_is_type(args[0], &mp_type_tuple)) { - mp_raise_TypeError(translate("argument must be an integer or a tuple of integers")); - } - if(mp_obj_is_int(args[0])) { + if(args[0] == mp_const_none) { + #ifndef MICROPY_PY_RANDOM_SEED_INIT_FUNC + mp_raise_ValueError(translate("no default seed")); + #endif + random_generator_obj_t *generator = m_new_obj(random_generator_obj_t); + generator->base.type = &random_generator_type; + generator->state = MICROPY_PY_RANDOM_SEED_INIT_FUNC; + return MP_OBJ_FROM_PTR(generator); + } else if(mp_obj_is_int(args[0])) { random_generator_obj_t *generator = m_new_obj(random_generator_obj_t); generator->base.type = &random_generator_type; generator->state = (size_t)mp_obj_get_int(args[0]); return MP_OBJ_FROM_PTR(generator); - } else { + } else if(mp_obj_is_type(args[0], &mp_type_tuple)){ mp_obj_tuple_t *seeds = MP_OBJ_TO_PTR(args[0]); mp_obj_t *items = m_new(mp_obj_t, seeds->len); @@ -85,6 +90,8 @@ mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, siz items[i] = generator; } return mp_obj_new_tuple(seeds->len, items); + } else { + mp_raise_TypeError(translate("argument must be None, an integer or a tuple of integers")); } // we should never end up here return mp_const_none; @@ -113,7 +120,7 @@ static inline uint64_t pcg32_next64(uint64_t *state) { #if ULAB_NUMPY_RANDOM_HAS_RANDOM static mp_obj_t random_random(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_OBJ, { .u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_size, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, { MP_QSTR_out, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, }; @@ -126,30 +133,41 @@ static mp_obj_t random_random(size_t n_args, const mp_obj_t *pos_args, mp_map_t mp_obj_t size = args[1].u_obj; mp_obj_t out = args[2].u_obj; - if((size == mp_const_none) && (out == mp_const_none)) { - mp_raise_ValueError(translate("cannot determine output shape")); + if(size == mp_const_none) { + // return single value + mp_float_t value; + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + uint32_t x = pcg32_next(&self->state); + value = (float)(int32_t)(x >> 8) * 0x1.0p-24f; + #else + uint64_t x = pcg32_next64(&self->state); + value = (double)(int64_t)(x >> 11) * 0x1.0p-53; + #endif + return mp_obj_new_float(value); } ndarray_obj_t *ndarray = NULL; - if(size != mp_const_none) { - if(!mp_obj_is_type(size, &mp_type_tuple) && !mp_obj_is_int(size)) { - mp_raise_TypeError(translate("shape must be integer or tuple of integers")); - } + if(out != mp_const_none) { + ndarray = MP_OBJ_TO_PTR(out); + } else { if(mp_obj_is_int(size)) { size_t len = (size_t)mp_obj_get_int(size); ndarray = ndarray_new_linear_array(len, NDARRAY_FLOAT); - } else { // at this point, size must be a tuple + } else if(mp_obj_is_type(size, &mp_type_tuple)) { mp_obj_tuple_t *shape = MP_OBJ_TO_PTR(size); if(shape->len > ULAB_MAX_DIMS) { mp_raise_ValueError(translate("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); } ndarray = ndarray_new_ndarray_from_tuple(shape, NDARRAY_FLOAT); + } else { // input type not supported + mp_raise_TypeError(translate("shape must be None, and integer or a tuple of integers")); } } - mp_float_t *array = (mp_float_t *)ndarray->array; + // numpy's random supports only dense output arrays, so we can simply + // loop through the elements in a linear fashion for(size_t i = 0; i < ndarray->len; i++) { #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT diff --git a/code/ulab_tools.c b/code/ulab_tools.c index 514721f7..a6a92a61 100644 --- a/code/ulab_tools.c +++ b/code/ulab_tools.c @@ -274,3 +274,64 @@ bool ulab_tools_mp_obj_is_scalar(mp_obj_t obj) { } #endif } + +#if 0 +#if ULAB_NUMPY_HAS_RANDOM_MODULE +ndarray_obj_t *ulab_tools_create_out(mp_obj_t shape, mp_obj_t out, uint8_t dtype, bool check_dense) { + // raise various exceptions, if the supplied output is not compatible with + // the requested shape or the output of a particular function + + // if no out object is supplied, there is nothing to inspect + if(out == mp_const_none) { + return; + } + + if(mp_obj_is_int(shape)) { + size_t len = (size_t)mp_obj_get_int(shape); + return ndarray_new_linear_array(len, dtype); + } else if(mp_obj_is_type(size, &mp_type_tuple)) { + mp_obj_tuple_t *shape = MP_OBJ_TO_PTR(shape); + if(shape->len > ULAB_MAX_DIMS) { + mp_raise_ValueError(translate("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); + } + if(out != mp_const_none) { + if(!mp_obj_is_type(out, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("out must be an ndarray")); + } + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(out); + if(ndarray->dtype != dtype) { + mp_raise_TypeError(translate("incompatible output dtype")); + } + // some functions require a dense output array + if(check_dense && !ndarray_is_dense(ndarray)) { + mp_raise_ValueError(translate("supplied output array must be contiguous")); + } + + + // check if the requested shape and the output shape are compatible + bool shape_is_compatible = ndarray->ndim == shape->len; + for(uint8_t i = 0; i < ndarray->ndim; i++) { + if(ndarray->shape[ULAB_MAX_DIMS - ndarray->ndim + i] != mp_obj_get_int(shape->items[i])) { + shape_is_compatible = false; + break; + } + } + if(!shape_is_compatible) { + mp_raise_ValueError(translate("size must match out shape when used together")); + } + + } + + + return ndarray_new_ndarray_from_tuple(shape, dtype); + } else { // input type not supported + mp_raise_TypeError(translate("shape must be None, and integer or a tuple of integers")); + } + + + if(mp_obj_is_type(shape, &mp_tuple_type)) { + + } +} +#endif /* ULAB_NUMPY_HAS_RANDOM_MODULE */ +#endif \ No newline at end of file diff --git a/code/ulab_tools.h b/code/ulab_tools.h index 5ae99df9..3e6b81e3 100644 --- a/code/ulab_tools.h +++ b/code/ulab_tools.h @@ -43,4 +43,8 @@ void ulab_rescale_float_strides(int32_t *); #endif bool ulab_tools_mp_obj_is_scalar(mp_obj_t ); + +#if ULAB_NUMPY_HAS_RANDOM_MODULE +ndarray_obj_t *ulab_tools_create_out(mp_obj_tuple_t , mp_obj_t , uint8_t , bool ); +#endif #endif From ea114dfefa60ba16e8a65cb70607ee6518f58c62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 9 Jan 2024 22:19:14 +0100 Subject: [PATCH 07/15] add support for out keyword argument --- code/numpy/random/random.c | 73 +++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 21 deletions(-) diff --git a/code/numpy/random/random.c b/code/numpy/random/random.c index cdf77a4b..b0261c57 100644 --- a/code/numpy/random/random.c +++ b/code/numpy/random/random.c @@ -133,37 +133,68 @@ static mp_obj_t random_random(size_t n_args, const mp_obj_t *pos_args, mp_map_t mp_obj_t size = args[1].u_obj; mp_obj_t out = args[2].u_obj; - if(size == mp_const_none) { - // return single value - mp_float_t value; - #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT - uint32_t x = pcg32_next(&self->state); - value = (float)(int32_t)(x >> 8) * 0x1.0p-24f; - #else - uint64_t x = pcg32_next64(&self->state); - value = (double)(int64_t)(x >> 11) * 0x1.0p-53; - #endif - return mp_obj_new_float(value); - } - ndarray_obj_t *ndarray = NULL; + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + uint8_t ndim = 1; - if(out != mp_const_none) { - ndarray = MP_OBJ_TO_PTR(out); - } else { + if(size != mp_const_none) { if(mp_obj_is_int(size)) { - size_t len = (size_t)mp_obj_get_int(size); - ndarray = ndarray_new_linear_array(len, NDARRAY_FLOAT); + shape[ULAB_MAX_DIMS - 1] = (size_t)mp_obj_get_int(size); } else if(mp_obj_is_type(size, &mp_type_tuple)) { - mp_obj_tuple_t *shape = MP_OBJ_TO_PTR(size); - if(shape->len > ULAB_MAX_DIMS) { + mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size); + if(_shape->len > ULAB_MAX_DIMS) { mp_raise_ValueError(translate("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); } - ndarray = ndarray_new_ndarray_from_tuple(shape, NDARRAY_FLOAT); + ndim = _shape->len; + for(size_t i = 0; i < ULAB_MAX_DIMS; i++) { + if(i >= ndim) { + shape[ULAB_MAX_DIMS - 1 - i] = 0; + } else { + shape[ULAB_MAX_DIMS - 1 - i] = mp_obj_get_int(_shape->items[i]); + } + } } else { // input type not supported mp_raise_TypeError(translate("shape must be None, and integer or a tuple of integers")); } } + + if(out != mp_const_none) { + if(!mp_obj_is_type(out, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("out has wrong type")); + } + + ndarray = MP_OBJ_TO_PTR(out); + + if(ndarray->dtype != NDARRAY_FLOAT) { + mp_raise_TypeError(translate("output array has wrong type")); + } + if(size != mp_const_none) { + for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) { + if(ndarray->shape[i] != shape[i]) { + mp_raise_ValueError(translate("size must match out.shape when used together")); + } + } + } + if(!ndarray_is_dense(ndarray)) { + mp_raise_ValueError(translate("output array must be contiguous")); + } + } else { // out == None + if(size != mp_const_none) { + ndarray = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + } else { + // return single value + mp_float_t value; + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + uint32_t x = pcg32_next(&self->state); + value = (float)(int32_t)(x >> 8) * 0x1.0p-24f; + #else + uint64_t x = pcg32_next64(&self->state); + value = (double)(int64_t)(x >> 11) * 0x1.0p-53; + #endif + return mp_obj_new_float(value); + } + } + mp_float_t *array = (mp_float_t *)ndarray->array; // numpy's random supports only dense output arrays, so we can simply From 8f7e5390e84118af7aaf664febc5dc75759694c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 9 Jan 2024 22:29:23 +0100 Subject: [PATCH 08/15] update change log --- code/ulab.c | 2 +- docs/ulab-change-log.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/code/ulab.c b/code/ulab.c index f8e35bb9..460b3b26 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.4.0 +#define ULAB_VERSION 6.5.0 #define xstr(s) str(s) #define str(s) #s diff --git a/docs/ulab-change-log.md b/docs/ulab-change-log.md index ad4021a4..b3a6675c 100644 --- a/docs/ulab-change-log.md +++ b/docs/ulab-change-log.md @@ -1,3 +1,9 @@ +Tue, 9 Jan 2024 + +version 6.5.0 + + add random module + Thu, 20 Jul 2023 version 6.4.0 From c69c02fe410a517d57b9d9302a0209b4b1679c32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 9 Jan 2024 22:35:23 +0100 Subject: [PATCH 09/15] add links to header files --- code/numpy/random/random.c | 3 +-- code/numpy/random/random.h | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/code/numpy/random/random.c b/code/numpy/random/random.c index b0261c57..40a20772 100644 --- a/code/numpy/random/random.c +++ b/code/numpy/random/random.c @@ -5,14 +5,13 @@ * * The MIT License (MIT) * - * Copyright (c) 2023 Zoltán Vörös + * Copyright (c) 2024 Zoltán Vörös */ #include "py/builtin.h" #include "py/obj.h" #include "py/runtime.h" -#include "../../ndarray.h" #include "random.h" // methods of the Generator object diff --git a/code/numpy/random/random.h b/code/numpy/random/random.h index 71df18fa..c5f0ad51 100644 --- a/code/numpy/random/random.h +++ b/code/numpy/random/random.h @@ -5,9 +5,12 @@ * * The MIT License (MIT) * - * Copyright (c) 2023 Zoltán Vörös + * Copyright (c) 2024 Zoltán Vörös */ +#include "../ulab.h" +#include "../../ndarray.h" + #ifndef _NUMPY_RANDOM_ #define _NUMPY_RANDOM_ From 79ab7926e4d99509850cb1a6f2afdecf412db219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 9 Jan 2024 22:42:25 +0100 Subject: [PATCH 10/15] fix file link --- code/numpy/random/random.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/numpy/random/random.h b/code/numpy/random/random.h index c5f0ad51..2a26155b 100644 --- a/code/numpy/random/random.h +++ b/code/numpy/random/random.h @@ -8,7 +8,7 @@ * Copyright (c) 2024 Zoltán Vörös */ -#include "../ulab.h" +#include "../../ulab.h" #include "../../ndarray.h" #ifndef _NUMPY_RANDOM_ From 9ac0cadb9bc0ad3f81678cefa3935a7eb522a8f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Tue, 9 Jan 2024 22:53:39 +0100 Subject: [PATCH 11/15] fix error messages --- code/numpy/random/random.c | 16 +++++----- code/numpy/random/random.h | 1 - code/ulab_tools.c | 61 -------------------------------------- 3 files changed, 8 insertions(+), 70 deletions(-) diff --git a/code/numpy/random/random.c b/code/numpy/random/random.c index 40a20772..755b590c 100644 --- a/code/numpy/random/random.c +++ b/code/numpy/random/random.c @@ -67,7 +67,7 @@ mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, siz if(args[0] == mp_const_none) { #ifndef MICROPY_PY_RANDOM_SEED_INIT_FUNC - mp_raise_ValueError(translate("no default seed")); + mp_raise_ValueError(MP_ERROR_TEXT("no default seed")); #endif random_generator_obj_t *generator = m_new_obj(random_generator_obj_t); generator->base.type = &random_generator_type; @@ -90,7 +90,7 @@ mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, siz } return mp_obj_new_tuple(seeds->len, items); } else { - mp_raise_TypeError(translate("argument must be None, an integer or a tuple of integers")); + mp_raise_TypeError(MP_ERROR_TEXT("argument must be None, an integer or a tuple of integers")); } // we should never end up here return mp_const_none; @@ -142,7 +142,7 @@ static mp_obj_t random_random(size_t n_args, const mp_obj_t *pos_args, mp_map_t } else if(mp_obj_is_type(size, &mp_type_tuple)) { mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size); if(_shape->len > ULAB_MAX_DIMS) { - mp_raise_ValueError(translate("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); + mp_raise_ValueError(MP_ERROR_TEXT("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); } ndim = _shape->len; for(size_t i = 0; i < ULAB_MAX_DIMS; i++) { @@ -153,29 +153,29 @@ static mp_obj_t random_random(size_t n_args, const mp_obj_t *pos_args, mp_map_t } } } else { // input type not supported - mp_raise_TypeError(translate("shape must be None, and integer or a tuple of integers")); + mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, and integer or a tuple of integers")); } } if(out != mp_const_none) { if(!mp_obj_is_type(out, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("out has wrong type")); + mp_raise_TypeError(MP_ERROR_TEXT("out has wrong type")); } ndarray = MP_OBJ_TO_PTR(out); if(ndarray->dtype != NDARRAY_FLOAT) { - mp_raise_TypeError(translate("output array has wrong type")); + mp_raise_TypeError(MP_ERROR_TEXT("output array has wrong type")); } if(size != mp_const_none) { for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) { if(ndarray->shape[i] != shape[i]) { - mp_raise_ValueError(translate("size must match out.shape when used together")); + mp_raise_ValueError(MP_ERROR_TEXT("size must match out.shape when used together")); } } } if(!ndarray_is_dense(ndarray)) { - mp_raise_ValueError(translate("output array must be contiguous")); + mp_raise_ValueError(MP_ERROR_TEXT("output array must be contiguous")); } } else { // out == None if(size != mp_const_none) { diff --git a/code/numpy/random/random.h b/code/numpy/random/random.h index 2a26155b..c642b60f 100644 --- a/code/numpy/random/random.h +++ b/code/numpy/random/random.h @@ -8,7 +8,6 @@ * Copyright (c) 2024 Zoltán Vörös */ -#include "../../ulab.h" #include "../../ndarray.h" #ifndef _NUMPY_RANDOM_ diff --git a/code/ulab_tools.c b/code/ulab_tools.c index d5530c86..b57f1e66 100644 --- a/code/ulab_tools.c +++ b/code/ulab_tools.c @@ -274,64 +274,3 @@ bool ulab_tools_mp_obj_is_scalar(mp_obj_t obj) { } #endif } - -#if 0 -#if ULAB_NUMPY_HAS_RANDOM_MODULE -ndarray_obj_t *ulab_tools_create_out(mp_obj_t shape, mp_obj_t out, uint8_t dtype, bool check_dense) { - // raise various exceptions, if the supplied output is not compatible with - // the requested shape or the output of a particular function - - // if no out object is supplied, there is nothing to inspect - if(out == mp_const_none) { - return; - } - - if(mp_obj_is_int(shape)) { - size_t len = (size_t)mp_obj_get_int(shape); - return ndarray_new_linear_array(len, dtype); - } else if(mp_obj_is_type(size, &mp_type_tuple)) { - mp_obj_tuple_t *shape = MP_OBJ_TO_PTR(shape); - if(shape->len > ULAB_MAX_DIMS) { - mp_raise_ValueError(translate("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); - } - if(out != mp_const_none) { - if(!mp_obj_is_type(out, &ulab_ndarray_type)) { - mp_raise_TypeError(translate("out must be an ndarray")); - } - ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(out); - if(ndarray->dtype != dtype) { - mp_raise_TypeError(translate("incompatible output dtype")); - } - // some functions require a dense output array - if(check_dense && !ndarray_is_dense(ndarray)) { - mp_raise_ValueError(translate("supplied output array must be contiguous")); - } - - - // check if the requested shape and the output shape are compatible - bool shape_is_compatible = ndarray->ndim == shape->len; - for(uint8_t i = 0; i < ndarray->ndim; i++) { - if(ndarray->shape[ULAB_MAX_DIMS - ndarray->ndim + i] != mp_obj_get_int(shape->items[i])) { - shape_is_compatible = false; - break; - } - } - if(!shape_is_compatible) { - mp_raise_ValueError(translate("size must match out shape when used together")); - } - - } - - - return ndarray_new_ndarray_from_tuple(shape, dtype); - } else { // input type not supported - mp_raise_TypeError(translate("shape must be None, and integer or a tuple of integers")); - } - - - if(mp_obj_is_type(shape, &mp_tuple_type)) { - - } -} -#endif /* ULAB_NUMPY_HAS_RANDOM_MODULE */ -#endif \ No newline at end of file From 8f5e32970070b2d561700cc21553c6878975ea18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Wed, 10 Jan 2024 23:07:13 +0100 Subject: [PATCH 12/15] add uniform to random module --- code/ndarray.h | 2 +- code/numpy/random/random.c | 65 ++++++++++++++++++++++++++++++++++---- code/numpy/random/random.h | 2 +- 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/code/ndarray.h b/code/ndarray.h index ec8b3ee7..2ce84bed 100644 --- a/code/ndarray.h +++ b/code/ndarray.h @@ -40,7 +40,7 @@ // Constant float objects are a struct in ROM and are referenced via their pointer. // Use ULAB_DEFINE_FLOAT_CONST to define a constant float object. -// id is the name of the constant, num is it's floating point value. +// id is the name of the constant, num is its floating point value. // hex32 is computed as: hex(int.from_bytes(array.array('f', [num]), 'little')) // hex64 is computed as: hex(int.from_bytes(array.array('d', [num]), 'little')) diff --git a/code/numpy/random/random.c b/code/numpy/random/random.c index 755b590c..8f193004 100644 --- a/code/numpy/random/random.c +++ b/code/numpy/random/random.c @@ -14,6 +14,9 @@ #include "random.h" +ULAB_DEFINE_FLOAT_CONST(random_zero, MICROPY_FLOAT_CONST(0.0), 0UL, 0ULL); +ULAB_DEFINE_FLOAT_CONST(random_one, MICROPY_FLOAT_CONST(1.0), 0x3f800000UL, 0x3ff0000000000000ULL); + // methods of the Generator object static const mp_rom_map_elem_t random_generator_locals_dict_table[] = { #if ULAB_NUMPY_RANDOM_HAS_RANDOM @@ -95,7 +98,6 @@ mp_obj_t random_generator_make_new(const mp_obj_type_t *type, size_t n_args, siz // we should never end up here return mp_const_none; } - // END OF GENERATOR COMPONENTS @@ -210,19 +212,70 @@ static mp_obj_t random_random(size_t n_args, const mp_obj_t *pos_args, mp_map_t array++; } - return ndarray; + return MP_OBJ_FROM_PTR(ndarray); } MP_DEFINE_CONST_FUN_OBJ_KW(random_random_obj, 1, random_random); #endif /* ULAB_NUMPY_RANDOM_HAS_RANDOM */ #if ULAB_NUMPY_RANDOM_HAS_UNIFORM -static mp_obj_t random_uniform(mp_obj_t oin) { - (void)oin; - return mp_const_none; +static mp_obj_t random_uniform(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_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_low, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_zero) } }, + { MP_QSTR_high, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_one) } }, + { MP_QSTR_size, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + }; + + 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); + + random_generator_obj_t *self = MP_OBJ_TO_PTR(args[0].u_obj); + mp_float_t low = mp_obj_get_float(args[1].u_obj); + mp_float_t high = mp_obj_get_float(args[2].u_obj); + mp_obj_t size = args[3].u_obj; + + ndarray_obj_t *ndarray = NULL; + + if(size == mp_const_none) { + // return single value + mp_float_t value; + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + uint32_t x = pcg32_next(&self->state); + value = (float)(int32_t)(x >> 8) * 0x1.0p-24f; + #else + uint64_t x = pcg32_next64(&self->state); + value = (double)(int64_t)(x >> 11) * 0x1.0p-53; + #endif + return mp_obj_new_float(value); + } else if(mp_obj_is_type(size, &mp_type_tuple)) { + mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size); + // TODO: this could be reduced, if the inspection was in the ndarray_new_ndarray_from_tuple function + if(_shape->len > ULAB_MAX_DIMS) { + mp_raise_ValueError(MP_ERROR_TEXT("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); + } + ndarray = ndarray_new_ndarray_from_tuple(_shape, NDARRAY_FLOAT); + } else { // input type not supported + mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, and integer or a tuple of integers")); + } + + mp_float_t *array = (mp_float_t *)ndarray->array; + mp_float_t diff = high - low; + for(size_t i = 0; i < ndarray->len; i++) { + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + uint32_t x = pcg32_next(&self->state); + *array = (float)(int32_t)(x >> 8) * 0x1.0p-24f; + #else + uint64_t x = pcg32_next64(&self->state); + *array = (double)(int64_t)(x >> 11) * 0x1.0p-53; + #endif + *array = low + diff * *array; + array++; + } + return MP_OBJ_FROM_PTR(ndarray); } -MP_DEFINE_CONST_FUN_OBJ_1(random_uniform_obj, random_uniform); +MP_DEFINE_CONST_FUN_OBJ_KW(random_uniform_obj, 1, random_uniform); #endif /* ULAB_NUMPY_RANDOM_HAS_UNIFORM */ diff --git a/code/numpy/random/random.h b/code/numpy/random/random.h index c642b60f..b00222dd 100644 --- a/code/numpy/random/random.h +++ b/code/numpy/random/random.h @@ -31,6 +31,6 @@ void random_generator_print(const mp_print_t *, mp_obj_t , mp_print_kind_t ); MP_DECLARE_CONST_FUN_OBJ_KW(random_random_obj); -MP_DECLARE_CONST_FUN_OBJ_1(random_uniform_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(random_uniform_obj); #endif From 12f4647d412128af21becd71ca6e82c88d39fe9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Fri, 12 Jan 2024 22:26:31 +0100 Subject: [PATCH 13/15] add normal distribution --- code/numpy/random/random.c | 85 ++++++++++++++++++++++++++++++++++++++ code/numpy/random/random.h | 1 + code/ulab.h | 4 ++ 3 files changed, 90 insertions(+) diff --git a/code/numpy/random/random.c b/code/numpy/random/random.c index 8f193004..3bda17c6 100644 --- a/code/numpy/random/random.c +++ b/code/numpy/random/random.c @@ -8,6 +8,8 @@ * Copyright (c) 2024 Zoltán Vörös */ +#include + #include "py/builtin.h" #include "py/obj.h" #include "py/runtime.h" @@ -19,6 +21,9 @@ ULAB_DEFINE_FLOAT_CONST(random_one, MICROPY_FLOAT_CONST(1.0), 0x3f800000UL, 0x3f // methods of the Generator object static const mp_rom_map_elem_t random_generator_locals_dict_table[] = { + #if ULAB_NUMPY_RANDOM_HAS_NORMAL + { MP_ROM_QSTR(MP_QSTR_normal), MP_ROM_PTR(&random_normal_obj) }, + #endif #if ULAB_NUMPY_RANDOM_HAS_RANDOM { MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&random_random_obj) }, #endif @@ -118,6 +123,86 @@ static inline uint64_t pcg32_next64(uint64_t *state) { } #endif +#if ULAB_NUMPY_RANDOM_HAS_NORMAL +static mp_obj_t random_normal(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_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_loc, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_zero) } }, + { MP_QSTR_scale, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_one) } }, + { MP_QSTR_size, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + }; + + 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); + + random_generator_obj_t *self = MP_OBJ_TO_PTR(args[0].u_obj); + mp_float_t loc = mp_obj_get_float(args[1].u_obj); + mp_float_t scale = mp_obj_get_float(args[2].u_obj); + mp_obj_t size = args[3].u_obj; + + ndarray_obj_t *ndarray = NULL; + mp_float_t u, v, value; + + if(size != mp_const_none) { + if(mp_obj_is_int(size)) { + ndarray = ndarray_new_linear_array((size_t)mp_obj_get_int(size), NDARRAY_FLOAT); + } else if(mp_obj_is_type(size, &mp_type_tuple)) { + mp_obj_tuple_t *_shape = MP_OBJ_TO_PTR(size); + if(_shape->len > ULAB_MAX_DIMS) { + mp_raise_ValueError(MP_ERROR_TEXT("maximum number of dimensions is " MP_STRINGIFY(ULAB_MAX_DIMS))); + } + ndarray = ndarray_new_ndarray_from_tuple(_shape, NDARRAY_FLOAT); + } else { // input type not supported + mp_raise_TypeError(MP_ERROR_TEXT("shape must be None, and integer or a tuple of integers")); + } + } else { + // return single value + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + uint32_t x = pcg32_next(&self->state); + u = (float)(int32_t)(x >> 8) * 0x1.0p-24f; + x = pcg32_next(&self->state); + v = (float)(int32_t)(x >> 8) * 0x1.0p-24f; + #else + uint64_t x = pcg32_next64(&self->state); + u = (double)(int64_t)(x >> 11) * 0x1.0p-53; + x = pcg32_next64(&self->state); + v = (double)(int64_t)(x >> 11) * 0x1.0p-53; + #endif + mp_float_t sqrt_log = MICROPY_FLOAT_C_FUN(sqrt)(-MICROPY_FLOAT_CONST(2.0) * MICROPY_FLOAT_C_FUN(log)(u)); + value = sqrt_log * MICROPY_FLOAT_C_FUN(cos)(MICROPY_FLOAT_CONST(2.0) * MP_PI * v); + return mp_obj_new_float(loc + scale * value); + } + + mp_float_t *array = (mp_float_t *)ndarray->array; + + // numpy's random supports only dense output arrays, so we can simply + // loop through the elements in a linear fashion + for(size_t i = 0; i < ndarray->len; i = i + 2) { + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + uint32_t x = pcg32_next(&self->state); + u = (float)(int32_t)(x >> 8) * 0x1.0p-24f; + x = pcg32_next(&self->state); + v = (float)(int32_t)(x >> 8) * 0x1.0p-24f; + #else + uint64_t x = pcg32_next64(&self->state); + u = (double)(int64_t)(x >> 11) * 0x1.0p-53; + x = pcg32_next64(&self->state); + v = (double)(int64_t)(x >> 11) * 0x1.0p-53; + #endif + mp_float_t sqrt_log = MICROPY_FLOAT_C_FUN(sqrt)(-MICROPY_FLOAT_CONST(2.0) * MICROPY_FLOAT_C_FUN(log)(u)); + value = sqrt_log * MICROPY_FLOAT_C_FUN(cos)(MICROPY_FLOAT_CONST(2.0) * MP_PI * v); + *array++ = loc + scale * value; + if((i & 1) == 0) { + value = sqrt_log * MICROPY_FLOAT_C_FUN(sin)(MICROPY_FLOAT_CONST(2.0) * MP_PI * v); + *array++ = loc + scale * value; + } + } + return MP_OBJ_FROM_PTR(ndarray); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(random_normal_obj, 1, random_normal); +#endif /* ULAB_NUMPY_RANDOM_HAS_NORMAL */ + #if ULAB_NUMPY_RANDOM_HAS_RANDOM static mp_obj_t random_random(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { diff --git a/code/numpy/random/random.h b/code/numpy/random/random.h index b00222dd..314fefa0 100644 --- a/code/numpy/random/random.h +++ b/code/numpy/random/random.h @@ -30,6 +30,7 @@ mp_obj_t random_generator_make_new(const mp_obj_type_t *, size_t , size_t , cons void random_generator_print(const mp_print_t *, mp_obj_t , mp_print_kind_t ); +MP_DECLARE_CONST_FUN_OBJ_KW(random_normal_obj); MP_DECLARE_CONST_FUN_OBJ_KW(random_random_obj); MP_DECLARE_CONST_FUN_OBJ_KW(random_uniform_obj); diff --git a/code/ulab.h b/code/ulab.h index d5844dcd..a384bfe2 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -702,6 +702,10 @@ #define ULAB_NUMPY_HAS_RANDOM_MODULE (1) #endif +#ifndef ULAB_NUMPY_RANDOM_HAS_NORMAL +#define ULAB_NUMPY_RANDOM_HAS_NORMAL (1) +#endif + #ifndef ULAB_NUMPY_RANDOM_HAS_RANDOM #define ULAB_NUMPY_RANDOM_HAS_RANDOM (1) #endif From 208adb25f1b46bbc7bbf8735d5260f566f2f0057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 13 Jan 2024 12:21:33 +0100 Subject: [PATCH 14/15] fix argument options in normal and uniform --- code/numpy/random/random.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/numpy/random/random.c b/code/numpy/random/random.c index 3bda17c6..73a34f82 100644 --- a/code/numpy/random/random.c +++ b/code/numpy/random/random.c @@ -127,9 +127,9 @@ static inline uint64_t pcg32_next64(uint64_t *state) { static mp_obj_t random_normal(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_OBJ, { .u_rom_obj = MP_ROM_NONE } }, - { MP_QSTR_loc, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_zero) } }, - { MP_QSTR_scale, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_one) } }, - { MP_QSTR_size, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_loc, MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_zero) } }, + { MP_QSTR_scale, MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_one) } }, + { MP_QSTR_size, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -307,9 +307,9 @@ MP_DEFINE_CONST_FUN_OBJ_KW(random_random_obj, 1, random_random); static mp_obj_t random_uniform(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_OBJ, { .u_rom_obj = MP_ROM_NONE } }, - { MP_QSTR_low, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_zero) } }, - { MP_QSTR_high, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_one) } }, - { MP_QSTR_size, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_low, MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_zero) } }, + { MP_QSTR_high, MP_ARG_OBJ, { .u_rom_obj = ULAB_REFERENCE_FLOAT_CONST(random_one) } }, + { MP_QSTR_size, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; From c19f9c82498d2cc75e782779fdc648d3997fbc8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 13 Jan 2024 15:53:28 +0100 Subject: [PATCH 15/15] update documentation --- code/numpy/vector.c | 10 +- docs/manual/source/conf.py | 4 +- docs/manual/source/index.rst | 1 + docs/manual/source/numpy-functions.rst | 177 +++++++-- docs/manual/source/numpy-universal.rst | 25 +- docs/manual/source/ulab-ndarray.rst | 17 +- docs/numpy-functions.ipynb | 118 +++++- docs/numpy-random.ipynb | 492 +++++++++++++++++++++++++ docs/numpy-universal.ipynb | 35 +- docs/ulab-ndarray.ipynb | 4 +- 10 files changed, 823 insertions(+), 60 deletions(-) create mode 100644 docs/numpy-random.ipynb diff --git a/code/numpy/vector.c b/code/numpy/vector.c index cd8fef3e..0df3a338 100644 --- a/code/numpy/vector.c +++ b/code/numpy/vector.c @@ -609,9 +609,6 @@ static mp_obj_t vector_exp(mp_obj_t o_in) { mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); mp_obj_t o_in = args[0].u_obj; mp_obj_t out = args[1].u_obj; - if(out != mp_const_none) { - mp_raise_ValueError(MP_ERROR_TEXT("out keyword is not supported for complex dtype")); - } #endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ if(mp_obj_is_type(o_in, &mp_type_complex)) { @@ -621,6 +618,13 @@ static mp_obj_t vector_exp(mp_obj_t o_in) { return mp_obj_new_complex(exp_real * MICROPY_FLOAT_C_FUN(cos)(imag), exp_real * MICROPY_FLOAT_C_FUN(sin)(imag)); } else if(mp_obj_is_type(o_in, &ulab_ndarray_type)) { ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in); + + #if ULAB_MATH_FUNCTIONS_OUT_KEYWORD + if((out != mp_const_none) && (source->dtype == NDARRAY_COMPLEX)){ + mp_raise_ValueError(MP_ERROR_TEXT("out keyword is not supported for complex dtype")); + } + #endif /* ULAB_MATH_FUNCTIONS_OUT_KEYWORD */ + if(source->dtype == NDARRAY_COMPLEX) { uint8_t *sarray = (uint8_t *)source->array; ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_COMPLEX); diff --git a/docs/manual/source/conf.py b/docs/manual/source/conf.py index 3f4d5eb4..4a2cea6e 100644 --- a/docs/manual/source/conf.py +++ b/docs/manual/source/conf.py @@ -23,11 +23,11 @@ # -- Project information ----------------------------------------------------- project = 'The ulab book' -copyright = '2019-2022, Zoltán Vörös and contributors' +copyright = '2019-2024, Zoltán Vörös and contributors' author = 'Zoltán Vörös' # The full version, including alpha/beta/rc tags -release = '5.1.0' +release = '6.5.0' # -- General configuration --------------------------------------------------- diff --git a/docs/manual/source/index.rst b/docs/manual/source/index.rst index 1bae7a3d..40fbc00d 100644 --- a/docs/manual/source/index.rst +++ b/docs/manual/source/index.rst @@ -22,6 +22,7 @@ Welcome to the ulab book! numpy-universal numpy-fft numpy-linalg + numpy-random scipy-linalg scipy-optimize scipy-signal diff --git a/docs/manual/source/numpy-functions.rst b/docs/manual/source/numpy-functions.rst index aac19bde..a2a3e410 100644 --- a/docs/manual/source/numpy-functions.rst +++ b/docs/manual/source/numpy-functions.rst @@ -12,43 +12,48 @@ the firmware was compiled with complex support. 4. `numpy.argmin <#argmin>`__ 5. `numpy.argsort <#argsort>`__ 6. `numpy.asarray\* <#asarray>`__ -7. `numpy.clip <#clip>`__ -8. `numpy.compress\* <#compress>`__ -9. `numpy.conjugate\* <#conjugate>`__ -10. `numpy.convolve\* <#convolve>`__ -11. `numpy.delete <#delete>`__ -12. `numpy.diff <#diff>`__ -13. `numpy.dot <#dot>`__ -14. `numpy.equal <#equal>`__ -15. `numpy.flip\* <#flip>`__ -16. `numpy.imag\* <#imag>`__ -17. `numpy.interp <#interp>`__ -18. `numpy.isfinite <#isfinite>`__ -19. `numpy.isinf <#isinf>`__ -20. `numpy.load <#load>`__ -21. `numpy.loadtxt <#loadtxt>`__ -22. `numpy.max <#max>`__ -23. `numpy.maximum <#maximum>`__ -24. `numpy.mean <#mean>`__ -25. `numpy.median <#median>`__ -26. `numpy.min <#min>`__ -27. `numpy.minimum <#minimum>`__ -28. `numpy.nozero <#nonzero>`__ -29. `numpy.not_equal <#equal>`__ -30. `numpy.polyfit <#polyfit>`__ -31. `numpy.polyval <#polyval>`__ -32. `numpy.real\* <#real>`__ -33. `numpy.roll <#roll>`__ -34. `numpy.save <#save>`__ -35. `numpy.savetxt <#savetxt>`__ -36. `numpy.size <#size>`__ -37. `numpy.sort <#sort>`__ -38. `numpy.sort_complex\* <#sort_complex>`__ -39. `numpy.std <#std>`__ -40. `numpy.sum <#sum>`__ -41. `numpy.trace <#trace>`__ -42. `numpy.trapz <#trapz>`__ -43. `numpy.where <#where>`__ +7. `numpy.bitwise_and <#bitwise_and>`__ +8. `numpy.bitwise_or <#bitwise_and>`__ +9. `numpy.bitwise_xor <#bitwise_and>`__ +10. `numpy.clip <#clip>`__ +11. `numpy.compress\* <#compress>`__ +12. `numpy.conjugate\* <#conjugate>`__ +13. `numpy.convolve\* <#convolve>`__ +14. `numpy.delete <#delete>`__ +15. `numpy.diff <#diff>`__ +16. `numpy.dot <#dot>`__ +17. `numpy.equal <#equal>`__ +18. `numpy.flip\* <#flip>`__ +19. `numpy.imag\* <#imag>`__ +20. `numpy.interp <#interp>`__ +21. `numpy.isfinite <#isfinite>`__ +22. `numpy.isinf <#isinf>`__ +23. `numpy.left_shift <#left_shift>`__ +24. `numpy.load <#load>`__ +25. `numpy.loadtxt <#loadtxt>`__ +26. `numpy.max <#max>`__ +27. `numpy.maximum <#maximum>`__ +28. `numpy.mean <#mean>`__ +29. `numpy.median <#median>`__ +30. `numpy.min <#min>`__ +31. `numpy.minimum <#minimum>`__ +32. `numpy.nozero <#nonzero>`__ +33. `numpy.not_equal <#equal>`__ +34. `numpy.polyfit <#polyfit>`__ +35. `numpy.polyval <#polyval>`__ +36. `numpy.real\* <#real>`__ +37. `numpy.right_shift <#right_shift>`__ +38. `numpy.roll <#roll>`__ +39. `numpy.save <#save>`__ +40. `numpy.savetxt <#savetxt>`__ +41. `numpy.size <#size>`__ +42. `numpy.sort <#sort>`__ +43. `numpy.sort_complex\* <#sort_complex>`__ +44. `numpy.std <#std>`__ +45. `numpy.sum <#sum>`__ +46. `numpy.trace <#trace>`__ +47. `numpy.trapz <#trapz>`__ +48. `numpy.where <#where>`__ all --- @@ -323,6 +328,58 @@ an alias for ``array``. +bitwise_and +----------- + +``numpy``: https://numpy.org/doc/stable/reference/routines.bitwise.html + +``numpy``: +https://numpy.org/doc/stable/reference/generated/numpy.bitwise_and.html + +``numpy``: +https://numpy.org/doc/stable/reference/generated/numpy.bitwise_or.html + +``numpy``: +https://numpy.org/doc/stable/reference/generated/numpy.bitwise_xor.html + +Each of ``bitwise_and``, ``bitwise_or``, and ``bitwise_xor`` takes two +integer-type ``ndarray``\ s as arguments, and returns the element-wise +results of the ``AND``, ``OR``, and ``XOR`` operators. Broadcasting is +supported. If the ``dtype`` of the input arrays is not an integer, and +exception will be raised. + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + a = np.array(range(8), dtype=np.uint8) + b = a + 1 + + print(a) + print(b) + print('\nbitwise_and:\n', np.bitwise_and(a, b)) + print('\nbitwise_or:\n', np.bitwise_or(a, b)) + print('\nbitwise_xor:\n', np.bitwise_xor(a, b)) + +.. parsed-literal:: + + array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint8) + array([1, 2, 3, 4, 5, 6, 7, 8], dtype=uint8) + + bitwise_and: + array([0, 0, 2, 0, 4, 4, 6, 0], dtype=uint8) + + bitwise_or: + array([1, 3, 3, 7, 5, 7, 7, 15], dtype=uint8) + + bitwise_xor: + array([1, 3, 1, 7, 1, 3, 1, 15], dtype=uint8) + + + + clip ---- @@ -987,6 +1044,52 @@ positions, where the input is infinite. Integer types return the +left_shift +---------- + +``numpy``: +https://numpy.org/doc/stable/reference/generated/numpy.left_shift.html + +``numpy``: +https://numpy.org/doc/stable/reference/generated/numpy.right_shift.html + +``left_shift``, and ``right_shift`` both take two integer-type +``ndarray``\ s, and bit-wise shift the elements of the first array by an +amount given by the second array to the left, and right, respectively. +Broadcasting is supported. If the ``dtype`` of the input arrays is not +an integer, and exception will be raised. + +.. code:: + + # code to be run in micropython + + from ulab import numpy as np + + a = np.ones(7, dtype=np.uint8) + b = np.zeros(7, dtype=np.uint8) + 255 + c = np.array(range(7), dtype=np.uint8) + 1 + + print('a: ', a) + print('b: ', b) + print('c: ', c) + print('\na left shifted by c:\n', np.left_shift(a, c)) + print('\nb right shifted by c:\n', np.right_shift(b, c)) + +.. parsed-literal:: + + a: array([1, 1, 1, 1, 1, 1, 1], dtype=uint8) + b: array([255, 255, 255, 255, 255, 255, 255], dtype=uint8) + c: array([1, 2, 3, 4, 5, 6, 7], dtype=uint8) + + a left shifted by c: + array([2, 4, 8, 16, 32, 64, 128], dtype=uint8) + + b right shifted by c: + array([127, 63, 31, 15, 7, 3, 1], dtype=uint8) + + + + load ---- diff --git a/docs/manual/source/numpy-universal.rst b/docs/manual/source/numpy-universal.rst index b9b7f9f1..f39ee173 100644 --- a/docs/manual/source/numpy-universal.rst +++ b/docs/manual/source/numpy-universal.rst @@ -20,12 +20,18 @@ operate on, or can return complex arrays): ``acos``, ``acosh``, ``arctan2``, ``around``, ``asin``, ``asinh``, ``atan``, ``arctan2``, ``atanh``, ``ceil``, ``cos``, ``degrees``, ``exp*``, ``expm1``, ``floor``, ``log``, ``log10``, ``log2``, -``radians``, ``sin``, ``sinh``, ``sqrt*``, ``tan``, ``tanh``. +``radians``, ``sin``, ``sinc``, ``sinh``, ``sqrt*``, ``tan``, ``tanh``. These functions are applied element-wise to the arguments, thus, e.g., the exponential of a matrix cannot be calculated in this way, only the exponential of the matrix entries. +In order to avoid repeated memory allocations, functions can take the +``out=None`` optional argument, which must be a floating point +``ndarray`` of the same size as the input ``array``. If these conditions +are not fulfilled, and exception will be raised. If ``out=None``, a new +array will be created upon each invocation of the function. + .. code:: # code to be run in micropython @@ -47,6 +53,13 @@ exponential of the matrix entries. c = np.array(range(9)).reshape((3, 3)) print('\n=============\nc:\n', c) print('exp(c):\n', np.exp(c)) + + # using the `out` argument + d = np.array(range(9)).reshape((3, 3)) + + print('\nd before invoking the function:\n', d) + np.exp(c, out=d) + print('\nd afteri nvoking the function:\n', d) .. parsed-literal:: @@ -69,6 +82,16 @@ exponential of the matrix entries. [20.08553692318767, 54.59815003314424, 148.4131591025766], [403.4287934927351, 1096.633158428459, 2980.957987041728]], dtype=float64) + d before invoking the function: + array([[0.0, 1.0, 2.0], + [3.0, 4.0, 5.0], + [6.0, 7.0, 8.0]], dtype=float64) + + d afteri nvoking the function: + array([[1.0, 2.718281828459045, 7.38905609893065], + [20.08553692318767, 54.59815003314424, 148.4131591025766], + [403.4287934927351, 1096.633158428459, 2980.957987041728]], dtype=float64) + diff --git a/docs/manual/source/ulab-ndarray.rst b/docs/manual/source/ulab-ndarray.rst index ff8e6eb7..ef54a242 100644 --- a/docs/manual/source/ulab-ndarray.rst +++ b/docs/manual/source/ulab-ndarray.rst @@ -1816,12 +1816,17 @@ Binary operators ``ulab`` implements the ``+``, ``-``, ``*``, ``/``, ``**``, ``<``, ``>``, ``<=``, ``>=``, ``==``, ``!=``, ``+=``, ``-=``, ``*=``, ``/=``, -``**=`` binary operators that work element-wise. Broadcasting is -available, meaning that the two operands do not even have to have the -same shape. If the lengths along the respective axes are equal, or one -of them is 1, or the axis is missing, the element-wise operation can -still be carried out. A thorough explanation of broadcasting can be -found under https://numpy.org/doc/stable/user/basics.broadcasting.html. +``**=`` binary operators, as well as the ``AND``, ``OR``, ``XOR`` +bit-wise operators that work element-wise. Note that the bit-wise +operators will raise an exception, if either of the operands is of +``float`` or ``complex`` type. + +Broadcasting is available, meaning that the two operands do not even +have to have the same shape. If the lengths along the respective axes +are equal, or one of them is 1, or the axis is missing, the element-wise +operation can still be carried out. A thorough explanation of +broadcasting can be found under +https://numpy.org/doc/stable/user/basics.broadcasting.html. **WARNING**: note that relational operators (``<``, ``>``, ``<=``, ``>=``, ``==``, ``!=``) should have the ``ndarray`` on their left hand diff --git a/docs/numpy-functions.ipynb b/docs/numpy-functions.ipynb index c57225f9..2ef884a2 100644 --- a/docs/numpy-functions.ipynb +++ b/docs/numpy-functions.ipynb @@ -77,7 +77,7 @@ " if args.unix: # tests the code on the unix port. Note that this works on unix only\n", " with open('/dev/shm/micropython.py', 'w') as fout:\n", " fout.write(cell)\n", - " proc = subprocess.Popen([\"../micropython/ports/unix/micropython-2\", \"/dev/shm/micropython.py\"], \n", + " proc = subprocess.Popen([\"../micropython/ports/unix/build-2/micropython-2\", \"/dev/shm/micropython.py\"], \n", " stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n", " print(proc.stdout.read().decode(\"utf-8\"))\n", " print(proc.stderr.read().decode(\"utf-8\"))\n", @@ -238,6 +238,9 @@ "1. [numpy.argmin](#argmin)\n", "1. [numpy.argsort](#argsort)\n", "1. [numpy.asarray*](#asarray)\n", + "1. [numpy.bitwise_and](#bitwise_and)\n", + "1. [numpy.bitwise_or](#bitwise_and)\n", + "1. [numpy.bitwise_xor](#bitwise_and)\n", "1. [numpy.clip](#clip)\n", "1. [numpy.compress*](#compress)\n", "1. [numpy.conjugate*](#conjugate)\n", @@ -251,6 +254,7 @@ "1. [numpy.interp](#interp)\n", "1. [numpy.isfinite](#isfinite)\n", "1. [numpy.isinf](#isinf)\n", + "1. [numpy.left_shift](#left_shift)\n", "1. [numpy.load](#load)\n", "1. [numpy.loadtxt](#loadtxt)\n", "1. [numpy.max](#max)\n", @@ -264,6 +268,7 @@ "1. [numpy.polyfit](#polyfit)\n", "1. [numpy.polyval](#polyval)\n", "1. [numpy.real*](#real)\n", + "1. [numpy.right_shift](#right_shift)\n", "1. [numpy.roll](#roll)\n", "1. [numpy.save](#save)\n", "1. [numpy.savetxt](#savetxt)\n", @@ -606,6 +611,63 @@ "print('a == c: {}'.format(a is c))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## bitwise_and\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/routines.bitwise.html\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.bitwise_and.html\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.bitwise_or.html\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.bitwise_xor.html\n", + "\n", + "Each of `bitwise_and`, `bitwise_or`, and `bitwise_xor` takes two integer-type `ndarray`s as arguments, and returns the element-wise results of the `AND`, `OR`, and `XOR` operators. Broadcasting is supported. If the `dtype` of the input arrays is not an integer, and exception will be raised." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint8)\n", + "array([1, 2, 3, 4, 5, 6, 7, 8], dtype=uint8)\n", + "\n", + "bitwise_and:\n", + " array([0, 0, 2, 0, 4, 4, 6, 0], dtype=uint8)\n", + "\n", + "bitwise_or:\n", + " array([1, 3, 3, 7, 5, 7, 7, 15], dtype=uint8)\n", + "\n", + "bitwise_xor:\n", + " array([1, 3, 1, 7, 1, 3, 1, 15], dtype=uint8)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "a = np.array(range(8), dtype=np.uint8)\n", + "b = a + 1\n", + "\n", + "print(a)\n", + "print(b)\n", + "print('\\nbitwise_and:\\n', np.bitwise_and(a, b))\n", + "print('\\nbitwise_or:\\n', np.bitwise_or(a, b))\n", + "print('\\nbitwise_xor:\\n', np.bitwise_xor(a, b))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1423,6 +1485,58 @@ "print('\\nisinf(c):\\n', np.isinf(c))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## left_shift\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.left_shift.html\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.right_shift.html\n", + "\n", + "`left_shift`, and `right_shift` both take two integer-type `ndarray`s, and bit-wise shift the elements of the first array by an amount given by the second array to the left, and right, respectively. Broadcasting is supported. If the `dtype` of the input arrays is not an integer, and exception will be raised." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a: array([1, 1, 1, 1, 1, 1, 1], dtype=uint8)\n", + "b: array([255, 255, 255, 255, 255, 255, 255], dtype=uint8)\n", + "c: array([1, 2, 3, 4, 5, 6, 7], dtype=uint8)\n", + "\n", + "a left shifted by c:\n", + " array([2, 4, 8, 16, 32, 64, 128], dtype=uint8)\n", + "\n", + "b right shifted by c:\n", + " array([127, 63, 31, 15, 7, 3, 1], dtype=uint8)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "a = np.ones(7, dtype=np.uint8)\n", + "b = np.zeros(7, dtype=np.uint8) + 255\n", + "c = np.array(range(7), dtype=np.uint8) + 1\n", + "\n", + "print('a: ', a)\n", + "print('b: ', b)\n", + "print('c: ', c)\n", + "print('\\na left shifted by c:\\n', np.left_shift(a, c))\n", + "print('\\nb right shifted by c:\\n', np.right_shift(b, c))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -2786,7 +2900,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.9.13" }, "toc": { "base_numbering": 1, diff --git a/docs/numpy-random.ipynb b/docs/numpy-random.ipynb new file mode 100644 index 00000000..4c9aa2a4 --- /dev/null +++ b/docs/numpy-random.ipynb @@ -0,0 +1,492 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2020-05-01T09:27:13.438054Z", + "start_time": "2020-05-01T09:27:13.191491Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Populating the interactive namespace from numpy and matplotlib\n" + ] + } + ], + "source": [ + "%pylab inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Notebook magic" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2022-01-07T18:24:48.499467Z", + "start_time": "2022-01-07T18:24:48.488004Z" + } + }, + "outputs": [], + "source": [ + "from IPython.core.magic import Magics, magics_class, line_cell_magic\n", + "from IPython.core.magic import cell_magic, register_cell_magic, register_line_magic\n", + "from IPython.core.magic_arguments import argument, magic_arguments, parse_argstring\n", + "import subprocess\n", + "import os" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2020-07-23T20:31:25.296014Z", + "start_time": "2020-07-23T20:31:25.265937Z" + } + }, + "outputs": [], + "source": [ + "@magics_class\n", + "class PyboardMagic(Magics):\n", + " @cell_magic\n", + " @magic_arguments()\n", + " @argument('-skip')\n", + " @argument('-unix')\n", + " @argument('-pyboard')\n", + " @argument('-file')\n", + " @argument('-data')\n", + " @argument('-time')\n", + " @argument('-memory')\n", + " def micropython(self, line='', cell=None):\n", + " args = parse_argstring(self.micropython, line)\n", + " if args.skip: # doesn't care about the cell's content\n", + " print('skipped execution')\n", + " return None # do not parse the rest\n", + " if args.unix: # tests the code on the unix port. Note that this works on unix only\n", + " with open('/dev/shm/micropython.py', 'w') as fout:\n", + " fout.write(cell)\n", + " proc = subprocess.Popen([\"../micropython/ports/unix/build-2/micropython-2\", \"/dev/shm/micropython.py\"], \n", + " stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n", + " print(proc.stdout.read().decode(\"utf-8\"))\n", + " print(proc.stderr.read().decode(\"utf-8\"))\n", + " return None\n", + " if args.file: # can be used to copy the cell content onto the pyboard's flash\n", + " spaces = \" \"\n", + " try:\n", + " with open(args.file, 'w') as fout:\n", + " fout.write(cell.replace('\\t', spaces))\n", + " printf('written cell to {}'.format(args.file))\n", + " except:\n", + " print('Failed to write to disc!')\n", + " return None # do not parse the rest\n", + " if args.data: # can be used to load data from the pyboard directly into kernel space\n", + " message = pyb.exec(cell)\n", + " if len(message) == 0:\n", + " print('pyboard >>>')\n", + " else:\n", + " print(message.decode('utf-8'))\n", + " # register new variable in user namespace\n", + " self.shell.user_ns[args.data] = string_to_matrix(message.decode(\"utf-8\"))\n", + " \n", + " if args.time: # measures the time of executions\n", + " pyb.exec('import utime')\n", + " message = pyb.exec('t = utime.ticks_us()\\n' + cell + '\\ndelta = utime.ticks_diff(utime.ticks_us(), t)' + \n", + " \"\\nprint('execution time: {:d} us'.format(delta))\")\n", + " print(message.decode('utf-8'))\n", + " \n", + " if args.memory: # prints out memory information \n", + " message = pyb.exec('from micropython import mem_info\\nprint(mem_info())\\n')\n", + " print(\"memory before execution:\\n========================\\n\", message.decode('utf-8'))\n", + " message = pyb.exec(cell)\n", + " print(\">>> \", message.decode('utf-8'))\n", + " message = pyb.exec('print(mem_info())')\n", + " print(\"memory after execution:\\n========================\\n\", message.decode('utf-8'))\n", + "\n", + " if args.pyboard:\n", + " message = pyb.exec(cell)\n", + " print(message.decode('utf-8'))\n", + "\n", + "ip = get_ipython()\n", + "ip.register_magics(PyboardMagic)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## pyboard" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "ExecuteTime": { + "end_time": "2020-05-07T07:35:35.126401Z", + "start_time": "2020-05-07T07:35:35.105824Z" + } + }, + "outputs": [], + "source": [ + "import pyboard\n", + "pyb = pyboard.Pyboard('/dev/ttyACM0')\n", + "pyb.enter_raw_repl()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2020-05-19T19:11:18.145548Z", + "start_time": "2020-05-19T19:11:18.137468Z" + } + }, + "outputs": [], + "source": [ + "pyb.exit_raw_repl()\n", + "pyb.close()" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "ExecuteTime": { + "end_time": "2020-05-07T07:35:38.725924Z", + "start_time": "2020-05-07T07:35:38.645488Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "%%micropython -pyboard 1\n", + "\n", + "import utime\n", + "import ulab as np\n", + "\n", + "def timeit(n=1000):\n", + " def wrapper(f, *args, **kwargs):\n", + " func_name = str(f).split(' ')[1]\n", + " def new_func(*args, **kwargs):\n", + " run_times = np.zeros(n, dtype=np.uint16)\n", + " for i in range(n):\n", + " t = utime.ticks_us()\n", + " result = f(*args, **kwargs)\n", + " run_times[i] = utime.ticks_diff(utime.ticks_us(), t)\n", + " print('{}() execution times based on {} cycles'.format(func_name, n, (delta2-delta1)/n))\n", + " print('\\tbest: %d us'%np.min(run_times))\n", + " print('\\tworst: %d us'%np.max(run_times))\n", + " print('\\taverage: %d us'%np.mean(run_times))\n", + " print('\\tdeviation: +/-%.3f us'%np.std(run_times)) \n", + " return result\n", + " return new_func\n", + " return wrapper\n", + "\n", + "def timeit(f, *args, **kwargs):\n", + " func_name = str(f).split(' ')[1]\n", + " def new_func(*args, **kwargs):\n", + " t = utime.ticks_us()\n", + " result = f(*args, **kwargs)\n", + " print('execution time: ', utime.ticks_diff(utime.ticks_us(), t), ' us')\n", + " return result\n", + " return new_func" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__END_OF_DEFS__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# numpy.random\n", + "\n", + "Random numbers drawn specific distributions can be generated by instantiating a `Generator` object, and calling its methods. The module defines the following three functions:\n", + "\n", + "1. [numpy.random.Generator.normal](#normal)\n", + "1. [numpy.random.Generator.random](#random)\n", + "1. [numpy.random.Generator.uniform](#uniform)\n", + "\n", + "\n", + "The `Generator` object, when instantiated, takes a single integer as its argument. This integer is the seed, which will be fed to the 32-bit or 64-bit routine. More details can be found under https://www.pcg-random.org/index.html. The generator is a standard `python` object that keeps track of its state.\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/random/index.html" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## normal\n", + "\n", + "A random set of number from the `normal` distribution can be generated by calling the generator's `normal` method. The method takes three optional arguments, `loc=0.0`, the centre of the distribution, `scale=1.0`, the width of the distribution, and `size=None`, a tuple containing the shape of the returned array. In case `size` is `None`, a single floating point number is returned.\n", + "\n", + "The `normal` method of the `Generator` object is based on the [Box-Muller transform](https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform).\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.normal.html" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2019-10-19T13:08:17.647416Z", + "start_time": "2019-10-19T13:08:17.597456Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Gnerator() at 0x7fa9dae05340\n", + "-6.285246229407202\n", + "array([[24.95816273705659, 15.2670302229426, 14.81001577336041],\n", + " [20.17589833056986, 23.14539083787544, 26.37772041367461],\n", + " [41.94894234387275, 37.11027030608206, 25.65889562100477]], dtype=float64)\n", + "array([[21.52562779033434, 12.74685887865834, 24.08404670765186],\n", + " [4.728112596365396, 7.667757906857082, 21.61576094228444],\n", + " [2.432338873595267, 27.75945683572574, 5.730827584659245]], dtype=float64)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "rng = np.random.Generator(123456)\n", + "print(rng)\n", + "\n", + "# return single number from a distribution of scale 1, and location 0\n", + "print(rng.normal())\n", + "\n", + "print(rng.normal(loc=20.0, scale=10.0, size=(3,3)))\n", + "# same as above, with positional arguments\n", + "print(rng.normal(20.0, 10.0, (3,3)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## random\n", + "\n", + "A random set of number from the uniform distribution in the interval [0, 1] can be generated by calling the generator's `random` method. The method takes two optional arguments, `size=None`, a tuple containing the shape of the returned array, and `out`. In case `size` is `None`, a single floating point number is returned. \n", + "\n", + "`out` can be used, if a floating point array is available. An exception will be raised, if the array is not of `float` `dtype`, or if both `size` and `out` are supplied, and there is a conflict in their shapes.\n", + "\n", + "If `size` is `None`, a single floating point number will be returned.\n", + "\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.random.html" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Gnerator() at 0x7f299de05340\n", + "6.384615058863119e-11\n", + "\n", + " array([[0.4348157846574171, 0.7906325931024071, 0.878697619856133],\n", + " [0.8738606263361598, 0.4946080034142021, 0.7765890156101152],\n", + " [0.1770783715717074, 0.02080447648492112, 0.1053837559005948]], dtype=float64)\n", + "\n", + "buffer array before:\n", + " array([[0.0, 1.0, 2.0],\n", + " [3.0, 4.0, 5.0],\n", + " [6.0, 7.0, 8.0]], dtype=float64)\n", + "\n", + "buffer array after:\n", + " array([[0.8508024287393201, 0.9848489829156055, 0.7598167589604003],\n", + " [0.782995698302952, 0.2866337782847831, 0.7915884498022229],\n", + " [0.4614071706315902, 0.4792657443088592, 0.1581582066230718]], dtype=float64)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "rng = np.random.Generator(123456)\n", + "print(rng)\n", + "\n", + "# returning new objects\n", + "print(rng.random())\n", + "print('\\n', rng.random(size=(3,3)))\n", + "\n", + "# supplying a buffer\n", + "a = np.array(range(9), dtype=np.float).reshape((3,3))\n", + "print('\\nbuffer array before:\\n', a)\n", + "rng.random(out=a)\n", + "print('\\nbuffer array after:\\n', a)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## uniform\n", + "\n", + "`uniform` is similar to `random`, except that the interval over which the numbers are distributed can be specified, while the `out` argument cannot. In addition to `size` specifying the shape of the output, `low=0.0`, and `high=1.0` are accepted arguments. With the indicated defaults, `uniform` is identical to `random`, which can be seen from the fact that the first 3-by-3 tensor below is the same as the one produced by `rng.random(size=(3,3))` above.\n", + "\n", + "\n", + "If `size` is `None`, a single floating point number will be returned.\n", + "\n", + "\n", + "`numpy`: https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.uniform.html" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Gnerator() at 0x7f1891205340\n", + "6.384615058863119e-11\n", + "\n", + " array([[0.4348157846574171, 0.7906325931024071, 0.878697619856133],\n", + " [0.8738606263361598, 0.4946080034142021, 0.7765890156101152],\n", + " [0.1770783715717074, 0.02080447648492112, 0.1053837559005948]], dtype=float64)\n", + "\n", + " array([[18.5080242873932, 19.84848982915605, 17.598167589604],\n", + " [17.82995698302952, 12.86633778284783, 17.91588449802223],\n", + " [14.6140717063159, 14.79265744308859, 11.58158206623072]], dtype=float64)\n", + "\n", + " array([[14.3380400319162, 12.72487657409978, 15.77119643621117],\n", + " [13.61835831436355, 18.96062889255558, 15.78847796795966],\n", + " [12.59435855187034, 17.68262037443622, 14.77943040598734]], dtype=float64)\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%micropython -unix 1\n", + "\n", + "from ulab import numpy as np\n", + "\n", + "rng = np.random.Generator(123456)\n", + "print(rng)\n", + "\n", + "print(rng.uniform())\n", + "# returning numbers between 0, and 1\n", + "print('\\n', rng.uniform(size=(3,3)))\n", + "\n", + "# returning numbers between 10, and 20\n", + "print('\\n', rng.uniform(low=10, high=20, size=(3,3)))\n", + "\n", + "# same as above, without the keywords\n", + "print('\\n', rng.uniform(10, 20, (3,3)))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "382.797px" + }, + "toc_section_display": true, + "toc_window_display": true + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/numpy-universal.ipynb b/docs/numpy-universal.ipynb index 8934fa6e..1d5764b8 100644 --- a/docs/numpy-universal.ipynb +++ b/docs/numpy-universal.ipynb @@ -31,7 +31,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2022-01-07T19:10:30.696795Z", @@ -49,7 +49,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2022-01-07T19:10:30.785887Z", @@ -77,7 +77,7 @@ " if args.unix: # tests the code on the unix port. Note that this works on unix only\n", " with open('/dev/shm/micropython.py', 'w') as fout:\n", " fout.write(cell)\n", - " proc = subprocess.Popen([\"../micropython/ports/unix/micropython-2\", \"/dev/shm/micropython.py\"], \n", + " proc = subprocess.Popen([\"../micropython/ports/unix/build-2/micropython-2\", \"/dev/shm/micropython.py\"], \n", " stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n", " print(proc.stdout.read().decode(\"utf-8\"))\n", " print(proc.stderr.read().decode(\"utf-8\"))\n", @@ -229,14 +229,16 @@ "\n", "At present, the following functions are supported (starred functions can operate on, or can return complex arrays):\n", "\n", - "`acos`, `acosh`, `arctan2`, `around`, `asin`, `asinh`, `atan`, `arctan2`, `atanh`, `ceil`, `cos`, `degrees`, `exp*`, `expm1`, `floor`, `log`, `log10`, `log2`, `radians`, `sin`, `sinh`, `sqrt*`, `tan`, `tanh`.\n", + "`acos`, `acosh`, `arctan2`, `around`, `asin`, `asinh`, `atan`, `arctan2`, `atanh`, `ceil`, `cos`, `degrees`, `exp*`, `expm1`, `floor`, `log`, `log10`, `log2`, `radians`, `sin`, `sinc`, `sinh`, `sqrt*`, `tan`, `tanh`.\n", + "\n", + "These functions are applied element-wise to the arguments, thus, e.g., the exponential of a matrix cannot be calculated in this way, only the exponential of the matrix entries.\n", "\n", - "These functions are applied element-wise to the arguments, thus, e.g., the exponential of a matrix cannot be calculated in this way, only the exponential of the matrix entries." + "In order to avoid repeated memory allocations, functions can take the `out=None` optional argument, which must be a floating point `ndarray` of the same size as the input `array`. If these conditions are not fulfilled, and exception will be raised. If `out=None`, a new array will be created upon each invocation of the function." ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 9, "metadata": { "ExecuteTime": { "end_time": "2021-01-13T19:11:07.579601Z", @@ -267,6 +269,16 @@ " [20.08553692318767, 54.59815003314424, 148.4131591025766],\n", " [403.4287934927351, 1096.633158428459, 2980.957987041728]], dtype=float64)\n", "\n", + "d before invoking the function:\n", + " array([[0.0, 1.0, 2.0],\n", + " [3.0, 4.0, 5.0],\n", + " [6.0, 7.0, 8.0]], dtype=float64)\n", + "\n", + "d afteri nvoking the function:\n", + " array([[1.0, 2.718281828459045, 7.38905609893065],\n", + " [20.08553692318767, 54.59815003314424, 148.4131591025766],\n", + " [403.4287934927351, 1096.633158428459, 2980.957987041728]], dtype=float64)\n", + "\n", "\n" ] } @@ -290,7 +302,14 @@ "# as well as with matrices\n", "c = np.array(range(9)).reshape((3, 3))\n", "print('\\n=============\\nc:\\n', c)\n", - "print('exp(c):\\n', np.exp(c))" + "print('exp(c):\\n', np.exp(c))\n", + "\n", + "# using the `out` argument\n", + "d = np.array(range(9)).reshape((3, 3))\n", + "\n", + "print('\\nd before invoking the function:\\n', d)\n", + "np.exp(c, out=d)\n", + "print('\\nd afteri nvoking the function:\\n', d)" ] }, { @@ -814,7 +833,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.9.13" }, "toc": { "base_numbering": 1, diff --git a/docs/ulab-ndarray.ipynb b/docs/ulab-ndarray.ipynb index bf016b1a..0e2bb1b0 100644 --- a/docs/ulab-ndarray.ipynb +++ b/docs/ulab-ndarray.ipynb @@ -2599,7 +2599,9 @@ "source": [ "# Binary operators\n", "\n", - "`ulab` implements the `+`, `-`, `*`, `/`, `**`, `<`, `>`, `<=`, `>=`, `==`, `!=`, `+=`, `-=`, `*=`, `/=`, `**=` binary operators that work element-wise. Broadcasting is available, meaning that the two operands do not even have to have the same shape. If the lengths along the respective axes are equal, or one of them is 1, or the axis is missing, the element-wise operation can still be carried out. \n", + "`ulab` implements the `+`, `-`, `*`, `/`, `**`, `<`, `>`, `<=`, `>=`, `==`, `!=`, `+=`, `-=`, `*=`, `/=`, `**=` binary operators, as well as the `AND`, `OR`, `XOR` bit-wise operators that work element-wise. Note that the bit-wise operators will raise an exception, if either of the operands is of `float` or `complex` type.\n", + "\n", + "Broadcasting is available, meaning that the two operands do not even have to have the same shape. If the lengths along the respective axes are equal, or one of them is 1, or the axis is missing, the element-wise operation can still be carried out. \n", "A thorough explanation of broadcasting can be found under https://numpy.org/doc/stable/user/basics.broadcasting.html. \n", "\n", "**WARNING**: note that relational operators (`<`, `>`, `<=`, `>=`, `==`, `!=`) should have the `ndarray` on their left hand side, when compared to scalars. This means that the following works"