Skip to content

Commit

Permalink
merging from testing
Browse files Browse the repository at this point in the history
  • Loading branch information
v923z committed Nov 4, 2019
2 parents 841c468 + 351eafa commit a940870
Show file tree
Hide file tree
Showing 7 changed files with 534 additions and 94 deletions.
57 changes: 57 additions & 0 deletions code/numerical.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <stdlib.h>
#include <string.h>
#include "py/obj.h"
#include "py/objint.h"
#include "py/runtime.h"
#include "py/builtin.h"
#include "py/misc.h"
Expand Down Expand Up @@ -537,3 +538,59 @@ mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
m_del(int8_t, stencil, n);
return MP_OBJ_FROM_PTR(out);
}

mp_obj_t numerical_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {

static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj) } },
{ MP_QSTR_n, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1 } },
{ MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_int = -1 } },
};

mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(1, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);

if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
mp_raise_TypeError("sort argument must be an ndarray");
}

mp_obj_t out = ndarray_copy(args[0].u_obj);
ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(out);
size_t increment, start_inc, end, N;
if(args[2].u_obj == mp_const_none) { // flatten the array
ndarray->m = 1;
ndarray->n = ndarray->array->len;
increment = 1;
start_inc = ndarray->n;
end = ndarray->n;
N = ndarray->n;
} else if((mp_obj_get_int(args[2].u_obj) == -1) ||
(mp_obj_get_int(args[2].u_obj) == 1)) { // sort along the horizontal axis
increment = 1;
start_inc = ndarray->n;
end = ndarray->array->len;
N = ndarray->n;
} else if(mp_obj_get_int(args[2].u_obj) == 0) { // sort along vertical axis
increment = ndarray->n;
start_inc = 1;
end = ndarray->m;
N = ndarray->m;
} else {
mp_raise_ValueError("axis must be -1, 0, None, or 1");
}

size_t q, k, p, c;

for(size_t start=0; start < end; start+=start_inc) {
q = N;
k = (q >> 1);
if((ndarray->array->typecode == NDARRAY_UINT8) || (ndarray->array->typecode == NDARRAY_INT8)) {
HEAPSORT(uint8_t, ndarray);
} else if((ndarray->array->typecode == NDARRAY_INT16) || (ndarray->array->typecode == NDARRAY_INT16)) {
HEAPSORT(uint16_t, ndarray);
} else {
HEAPSORT(mp_float_t, ndarray);
}
}
return out;
}
34 changes: 33 additions & 1 deletion code/numerical.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,37 @@ mp_obj_t numerical_sort(size_t , const mp_obj_t *, mp_map_t *);
}\
}\
} while(0)


#define HEAPSORT(type, ndarray) do {\
uint8_t *array = (uint8_t *)(ndarray)->array->items;\
uint8_t tmp;\
for (; q > 0;) {\
if (k > 0) {\
tmp = array[start+(--k)*increment];\
} else {\
q--;\
if(q == 0) {\
break;\
}\
tmp = array[start+q*increment];\
array[start+q*increment] = array[start];\
}\
p = k;\
c = k + k + 1;\
while (c < q) {\
if((c + 1 < q) && (array[start+(c+1)*increment] > array[start+c*increment])) {\
c++;\
}\
if(array[start+c*increment] > tmp) {\
array[start+p*increment] = array[start+c*increment];\
p = c;\
c = p + p + 1;\
} else {\
break;\
}\
}\
array[start+p*increment] = tmp;\
}\
} while(0)

#endif
10 changes: 6 additions & 4 deletions code/ulab.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#include "py/runtime.h"
#include "py/binary.h"
#include "py/obj.h"
#include "py/objarray.h" // this can in the future be dropped
#include "py/objarray.h"

#include "ndarray.h"
#include "linalg.h"
Expand All @@ -24,7 +24,7 @@
#include "fft.h"
#include "numerical.h"

#define ULAB_VERSION 0.24
#define ULAB_VERSION 0.25

typedef struct _mp_obj_float_t {
mp_obj_base_t base;
Expand Down Expand Up @@ -84,6 +84,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argmax_obj, 1, numerical_argmax);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_roll_obj, 2, numerical_roll);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_flip_obj, 1, numerical_flip);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_diff_obj, 1, numerical_diff);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(numerical_sort_obj, 1, numerical_sort);

STATIC MP_DEFINE_CONST_FUN_OBJ_2(poly_polyval_obj, poly_polyval);
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poly_polyfit_obj, 2, 3, poly_polyfit);
Expand Down Expand Up @@ -160,11 +161,12 @@ STATIC const mp_map_elem_t ulab_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_argmax), (mp_obj_t)&numerical_argmax_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_roll), (mp_obj_t)&numerical_roll_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_flip), (mp_obj_t)&numerical_flip_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_diff), (mp_obj_t)&numerical_diff_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_diff), (mp_obj_t)&numerical_diff_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_sort), (mp_obj_t)&numerical_sort_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_polyval), (mp_obj_t)&poly_polyval_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_polyfit), (mp_obj_t)&poly_polyfit_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_fft), (mp_obj_t)&fft_fft_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_ifft), (mp_obj_t)&fft_ifft_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_ifft), (mp_obj_t)&fft_ifft_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_spectrum), (mp_obj_t)&fft_spectrum_obj },
// class constants
{ MP_ROM_QSTR(MP_QSTR_uint8), MP_ROM_INT(NDARRAY_UINT8) },
Expand Down
129 changes: 111 additions & 18 deletions docs/manual/source/ulab.rst
Original file line number Diff line number Diff line change
Expand Up @@ -357,22 +357,6 @@ themselves, though it should be pointed out that initialising through
arrays should be faster, because simply a new copy is created, without
inspection, iteration etc.

