arraykit 0.8.1__tar.gz → 0.8.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- arraykit-0.8.3/MANIFEST.in +2 -0
- {arraykit-0.8.1/arraykit.egg-info → arraykit-0.8.3}/PKG-INFO +1 -1
- {arraykit-0.8.1 → arraykit-0.8.3}/README.rst +12 -1
- {arraykit-0.8.1 → arraykit-0.8.3/arraykit.egg-info}/PKG-INFO +1 -1
- {arraykit-0.8.1 → arraykit-0.8.3}/arraykit.egg-info/SOURCES.txt +7 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/setup.py +1 -1
- {arraykit-0.8.1 → arraykit-0.8.3}/src/__init__.pyi +1 -1
- arraykit-0.8.3/src/array_go.h +8 -0
- arraykit-0.8.3/src/array_to_tuple.h +14 -0
- arraykit-0.8.3/src/block_index.h +15 -0
- arraykit-0.8.3/src/delimited_to_arrays.h +16 -0
- arraykit-0.8.3/src/methods.h +72 -0
- arraykit-0.8.3/src/tri_map.h +8 -0
- arraykit-0.8.3/src/utilities.h +321 -0
- arraykit-0.8.1/MANIFEST.in +0 -1
- {arraykit-0.8.1 → arraykit-0.8.3}/LICENSE.txt +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/arraykit.egg-info/dependency_links.txt +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/arraykit.egg-info/requires.txt +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/arraykit.egg-info/top_level.txt +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/setup.cfg +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/src/__init__.py +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/src/_arraykit.c +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/src/array_go.c +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/src/array_to_tuple.c +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/src/block_index.c +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/src/delimited_to_arrays.c +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/src/methods.c +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/src/py.typed +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/src/tri_map.c +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/test/test_array_go.py +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/test/test_block_index.py +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/test/test_delimited_to_arrays.py +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/test/test_delimited_to_arrays_integration.py +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/test/test_delimited_to_arrays_property.py +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/test/test_nonzero_1d.py +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/test/test_nonzero_1d_property.py +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/test/test_pyi.py +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/test/test_split_after_count.py +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/test/test_tri_map.py +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/test/test_type_discovery.py +0 -0
- {arraykit-0.8.1 → arraykit-0.8.3}/test/test_util.py +0 -0
|
@@ -37,11 +37,22 @@ ArrayKit requires the following:
|
|
|
37
37
|
What is New in ArrayKit
|
|
38
38
|
-------------------------
|
|
39
39
|
|
|
40
|
+
0.8.3
|
|
41
|
+
............
|
|
42
|
+
|
|
43
|
+
Corrected incorrect component of ``_TLabel`` in ``__init__.pyi``.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
0.8.2
|
|
47
|
+
............
|
|
48
|
+
|
|
49
|
+
Updated build configuration.
|
|
50
|
+
|
|
40
51
|
|
|
41
52
|
0.8.1
|
|
42
53
|
............
|
|
43
54
|
|
|
44
|
-
Updated build
|
|
55
|
+
Updated build configuration.
|
|
45
56
|
|
|
46
57
|
|
|
47
58
|
0.8.0
|
|
@@ -11,12 +11,19 @@ src/__init__.py
|
|
|
11
11
|
src/__init__.pyi
|
|
12
12
|
src/_arraykit.c
|
|
13
13
|
src/array_go.c
|
|
14
|
+
src/array_go.h
|
|
14
15
|
src/array_to_tuple.c
|
|
16
|
+
src/array_to_tuple.h
|
|
15
17
|
src/block_index.c
|
|
18
|
+
src/block_index.h
|
|
16
19
|
src/delimited_to_arrays.c
|
|
20
|
+
src/delimited_to_arrays.h
|
|
17
21
|
src/methods.c
|
|
22
|
+
src/methods.h
|
|
18
23
|
src/py.typed
|
|
19
24
|
src/tri_map.c
|
|
25
|
+
src/tri_map.h
|
|
26
|
+
src/utilities.h
|
|
20
27
|
test/test_array_go.py
|
|
21
28
|
test/test_block_index.py
|
|
22
29
|
test/test_delimited_to_arrays.py
|
|
@@ -5,7 +5,7 @@ from setuptools import Extension # type: ignore
|
|
|
5
5
|
from setuptools import setup
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
|
-
AK_VERSION = '0.8.
|
|
8
|
+
AK_VERSION = '0.8.3'
|
|
9
9
|
|
|
10
10
|
def get_long_description() -> str:
|
|
11
11
|
return '''The ArrayKit library provides utilities for creating and transforming NumPy arrays, implementing performance-critical StaticFrame operations as Python C extensions.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# ifndef ARRAYKIT_SRC_ARRAY_TO_TUPLE_H_
|
|
2
|
+
# define ARRAYKIT_SRC_ARRAY_TO_TUPLE_H_
|
|
3
|
+
|
|
4
|
+
# include "Python.h"
|
|
5
|
+
|
|
6
|
+
// Given a 1D or 2D array, return a 1D object array of tuples.
|
|
7
|
+
PyObject *
|
|
8
|
+
array_to_tuple_array(PyObject *Py_UNUSED(m), PyObject *a);
|
|
9
|
+
|
|
10
|
+
// Given a 2D array, return an iterator of row tuples.
|
|
11
|
+
PyObject *
|
|
12
|
+
array_to_tuple_iter(PyObject *Py_UNUSED(m), PyObject *a);
|
|
13
|
+
|
|
14
|
+
# endif // ARRAYKIT_SRC_ARRAY_TO_TUPLE_H_
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# ifndef ARRAYKIT_SRC_BLOCK_INDEX_H_
|
|
2
|
+
# define ARRAYKIT_SRC_BLOCK_INDEX_H_
|
|
3
|
+
|
|
4
|
+
# include "Python.h"
|
|
5
|
+
|
|
6
|
+
extern PyTypeObject BlockIndexType;
|
|
7
|
+
extern PyObject * ErrorInitTypeBlocks;
|
|
8
|
+
extern PyTypeObject BIIterType;
|
|
9
|
+
extern PyTypeObject BIIterSeqType;
|
|
10
|
+
extern PyTypeObject BIIterSliceType;
|
|
11
|
+
extern PyTypeObject BIIterBoolType;
|
|
12
|
+
extern PyTypeObject BIIterContiguousType;
|
|
13
|
+
extern PyTypeObject BIIterBlockType;
|
|
14
|
+
|
|
15
|
+
# endif /* ARRAYKIT_SRC_BLOCK_INDEX_H_ */
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# ifndef ARRAYKIT_SRC_DELIMITED_TO_ARRAYS_H_
|
|
2
|
+
# define ARRAYKIT_SRC_DELIMITED_TO_ARRAYS_H_
|
|
3
|
+
|
|
4
|
+
# include "Python.h"
|
|
5
|
+
|
|
6
|
+
// NOTE: implement skip_header, skip_footer in client Python, not here.
|
|
7
|
+
PyObject *
|
|
8
|
+
delimited_to_arrays(PyObject *Py_UNUSED(m), PyObject *args, PyObject *kwargs);
|
|
9
|
+
|
|
10
|
+
PyObject *
|
|
11
|
+
iterable_str_to_array_1d(PyObject *Py_UNUSED(m), PyObject *args, PyObject *kwargs);
|
|
12
|
+
|
|
13
|
+
PyObject *
|
|
14
|
+
split_after_count(PyObject *Py_UNUSED(m), PyObject *args, PyObject *kwargs);
|
|
15
|
+
|
|
16
|
+
# endif /* ARRAYKIT_SRC_DELIMITED_TO_ARRAYS_H_ */
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# ifndef ARRAYKIT_SRC_METHODS_H
|
|
2
|
+
# define ARRAYKIT_SRC_METHODS_H
|
|
3
|
+
# include "Python.h"
|
|
4
|
+
|
|
5
|
+
// A fast counter of unsized iterators
|
|
6
|
+
PyObject *
|
|
7
|
+
count_iteration(PyObject *Py_UNUSED(m), PyObject *iterable);
|
|
8
|
+
|
|
9
|
+
// Reshape if necessary a row that might be 2D or 1D is returned as a 1D array.
|
|
10
|
+
PyObject *
|
|
11
|
+
row_1d_filter(PyObject *Py_UNUSED(m), PyObject *a);
|
|
12
|
+
|
|
13
|
+
// Convert any slice to an ascending slice that covers the same values.
|
|
14
|
+
PyObject *
|
|
15
|
+
slice_to_ascending_slice(PyObject *Py_UNUSED(m), PyObject *args);
|
|
16
|
+
|
|
17
|
+
// Reshape if necessary a flat ndim 1 array into a 2D array with one columns and rows of length.
|
|
18
|
+
// related example: https://github.com/RhysU/ar/blob/master/ar-python.cpp
|
|
19
|
+
PyObject *
|
|
20
|
+
column_2d_filter(PyObject *Py_UNUSED(m), PyObject *a);
|
|
21
|
+
|
|
22
|
+
// Reshape if necessary a column that might be 2D or 1D is returned as a 1D array.
|
|
23
|
+
PyObject *
|
|
24
|
+
column_1d_filter(PyObject *Py_UNUSED(m), PyObject *a);
|
|
25
|
+
|
|
26
|
+
// Represent a 1D array as a 2D array with length as rows of a single-column array.
|
|
27
|
+
// https://stackoverflow.com/questions/56182259/how-does-one-acces-numpy-multidimensionnal-array-in-c-extensions
|
|
28
|
+
PyObject *
|
|
29
|
+
shape_filter(PyObject *Py_UNUSED(m), PyObject *a);
|
|
30
|
+
|
|
31
|
+
PyObject *
|
|
32
|
+
name_filter(PyObject *Py_UNUSED(m), PyObject *n);
|
|
33
|
+
|
|
34
|
+
// Return the integer version of the pointer to underlying data-buffer of array.
|
|
35
|
+
PyObject *
|
|
36
|
+
mloc(PyObject *Py_UNUSED(m), PyObject *a);
|
|
37
|
+
|
|
38
|
+
PyObject *
|
|
39
|
+
immutable_filter(PyObject *Py_UNUSED(m), PyObject *a);
|
|
40
|
+
|
|
41
|
+
PyObject *
|
|
42
|
+
resolve_dtype(PyObject *Py_UNUSED(m), PyObject *args);
|
|
43
|
+
|
|
44
|
+
PyObject *
|
|
45
|
+
resolve_dtype_iter(PyObject *Py_UNUSED(m), PyObject *arg);
|
|
46
|
+
|
|
47
|
+
PyObject *
|
|
48
|
+
nonzero_1d(PyObject *Py_UNUSED(m), PyObject *a);
|
|
49
|
+
|
|
50
|
+
PyObject *
|
|
51
|
+
first_true_1d(PyObject *Py_UNUSED(m), PyObject *args, PyObject *kwargs);
|
|
52
|
+
|
|
53
|
+
PyObject *
|
|
54
|
+
first_true_2d(PyObject *Py_UNUSED(m), PyObject *args, PyObject *kwargs);
|
|
55
|
+
|
|
56
|
+
PyObject *
|
|
57
|
+
dtype_from_element(PyObject *Py_UNUSED(m), PyObject *arg);
|
|
58
|
+
|
|
59
|
+
PyObject *
|
|
60
|
+
isna_element(PyObject *m, PyObject *args, PyObject *kwargs);
|
|
61
|
+
|
|
62
|
+
PyObject *
|
|
63
|
+
get_new_indexers_and_screen(PyObject *Py_UNUSED(m), PyObject *args, PyObject *kwargs);
|
|
64
|
+
|
|
65
|
+
// Specialized array deepcopy that stores immutable arrays in an optional memo dict that can be provided with kwargs.
|
|
66
|
+
PyObject *
|
|
67
|
+
array_deepcopy(PyObject *m, PyObject *args, PyObject *kwargs);
|
|
68
|
+
|
|
69
|
+
PyObject *
|
|
70
|
+
immutable_filter(PyObject *Py_UNUSED(m), PyObject *a);
|
|
71
|
+
|
|
72
|
+
# endif /* ARRAYKIT_SRC_METHODS_H */
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
#ifndef ARRAYKIT_SRC_UTILITIES_H_
|
|
2
|
+
#define ARRAYKIT_SRC_UTILITIES_H_
|
|
3
|
+
|
|
4
|
+
# include "Python.h"
|
|
5
|
+
# include "stdbool.h"
|
|
6
|
+
|
|
7
|
+
# define NO_IMPORT_ARRAY
|
|
8
|
+
# define PY_ARRAY_UNIQUE_SYMBOL AK_ARRAY_API
|
|
9
|
+
# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
|
|
10
|
+
|
|
11
|
+
# include "numpy/arrayobject.h"
|
|
12
|
+
|
|
13
|
+
static const size_t UCS4_SIZE = sizeof(Py_UCS4);
|
|
14
|
+
|
|
15
|
+
//------------------------------------------------------------------------------
|
|
16
|
+
// Macros
|
|
17
|
+
//------------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
// Given a PyObject, raise if not an array.
|
|
20
|
+
# define AK_CHECK_NUMPY_ARRAY(O) \
|
|
21
|
+
if (!PyArray_Check(O)) { \
|
|
22
|
+
return PyErr_Format(PyExc_TypeError, \
|
|
23
|
+
"Expected NumPy array, not %s.", \
|
|
24
|
+
Py_TYPE(O)->tp_name); \
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Given a PyObject, raise if not an array or is not one or two dimensional.
|
|
28
|
+
# define AK_CHECK_NUMPY_ARRAY_1D_2D(O) \
|
|
29
|
+
do { \
|
|
30
|
+
AK_CHECK_NUMPY_ARRAY(O) \
|
|
31
|
+
int ndim = PyArray_NDIM((PyArrayObject *)O); \
|
|
32
|
+
if (ndim != 1 && ndim != 2) { \
|
|
33
|
+
return PyErr_Format(PyExc_NotImplementedError,\
|
|
34
|
+
"Expected 1D or 2D array, not %i.", \
|
|
35
|
+
ndim); \
|
|
36
|
+
} \
|
|
37
|
+
} while (0)
|
|
38
|
+
|
|
39
|
+
// Given a PyObject, raise if not an array or is not two dimensional.
|
|
40
|
+
# define AK_CHECK_NUMPY_ARRAY_2D(O) \
|
|
41
|
+
do { \
|
|
42
|
+
AK_CHECK_NUMPY_ARRAY(O) \
|
|
43
|
+
int ndim = PyArray_NDIM((PyArrayObject *)O); \
|
|
44
|
+
if (ndim != 2) { \
|
|
45
|
+
return PyErr_Format(PyExc_NotImplementedError,\
|
|
46
|
+
"Expected a 2D array, not %i.", \
|
|
47
|
+
ndim); \
|
|
48
|
+
} \
|
|
49
|
+
} while (0)
|
|
50
|
+
|
|
51
|
+
# if defined __GNUC__ || defined __clang__
|
|
52
|
+
# define AK_LIKELY(X) __builtin_expect(!!(X), 1)
|
|
53
|
+
# define AK_UNLIKELY(X) __builtin_expect(!!(X), 0)
|
|
54
|
+
# else
|
|
55
|
+
# define AK_LIKELY(X) (!!(X))
|
|
56
|
+
# define AK_UNLIKELY(X) (!!(X))
|
|
57
|
+
# endif
|
|
58
|
+
|
|
59
|
+
// Placeholder of not implemented pathways / debugging.
|
|
60
|
+
# define AK_NOT_IMPLEMENTED(msg) \
|
|
61
|
+
do { \
|
|
62
|
+
PyErr_SetString(PyExc_NotImplementedError, msg);\
|
|
63
|
+
return NULL; \
|
|
64
|
+
} while (0)
|
|
65
|
+
|
|
66
|
+
# define _AK_DEBUG_BEGIN() \
|
|
67
|
+
do { \
|
|
68
|
+
fprintf(stderr, "--- %s: %i: %s: ", __FILE__, __LINE__, __FUNCTION__);
|
|
69
|
+
|
|
70
|
+
# define _AK_DEBUG_END() \
|
|
71
|
+
fprintf(stderr, "\n"); \
|
|
72
|
+
fflush(stderr); \
|
|
73
|
+
} while (0)
|
|
74
|
+
|
|
75
|
+
# define AK_DEBUG(msg) \
|
|
76
|
+
_AK_DEBUG_BEGIN(); \
|
|
77
|
+
fprintf(stderr, #msg); \
|
|
78
|
+
_AK_DEBUG_END()
|
|
79
|
+
|
|
80
|
+
# define AK_DEBUG_MSG_OBJ(msg, obj) \
|
|
81
|
+
_AK_DEBUG_BEGIN(); \
|
|
82
|
+
fprintf(stderr, #msg " "); \
|
|
83
|
+
PyObject_Print(obj, stderr, 0); \
|
|
84
|
+
_AK_DEBUG_END()
|
|
85
|
+
|
|
86
|
+
# define AK_DEBUG_MSG_REFCNT(msg, obj) \
|
|
87
|
+
_AK_DEBUG_BEGIN(); \
|
|
88
|
+
fprintf(stderr, #msg " ref count: "); \
|
|
89
|
+
PyObject_Print(PyLong_FromSsize_t(Py_REFCNT((PyObject*)obj)), stderr, 0); \
|
|
90
|
+
_AK_DEBUG_END()
|
|
91
|
+
|
|
92
|
+
# define AK_DEBUG_OBJ(obj) \
|
|
93
|
+
_AK_DEBUG_BEGIN(); \
|
|
94
|
+
fprintf(stderr, #obj " = "); \
|
|
95
|
+
PyObject_Print(obj, stderr, 0); \
|
|
96
|
+
_AK_DEBUG_END()
|
|
97
|
+
|
|
98
|
+
// Takes and returns a PyArrayObject, optionally copying a mutable array and setting it as immutable
|
|
99
|
+
static inline PyArrayObject *
|
|
100
|
+
AK_immutable_filter(PyArrayObject *a)
|
|
101
|
+
{
|
|
102
|
+
// https://numpy.org/devdocs/reference/c-api/array.html#array-flags
|
|
103
|
+
if (PyArray_FLAGS(a) & NPY_ARRAY_WRITEABLE) {
|
|
104
|
+
if ((a = (PyArrayObject *)PyArray_NewCopy(a, NPY_ANYORDER))) {
|
|
105
|
+
PyArray_CLEARFLAGS(a, NPY_ARRAY_WRITEABLE);
|
|
106
|
+
}
|
|
107
|
+
return a;
|
|
108
|
+
}
|
|
109
|
+
Py_INCREF(a);
|
|
110
|
+
return a;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Returns NULL on error.
|
|
114
|
+
static inline PyArray_Descr *
|
|
115
|
+
AK_resolve_dtype(PyArray_Descr *d1, PyArray_Descr *d2)
|
|
116
|
+
{
|
|
117
|
+
if (PyArray_EquivTypes(d1, d2)) {
|
|
118
|
+
Py_INCREF(d1);
|
|
119
|
+
return d1;
|
|
120
|
+
}
|
|
121
|
+
if (PyDataType_ISOBJECT(d1) || PyDataType_ISOBJECT(d2)
|
|
122
|
+
|| PyDataType_ISBOOL(d1) || PyDataType_ISBOOL(d2)
|
|
123
|
+
|| (PyDataType_ISSTRING(d1) != PyDataType_ISSTRING(d2))
|
|
124
|
+
|| ((PyDataType_ISDATETIME(d1) || PyDataType_ISDATETIME(d2))
|
|
125
|
+
// PyDataType_ISDATETIME matches NPY_DATETIME or NPY_TIMEDELTA, so
|
|
126
|
+
// we need to make sure we didn't get one of each:
|
|
127
|
+
&& !PyArray_EquivTypenums(d1->type_num, d2->type_num)))
|
|
128
|
+
{
|
|
129
|
+
return PyArray_DescrFromType(NPY_OBJECT);
|
|
130
|
+
}
|
|
131
|
+
PyArray_Descr *d = PyArray_PromoteTypes(d1, d2);
|
|
132
|
+
if (!d) {
|
|
133
|
+
PyErr_Clear();
|
|
134
|
+
return PyArray_DescrFromType(NPY_OBJECT);
|
|
135
|
+
}
|
|
136
|
+
return d;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Returns NULL on error. Returns a new reference.
|
|
140
|
+
static inline PyObject *
|
|
141
|
+
AK_build_pair_ssize_t(Py_ssize_t a, Py_ssize_t b)
|
|
142
|
+
{
|
|
143
|
+
PyObject* t = PyTuple_New(2);
|
|
144
|
+
if (t == NULL) {
|
|
145
|
+
return NULL;
|
|
146
|
+
}
|
|
147
|
+
PyObject* py_a = PyLong_FromSsize_t(a);
|
|
148
|
+
if (py_a == NULL) {
|
|
149
|
+
Py_DECREF(t);
|
|
150
|
+
return NULL;
|
|
151
|
+
}
|
|
152
|
+
PyObject* py_b = PyLong_FromSsize_t(b);
|
|
153
|
+
if (py_b == NULL) {
|
|
154
|
+
Py_DECREF(t);
|
|
155
|
+
Py_DECREF(py_a);
|
|
156
|
+
return NULL;
|
|
157
|
+
}
|
|
158
|
+
// steals refs
|
|
159
|
+
PyTuple_SET_ITEM(t, 0, py_a);
|
|
160
|
+
PyTuple_SET_ITEM(t, 1, py_b);
|
|
161
|
+
return t;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Returns a new ref; returns NULL on error. Any start or stop less than 0 will be set to NULL.
|
|
165
|
+
static inline PyObject *
|
|
166
|
+
AK_build_slice(Py_ssize_t start, Py_ssize_t stop, Py_ssize_t step)
|
|
167
|
+
{
|
|
168
|
+
PyObject* py_start = NULL;
|
|
169
|
+
PyObject* py_stop = NULL;
|
|
170
|
+
PyObject* py_step = NULL;
|
|
171
|
+
|
|
172
|
+
if (start >= 0) {
|
|
173
|
+
py_start = PyLong_FromSsize_t(start);
|
|
174
|
+
if (py_start == NULL) {return NULL;}
|
|
175
|
+
}
|
|
176
|
+
if (stop >= 0) {
|
|
177
|
+
py_stop = PyLong_FromSsize_t(stop);
|
|
178
|
+
if (py_stop == NULL) {return NULL;}
|
|
179
|
+
}
|
|
180
|
+
// do not set a step if not necessary
|
|
181
|
+
if (step != 0 && step != 1) {
|
|
182
|
+
py_step = PyLong_FromSsize_t(step);
|
|
183
|
+
if (py_step == NULL) {return NULL;}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// might be NULL, let return
|
|
187
|
+
PyObject* new = PySlice_New(py_start, py_stop, py_step);
|
|
188
|
+
|
|
189
|
+
Py_XDECREF(py_start);
|
|
190
|
+
Py_XDECREF(py_stop);
|
|
191
|
+
Py_XDECREF(py_step);
|
|
192
|
+
|
|
193
|
+
return new;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Utility function for converting slices; returns NULL on error; returns a new reference.
|
|
197
|
+
static inline PyObject *
|
|
198
|
+
AK_slice_to_ascending_slice(PyObject* slice, Py_ssize_t size)
|
|
199
|
+
{
|
|
200
|
+
Py_ssize_t step_count = -1;
|
|
201
|
+
Py_ssize_t start = 0;
|
|
202
|
+
Py_ssize_t stop = 0;
|
|
203
|
+
Py_ssize_t step = 0;
|
|
204
|
+
|
|
205
|
+
if (PySlice_Unpack(slice, &start, &stop, &step)) {
|
|
206
|
+
return NULL;
|
|
207
|
+
}
|
|
208
|
+
if (step > 0) {
|
|
209
|
+
Py_INCREF(slice);
|
|
210
|
+
return slice;
|
|
211
|
+
}
|
|
212
|
+
step_count = PySlice_AdjustIndices(
|
|
213
|
+
size,
|
|
214
|
+
&start,
|
|
215
|
+
&stop,
|
|
216
|
+
step);
|
|
217
|
+
|
|
218
|
+
// step will be negative; shift original start value down to find new start
|
|
219
|
+
return AK_build_slice(
|
|
220
|
+
start + (step * (step_count - 1)),
|
|
221
|
+
start + 1,
|
|
222
|
+
-step);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Given a Boolean, contiguous 1D array, return the index positions in an int64 array. Through experimentation it has been verified that doing full-size allocation of memory provides the best performance at all scales. Using NpyIter, or using, bit masks does not improve performance over pointer arithmetic. Prescanning for all empty is very effective. Note that NumPy benefits from first counting the nonzeros, then allocating only enough data for the expexted number of indices.
|
|
226
|
+
static inline PyObject *
|
|
227
|
+
AK_nonzero_1d(PyArrayObject* array) {
|
|
228
|
+
PyObject* final;
|
|
229
|
+
npy_intp count_max = PyArray_SIZE(array);
|
|
230
|
+
|
|
231
|
+
if (count_max == 0) { // return empty array
|
|
232
|
+
npy_intp dims = {count_max};
|
|
233
|
+
final = PyArray_SimpleNew(1, &dims, NPY_INT64);
|
|
234
|
+
PyArray_CLEARFLAGS((PyArrayObject*)final, NPY_ARRAY_WRITEABLE);
|
|
235
|
+
return final;
|
|
236
|
+
}
|
|
237
|
+
lldiv_t size_div = lldiv((long long)count_max, 8); // quot, rem
|
|
238
|
+
|
|
239
|
+
Py_ssize_t count = 0;
|
|
240
|
+
// the maximum number of collected integers is equal to or less than count_max
|
|
241
|
+
Py_ssize_t capacity = count_max;
|
|
242
|
+
npy_int64* indices = (npy_int64*)malloc(sizeof(npy_int64) * capacity);
|
|
243
|
+
|
|
244
|
+
NPY_BEGIN_THREADS_DEF;
|
|
245
|
+
NPY_BEGIN_THREADS;
|
|
246
|
+
|
|
247
|
+
if (PyArray_IS_C_CONTIGUOUS(array)) {
|
|
248
|
+
npy_bool* p_start = (npy_bool*)PyArray_DATA(array);
|
|
249
|
+
npy_bool* p = p_start;
|
|
250
|
+
npy_bool* p_end = p + count_max;
|
|
251
|
+
npy_bool* p_end_roll = p_end - size_div.rem;
|
|
252
|
+
|
|
253
|
+
while (p < p_end_roll) {
|
|
254
|
+
if (*(npy_uint64*)p == 0) {
|
|
255
|
+
p += 8; // no true within this 8 byte roll region
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (*p) {indices[count++] = p - p_start;}
|
|
259
|
+
p++;
|
|
260
|
+
if (*p) {indices[count++] = p - p_start;}
|
|
261
|
+
p++;
|
|
262
|
+
if (*p) {indices[count++] = p - p_start;}
|
|
263
|
+
p++;
|
|
264
|
+
if (*p) {indices[count++] = p - p_start;}
|
|
265
|
+
p++;
|
|
266
|
+
if (*p) {indices[count++] = p - p_start;}
|
|
267
|
+
p++;
|
|
268
|
+
if (*p) {indices[count++] = p - p_start;}
|
|
269
|
+
p++;
|
|
270
|
+
if (*p) {indices[count++] = p - p_start;}
|
|
271
|
+
p++;
|
|
272
|
+
if (*p) {indices[count++] = p - p_start;}
|
|
273
|
+
p++;
|
|
274
|
+
}
|
|
275
|
+
while (p < p_end) {
|
|
276
|
+
if (*p) {indices[count++] = p - p_start;}
|
|
277
|
+
p++;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
npy_intp i = 0; // position within Boolean array
|
|
282
|
+
npy_intp i_end = count_max;
|
|
283
|
+
npy_intp i_end_roll = count_max - size_div.rem;
|
|
284
|
+
while (i < i_end_roll) {
|
|
285
|
+
if (*(npy_bool*)PyArray_GETPTR1(array, i)) {indices[count++] = i;}
|
|
286
|
+
i++;
|
|
287
|
+
if (*(npy_bool*)PyArray_GETPTR1(array, i)) {indices[count++] = i;}
|
|
288
|
+
i++;
|
|
289
|
+
if (*(npy_bool*)PyArray_GETPTR1(array, i)) {indices[count++] = i;}
|
|
290
|
+
i++;
|
|
291
|
+
if (*(npy_bool*)PyArray_GETPTR1(array, i)) {indices[count++] = i;}
|
|
292
|
+
i++;
|
|
293
|
+
if (*(npy_bool*)PyArray_GETPTR1(array, i)) {indices[count++] = i;}
|
|
294
|
+
i++;
|
|
295
|
+
if (*(npy_bool*)PyArray_GETPTR1(array, i)) {indices[count++] = i;}
|
|
296
|
+
i++;
|
|
297
|
+
if (*(npy_bool*)PyArray_GETPTR1(array, i)) {indices[count++] = i;}
|
|
298
|
+
i++;
|
|
299
|
+
if (*(npy_bool*)PyArray_GETPTR1(array, i)) {indices[count++] = i;}
|
|
300
|
+
i++;
|
|
301
|
+
}
|
|
302
|
+
while (i < i_end) {
|
|
303
|
+
if (*(npy_bool*)PyArray_GETPTR1(array, i)) {indices[count++] = i;}
|
|
304
|
+
i++;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
NPY_END_THREADS;
|
|
308
|
+
|
|
309
|
+
npy_intp dims = {count};
|
|
310
|
+
final = PyArray_SimpleNewFromData(1, &dims, NPY_INT64, (void*)indices);
|
|
311
|
+
if (!final) {
|
|
312
|
+
free(indices);
|
|
313
|
+
return NULL;
|
|
314
|
+
}
|
|
315
|
+
// This ensures that the array frees the indices array; this has been tested by calling free(indices) and observing segfault
|
|
316
|
+
PyArray_ENABLEFLAGS((PyArrayObject*)final, NPY_ARRAY_OWNDATA);
|
|
317
|
+
PyArray_CLEARFLAGS((PyArrayObject*)final, NPY_ARRAY_WRITEABLE);
|
|
318
|
+
return final;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
#endif /* ARRAYKIT_SRC_UTILITIES_H_ */
|
arraykit-0.8.1/MANIFEST.in
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
include arraykit/__init__.pyi arraykit/py.typed
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|