Skip to content

Initial testing building as C module #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ venv/

.ipynb_checkpoints/
__pycache__/
.mpy_ld_cache/
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
emlearn>=0.21.0
emlearn>=0.21.1
scikit-learn>=1.0.0
ar>=1.0.0
pyelftools>=0.31
53 changes: 46 additions & 7 deletions src/emlearn_iir/iir_filter.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// Include the header file to get access to the MicroPython API
#ifdef MICROPY_ENABLE_DYNRUNTIME
#include "py/dynruntime.h"
#else
#include "py/runtime.h"
#endif

#include <eml_iir.h>

Expand Down Expand Up @@ -29,7 +33,11 @@ typedef struct _mp_obj_iir_filter_t {
EmlIIR filter;
} mp_obj_iir_filter_t;

#if MICROPY_ENABLE_DYNRUNTIME
mp_obj_full_type_t iir_filter_type;
#else
static const mp_obj_type_t iir_filter_type;
#endif

// Create a new instance
static mp_obj_t iir_filter_new(mp_obj_t array_obj) {
Expand All @@ -55,17 +63,17 @@ static mp_obj_t iir_filter_new(mp_obj_t array_obj) {
self->n_stages = n_values / 6;

self->states_length = self->n_stages * 4;
self->states = (float *)m_malloc(sizeof(float)*self->states_length);
self->states = m_new(float, self->states_length);

self->coefficients_length = n_values;
self->coefficients = (float *)m_malloc(sizeof(float)*self->coefficients_length);
self->coefficients = m_new(float, self->coefficients_length);
memcpy((float *)self->coefficients, values, sizeof(float)*self->coefficients_length);


const EmlError err = eml_iir_check(*self);
if (err != EmlOk) {
m_free(self->states);
m_free((float *)self->coefficients);
m_del(float, self->states, self->states_length);
m_del(float, (float *)self->coefficients, self->coefficients_length);
mp_raise_ValueError(MP_ERROR_TEXT("EmlError"));
}

Expand All @@ -80,9 +88,8 @@ static mp_obj_t iir_filter_del(mp_obj_t self_obj) {
EmlIIR *self = &o->filter;

// free allocated data
m_free(self->states);
m_free((float *)self->coefficients);

m_del(float, self->states, self->states_length);
m_del(float, (float *)self->coefficients, self->coefficients_length);

return mp_const_none;
}
Expand Down Expand Up @@ -114,6 +121,7 @@ static mp_obj_t iir_filter_run(mp_obj_t self_obj, mp_obj_t array_obj) {
static MP_DEFINE_CONST_FUN_OBJ_2(iir_filter_run_obj, iir_filter_run);


#ifdef MICROPY_ENABLE_DYNRUNTIME
mp_map_elem_t iir_locals_dict_table[2];
static MP_DEFINE_CONST_DICT(iir_locals_dict, iir_locals_dict_table);

Expand All @@ -136,4 +144,35 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a
// This must be last, it restores the globals dict
MP_DYNRUNTIME_INIT_EXIT
}
#else


// Define a class
static const mp_rom_map_elem_t emlearn_iir_filter_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_run), MP_ROM_PTR(&iir_filter_run_obj) },
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&iir_filter_del_obj) }
};
static MP_DEFINE_CONST_DICT(emlearn_iir_filter_locals_dict, emlearn_iir_filter_locals_dict_table);

static MP_DEFINE_CONST_OBJ_TYPE(
iir_filter_type,
MP_QSTR_emliir,
MP_TYPE_FLAG_NONE,
locals_dict, &emlearn_iir_filter_locals_dict
);

// Define module object.
static const mp_rom_map_elem_t emlearn_iir_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_new), MP_ROM_PTR(&iir_filter_new_obj) },
};
static MP_DEFINE_CONST_DICT(emlearn_iir_globals, emlearn_iir_globals_table);

const mp_obj_module_t emlearn_iir_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&emlearn_iir_globals,
};

MP_REGISTER_MODULE(MP_QSTR_emlearn_iir, emlearn_iir_cmodule);


#endif
9 changes: 9 additions & 0 deletions src/emlearn_iir/micropython.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MOD_DIR := $(USERMOD_DIR)

# Add all C files to SRC_USERMOD.
SRC_USERMOD_C += $(MOD_DIR)/iir_filter.c

EMLEARN_DIR := $(shell python3 -c "import emlearn; print(emlearn.includedir)")

# We can add our module folder to include paths if needed
CFLAGS_USERMOD += -I$(EMLEARN_DIR)
2 changes: 1 addition & 1 deletion src/emlearn_trees/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ $(DIST_FILE): $(MOD).mpy $(DIST_DIR)
# Include to get the rules for compiling and linking the module
include $(MPY_DIR)/py/dynruntime.mk

CFLAGS += -I$(EMLEARN_DIR)
CFLAGS += -I$(EMLEARN_DIR) -DDYNAMIC_RUNTIME=1

dist: $(DIST_FILE)
9 changes: 9 additions & 0 deletions src/emlearn_trees/micropython.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MOD_DIR := $(USERMOD_DIR)