.. code::
# code to be run in micropython
import ulab as np
a = np.array([1, 2, 3, 4, 5, 6, 7, 8])
print(a+5)
.. parsed-literal::
here is an intarray([6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0], dtype=float)
.. code::
# code to be run in micropython
Expand Down Expand Up @@ -557,20 +541,28 @@ directly.
**WARNING:** ``asbytearray`` is a ``ulab``-only method; it has no
equivalent in ``numpy``.

In the example below, note the difference between ``a``, and ``buffer``:
while both are designated as an array, you recognise the micropython
array from the fact that it prints the typecode (``b`` in this
particular case). The ``ndarray``, on the other hand, prints out the
``dtype`` (``int8`` here).

.. code::
# code to be run in micropython
import ulab as np
a = np.array([1, 2, 3, 4], dtype=np.int8)
print('a: ', a)
buffer = a.asbytearray()
print("array content:", buffer)
buffer[1] = 123
print("array content:", buffer)
.. parsed-literal::
a: array([1, 2, 3, 4], dtype=int8)
array content: array('b', [1, 2, 3, 4])
array content: array('b', [1, 123, 3, 4])
Expand Down Expand Up @@ -1467,7 +1459,7 @@ type, and then convert them to floats. All these steps are skipped for
Of course, such a time saving is reasonable only, if the data are
already available as an ``ndarray``. If one has to initialise the
``ndarray`` from the list, then there is no gain, because the iterator
was simple pushed into the initialisation function.
was simply pushed into the initialisation function.

Numerical
=========
Expand Down Expand Up @@ -1823,6 +1815,7 @@ array.
diff
----

numpy:
https://docs.scipy.org/doc/numpy/reference/generated/numpy.diff.html

The ``diff`` function returns the numerical derivative of the forward
Expand Down Expand Up @@ -1895,6 +1888,87 @@ and ``append`` keywords that can be found in ``numpy``.
sort
----

numpy:
https://docs.scipy.org/doc/numpy/reference/generated/numpy.sort.html?highlight=sort#numpy.sort

The sort function takes an ndarray, and sorts it along the specified
axis using a heap sort algorithm. Sorting takes place in place, without
auxiliary storage. The ``axis`` keyword argument, just as in
`diff <#diff>`__, takes on the possible values of -1 (the last axis, in
``ulab`` equivalent to the second axis, and this also happens to be the
default value), 0, or 1.

**WARNING:** ``numpy`` defines the ``kind``, and ``order`` keyword
arguments that are not implemented here. The function in ``ulab`` always
takes heap sort, and since ``ulab`` does not have the concept of data
fields, the ``order`` keyword argument has no meaning in our case.

.. code::
# code to be run in micropython
import ulab as np
a = np.array([[1, 12, 3, 0], [5, 3, 4, 1], [9, 11, 1, 8], [7, 10, 0, 1]], dtype=np.uint8)
print('\na:\n', a)
b = np.sort(a, axis=0)
print('\na sorted along vertical axis:\n', b)
c = np.sort(a, axis=1)
print('\na sorted along horizontal axis:\n', c)
c = np.sort(a, axis=None)
print('\nflat a sorted:\n', c)
.. parsed-literal::
a:
array([[1, 12, 3, 0],
[5, 3, 4, 1],
[9, 11, 1, 8],
[7, 10, 0, 1]], dtype=uint8)
a sorted along vertical axis:
array([[1, 3, 0, 0],
[5, 10, 1, 1],
[7, 11, 3, 1],
[9, 12, 4, 8]], dtype=uint8)
a sorted along horizontal axis:
array([[0, 1, 3, 12],
[1, 3, 4, 5],
[1, 8, 9, 11],
[0, 1, 7, 10]], dtype=uint8)
flat a sorted:
array([0, 0, 1, ..., 10, 11, 12], dtype=uint8)
Heap sort requires :math:`\sim N\log N` operations, and notably, the
worst case costs only 20% more time than the average. In order to get an
order-of-magnitude estimate, we will take the sine of 1000 uniformly
spaced numbers between 0, and two pi, and sort them:

.. code::
# code to be run in micropython
import ulab as np
@timeit
def sort_time(array):
return np.sort(array)
b = np.sin(np.linspace(0, 6.28, num=1000))
print('b: ', b)
sort_time(b)
print('\nb sorted:\n', b)
Linalg
======

Expand Down Expand Up @@ -2996,6 +3070,18 @@ and create a ``QString`` for the function name:
...
};
The exact form of the function object definition

.. code:: c
MP_DEFINE_CONST_FUN_OBJ_1(useless_function_obj, userless_function);
depends naturally on what exactly you implemented, i.e., how many
arguments the function takes, whether only positional arguments allowed
and so on. For a thorough discussion on how function objects have to be
defined, see, e.g., the `user module programming
manual <#https://micropython-usermod.readthedocs.io/en/latest/>`__.

At this point, you should be able to compile the module with your
extension by running ``make`` on the command line

Expand All @@ -3009,7 +3095,14 @@ for the unix port, and
make BOARD=PYBV11 CROSS_COMPILE=<arm_tools_path>/bin/arm-none-eabi- USER_C_MODULES=../../../ulab all
for the ``pyboard``.
for the ``pyboard``, provided that the you have defined

.. code:: makefile
#define MODULE_ULAB_ENABLED (1)
somewhere in ``micropython/port/unix/mpconfigport.h``, or
``micropython/stm32/unix/mpconfigport.h``, respectively.

.. code::
Expand Down
6 changes: 6 additions & 0 deletions docs/ulab-change-log.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@

Mon, 04 Nov 2019

version

added first implementation of sort, and fixed section on compilating the module in the manual

Thu, 31 Oct 2019

version 0.24
Expand Down

0 comments on commit a940870

Please sign in to comment.