add random module (#654)

* add random module skeleton

* add Generator object

* add placeholder for random.random method

* add rudimentary random.random implementation

* generator object accept seed(s) argument

* add out keyword

* add support for out keyword argument

* update change log

* add links to header files

* fix file link

* fix error messages

* add uniform to random module

* add normal distribution

* fix argument options in normal and uniform

* update documentation
This commit is contained in:
Zoltán Vörös 2024-01-13 18:42:43 +01:00 committed by GitHub
parent 7a9370612f
commit f2fad82a97
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 1277 additions and 68 deletions

View file

@ -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

View file

@ -561,9 +561,9 @@ ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *_shape, uint8_t dt
size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
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);

View file

@ -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'))

View file

@ -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) },

378
code/numpy/random/random.c Normal file
View file

@ -0,0 +1,378 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2024 Zoltán Vörös
*/
#include <math.h>
#include "py/builtin.h"
#include "py/obj.h"
#include "py/runtime.h"
#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_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
#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)
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,
locals_dict, &random_generator_locals_dict
);
#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,
.locals_dict = (mp_obj_dict_t*)&random_generator_locals_dict
};
#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);
if(args[0] == mp_const_none) {
#ifndef MICROPY_PY_RANDOM_SEED_INIT_FUNC
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;
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 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);
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);
} else {
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;
}
// 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_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_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)];
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[] = {
{ 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 } },
};
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_obj_t size = args[1].u_obj;
mp_obj_t out = args[2].u_obj;
ndarray_obj_t *ndarray = NULL;
size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
uint8_t ndim = 1;
if(size != mp_const_none) {
if(mp_obj_is_int(size)) {
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_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++) {
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(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(MP_ERROR_TEXT("out has wrong type"));
}
ndarray = MP_OBJ_TO_PTR(out);
if(ndarray->dtype != NDARRAY_FLOAT) {
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(MP_ERROR_TEXT("size must match out.shape when used together"));
}
}
}
if(!ndarray_is_dense(ndarray)) {
mp_raise_ValueError(MP_ERROR_TEXT("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
// 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
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 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(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_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)];
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_KW(random_uniform_obj, 1, 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) },
{ MP_ROM_QSTR(MP_QSTR_Generator), MP_ROM_PTR(&random_generator_type) },
};
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,
};

View file

@ -0,0 +1,37 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2024 Zoltán Vörös
*/
#include "../../ndarray.h"
#ifndef _NUMPY_RANDOM_
#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;
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 *);
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);
#endif

View file

@ -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);

View file

@ -33,7 +33,7 @@
#include "user/user.h"
#include "utils/utils.h"
#define ULAB_VERSION 6.4.3
#define ULAB_VERSION 6.5.0
#define xstr(s) str(s)
#define str(s) #s

View file

@ -697,6 +697,24 @@
#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_NORMAL
#define ULAB_NUMPY_RANDOM_HAS_NORMAL (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
// scipy modules
#ifndef ULAB_SCIPY_HAS_LINALG_MODULE
#define ULAB_SCIPY_HAS_LINALG_MODULE (1)

View file

@ -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

View file

@ -23,11 +23,11 @@ from sphinx import addnodes
# -- 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 ---------------------------------------------------

View file

@ -22,6 +22,7 @@ Welcome to the ulab book!
numpy-universal
numpy-fft
numpy-linalg
numpy-random
scipy-linalg
scipy-optimize
scipy-signal

View file

@ -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
----

View file

@ -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
@ -48,6 +54,13 @@ exponential of the matrix entries.
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::
a: range(0, 9)
@ -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)

View file

@ -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

View file

@ -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,

492
docs/numpy-random.ipynb Normal file
View file

@ -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
}

View file

@ -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."
"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",
"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,

View file

@ -1,3 +1,9 @@
Tue, 9 Jan 2024
version 6.5.0
add random module
Mon, 25 Dec 2023
version 6.4.3
@ -14,9 +20,7 @@ Thu, 10 Aug 2023
version 6.4.1
```
fix BOOLEAN issue, which would cause numpy.where funciton abnormally on RP2040(#643)
```
Thu, 20 Jul 2023

View file

@ -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"