# Add all C files to SRC_USERMOD.
SRC_USERMOD_C += $(MOD_DIR)/trees.c

EMLEARN_DIR := $(shell python3 -c "import emlearn; print(emlearn.includedir)")

# We can add our module folder to include paths if needed
CFLAGS_USERMOD += -I$(EMLEARN_DIR)
59 changes: 53 additions & 6 deletions src/emlearn_trees/trees.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
// Include the header file to get access to the MicroPython API

#ifdef MICROPY_ENABLE_DYNRUNTIME
#include "py/dynruntime.h"
#else
#include "py/runtime.h"
#endif

#define EML_TREES_REGRESSION_ENABLE 0
#include <eml_trees.h>
Expand Down Expand Up @@ -33,7 +38,11 @@ typedef struct _mp_obj_trees_builder_t {
EmlTreesBuilder builder;
} mp_obj_trees_builder_t;

#if MICROPY_ENABLE_DYNRUNTIME
mp_obj_full_type_t trees_builder_type;
#else
static const mp_obj_type_t trees_builder_type;
#endif

// Create a new tree builder
static mp_obj_t builder_new(mp_obj_t trees_obj, mp_obj_t nodes_obj, mp_obj_t leaves_obj) {
Expand All @@ -58,9 +67,9 @@ static mp_obj_t builder_new(mp_obj_t trees_obj, mp_obj_t nodes_obj, mp_obj_t lea
self->max_leaves = max_leaves;

// create storage for trees
EmlTreesNode *nodes = (EmlTreesNode *)m_malloc(sizeof(EmlTreesNode)*max_nodes);
int32_t *roots = (int32_t *)m_malloc(sizeof(int32_t)*max_trees);
uint8_t *leaves = (uint8_t *)m_malloc(sizeof(uint8_t)*max_leaves);
EmlTreesNode *nodes = m_new(EmlTreesNode, self->max_nodes);
int32_t *roots = m_new(int32_t, self->max_trees);
uint8_t *leaves = m_new(uint8_t, self->max_leaves);

#if EMLEARN_MICROPYTHON_DEBUG
mp_printf(&mp_plat_print, "emltrees nodes=%p roots=%p builder=%p\n", nodes, roots, self);
Expand Down Expand Up @@ -91,9 +100,9 @@ static mp_obj_t builder_del(mp_obj_t trees_obj) {
EmlTreesBuilder *self = &o->builder;

// free allocated data
m_free(self->trees.nodes);
m_free(self->trees.tree_roots);
m_free(self->trees.leaves);
m_del(EmlTreesNode, self->trees.nodes, self->max_nodes);
m_del(int32_t, self->trees.tree_roots, self->max_nodes);
m_del(uint8_t, self->trees.leaves, self->max_leaves);

#if EMLEARN_MICROPYTHON_DEBUG
mp_printf(&mp_plat_print, "emltrees del \n");
Expand Down Expand Up @@ -261,6 +270,7 @@ static mp_obj_t builder_predict(mp_obj_t self_obj, mp_obj_t features_obj, mp_obj
static MP_DEFINE_CONST_FUN_OBJ_3(builder_predict_obj, builder_predict);


#ifdef MICROPY_ENABLE_DYNRUNTIME
mp_map_elem_t trees_locals_dict_table[7];
static MP_DEFINE_CONST_DICT(trees_locals_dict, trees_locals_dict_table);

Expand Down Expand Up @@ -289,3 +299,40 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a
MP_DYNRUNTIME_INIT_EXIT
}

#else


// Define the tree builder class
static const mp_rom_map_elem_t emlearn_trees_builder_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_predict), MP_ROM_PTR(&builder_predict_obj) },
{ MP_ROM_QSTR(MP_QSTR_addnode), MP_ROM_PTR(&builder_addnode_obj) },
{ MP_ROM_QSTR(MP_QSTR_addroot), MP_ROM_PTR(&builder_addroot_obj) },
{ MP_ROM_QSTR(MP_QSTR_addleaf), MP_ROM_PTR(&builder_addleaf_obj) },
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&builder_del_obj) },
{ MP_ROM_QSTR(MP_QSTR_setdata), MP_ROM_PTR(&builder_setdata_obj) },
{ MP_ROM_QSTR(MP_QSTR_outputs), MP_ROM_PTR(&builder_get_outputs_obj) },
};
static MP_DEFINE_CONST_DICT(emlearn_trees_builder_locals_dict, emlearn_trees_builder_locals_dict_table);

static MP_DEFINE_CONST_OBJ_TYPE(
trees_builder_type,
MP_QSTR_emltrees,
MP_TYPE_FLAG_NONE,
locals_dict, &emlearn_trees_builder_locals_dict
);

// Define module object.
static const mp_rom_map_elem_t emlearn_trees_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_new), MP_ROM_PTR(&builder_new_obj) },
};
static MP_DEFINE_CONST_DICT(emlearn_trees_globals, emlearn_trees_globals_table);

const mp_obj_module_t emlearn_trees_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&emlearn_trees_globals,
};

MP_REGISTER_MODULE(MP_QSTR_emlearn_trees, emlearn_trees_cmodule);

#endif