Skip to content

Commit e4fa4cb

Browse files
authored
Merge pull request #303 from v923z/isinf
adds Isinf, isfinite
2 parents 9611a0a + 66f0851 commit e4fa4cb

File tree

13 files changed

+502
-37
lines changed

13 files changed

+502
-37
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@
44
/*.out
55
/docs/manual/build/
66
/docs/manual/source/**/*.pyi
7+
/docs/.ipynb_checkpoints/
8+
/docs/ulab-test.ipynb
9+
/code/.atom-build.yml

code/numpy/compare/compare.c

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,108 @@ mp_obj_t compare_not_equal(mp_obj_t x1, mp_obj_t x2) {
174174
MP_DEFINE_CONST_FUN_OBJ_2(compare_not_equal_obj, compare_not_equal);
175175
#endif
176176

177-
#if ULAB_NUMPY_HAS_MAXIMUM
177+
#if ULAB_NUMPY_HAS_ISFINITE | ULAB_NUMPY_HAS_ISINF
178+
static mp_obj_t compare_isinf_isfinite(mp_obj_t _x, uint8_t mask) {
179+
// mask should signify, whether the function is called from isinf (mask = 1),
180+
// or from isfinite (mask = 0)
181+
if(MP_OBJ_IS_INT(_x)) {
182+
if(mask) {
183+
return mp_const_false;
184+
} else {
185+
return mp_const_true;
186+
}
187+
} else if(mp_obj_is_float(_x)) {
188+
mp_float_t x = mp_obj_get_float(_x);
189+
if(isnan(x)) {
190+
return mp_const_false;
191+
}
192+
if(mask) { // called from isinf
193+
return isinf(x) ? mp_const_true : mp_const_false;
194+
} else { // called from isfinite
195+
return isinf(x) ? mp_const_false : mp_const_true;
196+
}
197+
} else if(MP_OBJ_IS_TYPE(_x, &ulab_ndarray_type)) {
198+
ndarray_obj_t *x = MP_OBJ_TO_PTR(_x);
199+
ndarray_obj_t *results = ndarray_new_dense_ndarray(x->ndim, x->shape, NDARRAY_BOOL);
200+
// At this point, results is all False
201+
uint8_t *rarray = (uint8_t *)results->array;
202+
if(x->dtype != NDARRAY_FLOAT) {
203+
// int types can never be infinite...
204+
if(!mask) {
205+
// ...so flip all values in the array, if the function was called from isfinite
206+
memset(rarray, 1, results->len);
207+
}
208+
return results;
209+
}
210+
uint8_t *xarray = (uint8_t *)x->array;
211+
212+
#if ULAB_MAX_DIMS > 3
213+
size_t i = 0;
214+
do {
215+
#endif
216+
#if ULAB_MAX_DIMS > 2
217+
size_t j = 0;
218+
do {
219+
#endif
220+
#if ULAB_MAX_DIMS > 1
221+
size_t k = 0;
222+
do {
223+
#endif
224+
size_t l = 0;
225+
do {
226+
mp_float_t value = *(mp_float_t *)xarray;
227+
if(isnan(value)) {
228+
*rarray++ = 0;
229+
} else {
230+
*rarray++ = isinf(value) ? mask : 1 - mask;
231+
}
232+
xarray += x->strides[ULAB_MAX_DIMS - 1];
233+
l++;
234+
} while(l < x->shape[ULAB_MAX_DIMS - 1]);
235+
#if ULAB_MAX_DIMS > 1
236+
xarray -= x->strides[ULAB_MAX_DIMS - 1] * x->shape[ULAB_MAX_DIMS-1];
237+
xarray += x->strides[ULAB_MAX_DIMS - 2];
238+
k++;
239+
} while(k < x->shape[ULAB_MAX_DIMS - 2]);
240+
#endif
241+
#if ULAB_MAX_DIMS > 2
242+
xarray -= x->strides[ULAB_MAX_DIMS - 2] * x->shape[ULAB_MAX_DIMS-2];
243+
xarray += x->strides[ULAB_MAX_DIMS - 3];
244+
j++;
245+
} while(j < x->shape[ULAB_MAX_DIMS - 3]);
246+
#endif
247+
#if ULAB_MAX_DIMS > 3
248+
xarray -= x->strides[ULAB_MAX_DIMS - 3] * x->shape[ULAB_MAX_DIMS-3];
249+
xarray += x->strides[ULAB_MAX_DIMS - 4];
250+
i++;
251+
} while(i < x->shape[ULAB_MAX_DIMS - 4]);
252+
#endif
253+
254+
return results;
255+
} else {
256+
mp_raise_TypeError(translate("wrong input type"));
257+
}
258+
return mp_const_none;
259+
}
260+
#endif
261+
262+
#if ULAB_NUMPY_HAS_ISFINITE
263+
mp_obj_t compare_isfinite(mp_obj_t _x) {
264+
return compare_isinf_isfinite(_x, 0);
265+
}
266+
267+
MP_DEFINE_CONST_FUN_OBJ_1(compare_isfinite_obj, compare_isfinite);
268+
#endif
178269

270+
#if ULAB_NUMPY_HAS_ISINF
271+
mp_obj_t compare_isinf(mp_obj_t _x) {
272+
return compare_isinf_isfinite(_x, 1);
273+
}
274+
275+
MP_DEFINE_CONST_FUN_OBJ_1(compare_isinf_obj, compare_isinf);
276+
#endif
277+
278+
#if ULAB_NUMPY_HAS_MAXIMUM
179279
mp_obj_t compare_maximum(mp_obj_t x1, mp_obj_t x2) {
180280
// extra round, so that we can return maximum(3, 4) properly
181281
mp_obj_t result = compare_function(x1, x2, COMPARE_MAXIMUM);

code/numpy/compare/compare.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ enum COMPARE_FUNCTION_TYPE {
2323
COMPARE_CLIP,
2424
};
2525

26+
MP_DECLARE_CONST_FUN_OBJ_3(compare_clip_obj);
2627
MP_DECLARE_CONST_FUN_OBJ_2(compare_equal_obj);
27-
MP_DECLARE_CONST_FUN_OBJ_2(compare_not_equal_obj);
28+
MP_DECLARE_CONST_FUN_OBJ_2(compare_isfinite_obj);
29+
MP_DECLARE_CONST_FUN_OBJ_2(compare_isinf_obj);
2830
MP_DECLARE_CONST_FUN_OBJ_2(compare_minimum_obj);
2931
MP_DECLARE_CONST_FUN_OBJ_2(compare_maximum_obj);
30-
MP_DECLARE_CONST_FUN_OBJ_3(compare_clip_obj);
32+
MP_DECLARE_CONST_FUN_OBJ_2(compare_not_equal_obj);
3133

3234
#if ULAB_MAX_DIMS == 1
3335
#define COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\

code/numpy/numpy.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ static const mp_rom_map_elem_t ulab_numpy_globals_table[] = {
132132
#if ULAB_NUMPY_HAS_NOTEQUAL
133133
{ MP_OBJ_NEW_QSTR(MP_QSTR_not_equal), (mp_obj_t)&compare_not_equal_obj },
134134
#endif
135+
#if ULAB_NUMPY_HAS_ISFINITE
136+
{ MP_OBJ_NEW_QSTR(MP_QSTR_isfinite), (mp_obj_t)&compare_isfinite_obj },
137+
#endif
138+
#if ULAB_NUMPY_HAS_ISINF
139+
{ MP_OBJ_NEW_QSTR(MP_QSTR_isinf), (mp_obj_t)&compare_isinf_obj },
140+
#endif
135141
#if ULAB_NUMPY_HAS_MAXIMUM
136142
{ MP_OBJ_NEW_QSTR(MP_QSTR_maximum), (mp_obj_t)&compare_maximum_obj },
137143
#endif

code/ulab.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
#include "user/user.h"
3535

36-
#define ULAB_VERSION 2.1.5
36+
#define ULAB_VERSION 2.2.0
3737
#define xstr(s) str(s)
3838
#define str(s) #s
3939
#define ULAB_VERSION_STRING xstr(ULAB_VERSION) xstr(-) xstr(ULAB_MAX_DIMS) xstr(D)

code/ulab.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
// A considerable amount of flash space can be saved by removing (setting
2626
// the corresponding constants to 0) the unnecessary functions and features.
2727

28-
// Values defined here can be overridden by your own config file as
28+
// Values defined here can be overridden by your own config file as
2929
// make -DULAB_CONFIG_FILE="my_ulab_config.h"
3030
#if defined(ULAB_CONFIG_FILE)
3131
#include ULAB_CONFIG_FILE
@@ -307,8 +307,12 @@
307307
#define ULAB_NUMPY_HAS_EQUAL (1)
308308
#endif
309309

310-
#ifndef ULAB_NUMPY_HAS_NOTEQUAL
311-
#define ULAB_NUMPY_HAS_NOTEQUAL (1)
310+
#ifndef ULAB_NUMPY_HAS_ISFINITE
311+
#define ULAB_NUMPY_HAS_ISFINITE (1)
312+
#endif
313+
314+
#ifndef ULAB_NUMPY_HAS_ISINF
315+
#define ULAB_NUMPY_HAS_ISINF (1)
312316
#endif
313317

314318
#ifndef ULAB_NUMPY_HAS_MAXIMUM
@@ -319,6 +323,10 @@
319323
#define ULAB_NUMPY_HAS_MINIMUM (1)
320324
#endif
321325

326+
#ifndef ULAB_NUMPY_HAS_NOTEQUAL
327+
#define ULAB_NUMPY_HAS_NOTEQUAL (1)
328+
#endif
329+
322330
// the linalg module; functions of the linalg module still have
323331
// to be defined separately
324332
#ifndef ULAB_NUMPY_HAS_LINALG_MODULE

docs/manual/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
author = 'Zoltán Vörös'
2828

2929
# The full version, including alpha/beta/rc tags
30-
release = '2.1.2'
30+
release = '2.2.0'
3131

3232

3333
# -- General configuration ---------------------------------------------------

docs/manual/source/numpy-functions.rst

Lines changed: 140 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,22 @@ from ``numpy``.
1414
7. `numpy.equal <#equal>`__
1515
8. `numpy.flip <#flip>`__
1616
9. `numpy.interp <#interp>`__
17-
10. `numpy.max <#max>`__
18-
11. `numpy.maximum <#maximum>`__
19-
12. `numpy.mean <#mean>`__
20-
13. `numpy.median <#median>`__
21-
14. `numpy.min <#min>`__
22-
15. `numpy.minimum <#minimum>`__
23-
16. `numpy.not_equal <#equal>`__
24-
17. `numpy.polyfit <#polyfit>`__
25-
18. `numpy.polyval <#polyval>`__
26-
19. `numpy.roll <#roll>`__
27-
20. `numpy.sort <#sort>`__
28-
21. `numpy.std <#std>`__
29-
22. `numpy.sum <#sum>`__
30-
23. `numpy.trapz <#trapz>`__
17+
10. `numpy.isfinite <#isfinite>`__
18+
11. `numpy.isinf <#isinf>`__
19+
12. `numpy.max <#max>`__
20+
13. `numpy.maximum <#maximum>`__
21+
14. `numpy.mean <#mean>`__
22+
15. `numpy.median <#median>`__
23+
16. `numpy.min <#min>`__
24+
17. `numpy.minimum <#minimum>`__
25+
18. `numpy.not_equal <#equal>`__
26+
19. `numpy.polyfit <#polyfit>`__
27+
20. `numpy.polyval <#polyval>`__
28+
21. `numpy.roll <#roll>`__
29+
22. `numpy.sort <#sort>`__
30+
23. `numpy.std <#std>`__
31+
24. `numpy.sum <#sum>`__
32+
25. `numpy.trapz <#trapz>`__
3133

3234
argmax
3335
------
@@ -426,6 +428,130 @@ respectively. If these arguments are not supplied, ``left``, and
426428
427429
428430
431+
isfinite
432+
--------
433+
434+
``numpy``:
435+
https://numpy.org/doc/stable/reference/generated/numpy.isfinite.html
436+
437+
Returns a Boolean array of the same shape as the input, or a
438+
``True/False``, if the input is a scalar. In the return value, all
439+
elements are ``True`` at positions, where the input value was finite.
440+
Integer types are automatically finite, therefore, if the input is of
441+
integer type, the output will be the ``True`` tensor.
442+
443+
.. code::
444+
445+
# code to be run in micropython
446+
447+
from ulab import numpy as np
448+
449+
print('isfinite(0): ', np.isfinite(0))
450+
451+
a = np.array([1, 2, np.nan])
452+
print('\n' + '='*20)
453+
print('a:\n', a)
454+
print('\nisfinite(a):\n', np.isfinite(a))
455+
456+
b = np.array([1, 2, np.inf])
457+
print('\n' + '='*20)
458+
print('b:\n', b)
459+
print('\nisfinite(b):\n', np.isfinite(b))
460+
461+
c = np.array([1, 2, 3], dtype=np.uint16)
462+
print('\n' + '='*20)
463+
print('c:\n', c)
464+
print('\nisfinite(c):\n', np.isfinite(c))
465+
466+
.. parsed-literal::
467+
468+
isfinite(0): True
469+
470+
====================
471+
a:
472+
array([1.0, 2.0, nan], dtype=float64)
473+
474+
isfinite(a):
475+
array([True, True, False], dtype=bool)
476+
477+
====================
478+
b:
479+
array([1.0, 2.0, inf], dtype=float64)
480+
481+
isfinite(b):
482+
array([True, True, False], dtype=bool)
483+
484+
====================
485+
c:
486+
array([1, 2, 3], dtype=uint16)
487+
488+
isfinite(c):
489+
array([True, True, True], dtype=bool)
490+
491+
492+
493+
494+
isinf
495+
-----
496+
497+
``numpy``:
498+
https://numpy.org/doc/stable/reference/generated/numpy.isinf.html
499+
500+
Similar to `isfinite <#isfinite>`__, but the output is ``True`` at
501+
positions, where the input is infinite. Integer types return the
502+
``False`` tensor.
503+
504+
.. code::
505+
506+
# code to be run in micropython
507+
508+
from ulab import numpy as np
509+
510+
print('isinf(0): ', np.isinf(0))
511+
512+
a = np.array([1, 2, np.nan])
513+
print('\n' + '='*20)
514+
print('a:\n', a)
515+
print('\nisinf(a):\n', np.isinf(a))
516+
517+
b = np.array([1, 2, np.inf])
518+
print('\n' + '='*20)
519+
print('b:\n', b)
520+
print('\nisinf(b):\n', np.isinf(b))
521+
522+
c = np.array([1, 2, 3], dtype=np.uint16)
523+
print('\n' + '='*20)
524+
print('c:\n', c)
525+
print('\nisinf(c):\n', np.isinf(c))
526+
527+
.. parsed-literal::
528+
529+
isinf(0): False
530+
531+
====================
532+
a:
533+
array([1.0, 2.0, nan], dtype=float64)
534+
535+
isinf(a):
536+
array([False, False, False], dtype=bool)
537+
538+
====================
539+
b:
540+
array([1.0, 2.0, inf], dtype=float64)
541+
542+
isinf(b):
543+
array([False, False, True], dtype=bool)
544+
545+
====================
546+
c:
547+
array([1, 2, 3], dtype=uint16)
548+
549+
isinf(c):
550+
array([False, False, False], dtype=bool)
551+
552+
553+
554+
429555
mean
430556
----
431557

0 commit comments

Comments
 (0)