imops 0.8.2__cp36-cp36m-win32.whl → 0.8.3__cp36-cp36m-win32.whl
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.
Potentially problematic release.
This version of imops might be problematic. Click here for more details.
- imops/__init__.py +1 -0
- imops/__version__.py +1 -1
- imops/backend.py +14 -10
- imops/crop.py +18 -2
- imops/interp1d.py +7 -4
- imops/measure.py +7 -7
- imops/morphology.py +6 -5
- imops/numeric.py +376 -0
- imops/pad.py +41 -5
- imops/radon.py +7 -5
- imops/src/_backprojection.cp36-win32.pyd +0 -0
- imops/src/_fast_backprojection.cp36-win32.pyd +0 -0
- imops/src/_fast_measure.cp36-win32.pyd +0 -0
- imops/src/_fast_morphology.cp36-win32.pyd +0 -0
- imops/src/_fast_numeric.cp36-win32.pyd +0 -0
- imops/src/_fast_numeric.pyx +208 -30
- imops/src/_fast_radon.cp36-win32.pyd +0 -0
- imops/src/_fast_zoom.cp36-win32.pyd +0 -0
- imops/src/_measure.cp36-win32.pyd +0 -0
- imops/src/_morphology.cp36-win32.pyd +0 -0
- imops/src/_numeric.cp36-win32.pyd +0 -0
- imops/src/_numeric.pyx +208 -30
- imops/src/_radon.cp36-win32.pyd +0 -0
- imops/src/_zoom.cp36-win32.pyd +0 -0
- imops/utils.py +65 -12
- imops/zoom.py +2 -2
- {imops-0.8.2.dist-info → imops-0.8.3.dist-info}/METADATA +3 -2
- imops-0.8.3.dist-info/RECORD +46 -0
- imops/_numeric.py +0 -124
- imops-0.8.2.dist-info/RECORD +0 -46
- {imops-0.8.2.dist-info → imops-0.8.3.dist-info}/LICENSE +0 -0
- {imops-0.8.2.dist-info → imops-0.8.3.dist-info}/WHEEL +0 -0
- {imops-0.8.2.dist-info → imops-0.8.3.dist-info}/top_level.txt +0 -0
imops/__init__.py
CHANGED
|
@@ -4,6 +4,7 @@ from .crop import crop_to_box, crop_to_shape
|
|
|
4
4
|
from .interp1d import interp1d
|
|
5
5
|
from .measure import label
|
|
6
6
|
from .morphology import binary_closing, binary_dilation, binary_erosion, binary_opening
|
|
7
|
+
from .numeric import copy, fill_, full, pointwise_add
|
|
7
8
|
from .pad import pad, pad_to_divisible, pad_to_shape, restore_crop
|
|
8
9
|
from .radon import inverse_radon, radon
|
|
9
10
|
from .zoom import _zoom, zoom, zoom_to_shape
|
imops/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '0.8.
|
|
1
|
+
__version__ = '0.8.3'
|
imops/backend.py
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
from contextlib import contextmanager
|
|
2
2
|
from dataclasses import dataclass
|
|
3
3
|
from typing import Dict, Type, Union
|
|
4
|
+
from warnings import warn
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class Backend:
|
|
7
8
|
def __init_subclass__(cls, **kwargs):
|
|
8
9
|
name = cls.__name__
|
|
9
|
-
if name in
|
|
10
|
+
if name in _AVAILABLE_BACKENDS:
|
|
10
11
|
raise ValueError(f'The name "{name}" is already in use.')
|
|
11
|
-
|
|
12
|
+
_AVAILABLE_BACKENDS[name] = cls
|
|
12
13
|
if not hasattr(Backend, name):
|
|
13
14
|
setattr(Backend, name, cls)
|
|
14
15
|
|
|
@@ -22,18 +23,18 @@ class Backend:
|
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
BackendLike = Union[str, Backend, Type[Backend], None]
|
|
25
|
-
|
|
26
|
+
_AVAILABLE_BACKENDS: Dict[str, Type[Backend]] = {}
|
|
26
27
|
|
|
27
28
|
|
|
28
|
-
def resolve_backend(value: BackendLike) -> Backend:
|
|
29
|
+
def resolve_backend(value: BackendLike, warn_stacklevel: int = 1) -> Backend:
|
|
29
30
|
if value is None:
|
|
30
31
|
return DEFAULT_BACKEND
|
|
31
32
|
|
|
32
33
|
if isinstance(value, str):
|
|
33
|
-
if value not in
|
|
34
|
-
raise ValueError(f'"{value}" is not in the list of available backends: {tuple(
|
|
34
|
+
if value not in _AVAILABLE_BACKENDS:
|
|
35
|
+
raise ValueError(f'"{value}" is not in the list of available backends: {tuple(_AVAILABLE_BACKENDS)}.')
|
|
35
36
|
|
|
36
|
-
return
|
|
37
|
+
return _AVAILABLE_BACKENDS[value]()
|
|
37
38
|
|
|
38
39
|
if isinstance(value, type):
|
|
39
40
|
value = value()
|
|
@@ -41,6 +42,9 @@ def resolve_backend(value: BackendLike) -> Backend:
|
|
|
41
42
|
if not isinstance(value, Backend):
|
|
42
43
|
raise ValueError(f'Expected a `Backend` instance, got {value}.')
|
|
43
44
|
|
|
45
|
+
if isinstance(value, Cython) and value.fast:
|
|
46
|
+
warn('`fast=True` has no effect for `Cython` backend for now.', stacklevel=warn_stacklevel)
|
|
47
|
+
|
|
44
48
|
return value
|
|
45
49
|
|
|
46
50
|
|
|
@@ -51,7 +55,7 @@ def set_backend(backend: BackendLike) -> Backend:
|
|
|
51
55
|
return current
|
|
52
56
|
|
|
53
57
|
|
|
54
|
-
@
|
|
58
|
+
@contextmanager
|
|
55
59
|
def imops_backend(backend: BackendLike):
|
|
56
60
|
previous = set_backend(backend)
|
|
57
61
|
try:
|
|
@@ -87,5 +91,5 @@ class Scipy(Backend):
|
|
|
87
91
|
|
|
88
92
|
DEFAULT_BACKEND = Cython()
|
|
89
93
|
|
|
90
|
-
|
|
94
|
+
BACKEND_NAME2ENV_NUM_THREADS_VAR_NAME = {Cython.__name__: 'OMP_NUM_THREADS', Numba.__name__: 'NUMBA_NUM_THREADS'}
|
|
91
95
|
SINGLE_THREADED_BACKENDS = (Scipy.__name__,)
|
imops/crop.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
|
|
3
|
+
from .backend import BackendLike
|
|
4
|
+
from .numeric import _NUMERIC_DEFAULT_NUM_THREADS
|
|
3
5
|
from .pad import pad
|
|
4
6
|
from .utils import AxesLike, AxesParams, broadcast_axis, fill_by_indices
|
|
5
7
|
|
|
@@ -43,10 +45,18 @@ def crop_to_shape(x: np.ndarray, shape: AxesLike, axis: AxesLike = None, ratio:
|
|
|
43
45
|
ratio = fill_by_indices(np.zeros(ndim), ratio, axis)
|
|
44
46
|
start = ((old_shape - new_shape) * ratio).astype(int)
|
|
45
47
|
|
|
48
|
+
# TODO: Create contiguous array?
|
|
46
49
|
return x[tuple(map(slice, start, start + new_shape))]
|
|
47
50
|
|
|
48
51
|
|
|
49
|
-
def crop_to_box(
|
|
52
|
+
def crop_to_box(
|
|
53
|
+
x: np.ndarray,
|
|
54
|
+
box: np.ndarray,
|
|
55
|
+
axis: AxesLike = None,
|
|
56
|
+
padding_values: AxesParams = None,
|
|
57
|
+
num_threads: int = _NUMERIC_DEFAULT_NUM_THREADS,
|
|
58
|
+
backend: BackendLike = None,
|
|
59
|
+
) -> np.ndarray:
|
|
50
60
|
"""
|
|
51
61
|
Crop `x` according to `box` along `axis`.
|
|
52
62
|
|
|
@@ -60,6 +70,11 @@ def crop_to_box(x: np.ndarray, box: np.ndarray, axis: AxesLike = None, padding_v
|
|
|
60
70
|
axis along which `x` will be cropped
|
|
61
71
|
padding_values: AxesParams
|
|
62
72
|
values to pad with if box exceeds the input's limits
|
|
73
|
+
num_threads: int
|
|
74
|
+
the number of threads to use for computation. Default = 4. If negative value passed
|
|
75
|
+
cpu count + num_threads + 1 threads will be used
|
|
76
|
+
backend: BackendLike
|
|
77
|
+
which backend to use. `cython` and `scipy` are available, `cython` is used by default
|
|
63
78
|
|
|
64
79
|
Returns
|
|
65
80
|
-------
|
|
@@ -86,9 +101,10 @@ def crop_to_box(x: np.ndarray, box: np.ndarray, axis: AxesLike = None, padding_v
|
|
|
86
101
|
|
|
87
102
|
slice_start = fill_by_indices(np.zeros(x.ndim, int), slice_start, axis)
|
|
88
103
|
slice_stop = fill_by_indices(x.shape, slice_stop, axis)
|
|
104
|
+
# TODO: Create contiguous array?
|
|
89
105
|
x = x[tuple(map(slice, slice_start, slice_stop))]
|
|
90
106
|
|
|
91
107
|
if padding_values is not None and padding.any():
|
|
92
|
-
x = pad(x, padding, axis, padding_values)
|
|
108
|
+
x = pad(x, padding, axis, padding_values, num_threads=num_threads, backend=backend)
|
|
93
109
|
|
|
94
110
|
return x
|
imops/interp1d.py
CHANGED
|
@@ -5,6 +5,7 @@ import numpy as np
|
|
|
5
5
|
from scipy.interpolate import interp1d as scipy_interp1d
|
|
6
6
|
|
|
7
7
|
from .backend import BackendLike, resolve_backend
|
|
8
|
+
from .numeric import copy as _copy
|
|
8
9
|
from .src._fast_zoom import _interp1d as cython_fast_interp1d
|
|
9
10
|
from .src._zoom import _interp1d as cython_interp1d
|
|
10
11
|
from .utils import normalize_num_threads
|
|
@@ -74,7 +75,7 @@ class interp1d:
|
|
|
74
75
|
num_threads: int = -1,
|
|
75
76
|
backend: BackendLike = None,
|
|
76
77
|
) -> None:
|
|
77
|
-
backend = resolve_backend(backend)
|
|
78
|
+
backend = resolve_backend(backend, warn_stacklevel=3)
|
|
78
79
|
if backend.name not in ('Scipy', 'Numba', 'Cython'):
|
|
79
80
|
raise ValueError(f'Unsupported backend "{backend.name}".')
|
|
80
81
|
|
|
@@ -88,6 +89,7 @@ class interp1d:
|
|
|
88
89
|
warn(
|
|
89
90
|
"Fast interpolation is only supported for ndim<=3, dtype=float32 or float64, order=1 or 'linear'. "
|
|
90
91
|
"Falling back to scipy's implementation.",
|
|
92
|
+
stacklevel=2,
|
|
91
93
|
)
|
|
92
94
|
self.scipy_interp1d = scipy_interp1d(x, y, kind, axis, copy, bounds_error, fill_value, assume_sorted)
|
|
93
95
|
else:
|
|
@@ -114,12 +116,13 @@ class interp1d:
|
|
|
114
116
|
|
|
115
117
|
self.fill_value = fill_value
|
|
116
118
|
self.scipy_interp1d = None
|
|
117
|
-
|
|
119
|
+
# FIXME: how to accurately pass `num_threads` and `backend` arguments to `copy`?
|
|
120
|
+
self.x = _copy(x, order='C') if copy else x
|
|
118
121
|
self.n_dummy = 3 - y.ndim
|
|
119
122
|
self.y = y[(None,) * self.n_dummy] if self.n_dummy else y
|
|
120
123
|
|
|
121
124
|
if copy:
|
|
122
|
-
self.y =
|
|
125
|
+
self.y = _copy(self.y, order='C')
|
|
123
126
|
|
|
124
127
|
self.assume_sorted = assume_sorted
|
|
125
128
|
|
|
@@ -149,7 +152,7 @@ class interp1d:
|
|
|
149
152
|
interpolated values. Shape is determined by replacing the interpolation axis in the original array with
|
|
150
153
|
the shape of x
|
|
151
154
|
"""
|
|
152
|
-
num_threads = normalize_num_threads(self.num_threads, self.backend)
|
|
155
|
+
num_threads = normalize_num_threads(self.num_threads, self.backend, warn_stacklevel=3)
|
|
153
156
|
|
|
154
157
|
if self.scipy_interp1d is not None:
|
|
155
158
|
return self.scipy_interp1d(x_new)
|
imops/measure.py
CHANGED
|
@@ -19,7 +19,7 @@ from .utils import normalize_num_threads
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
# (ndim, skimage_connectivity) -> cc3d_connectivity
|
|
22
|
-
|
|
22
|
+
_SKIMAGE2CC3D = {
|
|
23
23
|
(1, 1): 4,
|
|
24
24
|
(2, 1): 4,
|
|
25
25
|
(2, 2): 8,
|
|
@@ -90,7 +90,7 @@ def label(
|
|
|
90
90
|
raise ValueError(f'Connectivity for {ndim}D image should be in [1, ..., {ndim}]. Got {connectivity}.')
|
|
91
91
|
|
|
92
92
|
if ndim > 3:
|
|
93
|
-
warn("Fast label is only supported for ndim<=3, Falling back to scikit-image's implementation.")
|
|
93
|
+
warn("Fast label is only supported for ndim<=3, Falling back to scikit-image's implementation.", stacklevel=2)
|
|
94
94
|
labeled_image, num_components = skimage_label(
|
|
95
95
|
label_image, background=background, return_num=True, connectivity=connectivity
|
|
96
96
|
)
|
|
@@ -110,7 +110,7 @@ def label(
|
|
|
110
110
|
|
|
111
111
|
labeled_image, num_components = connected_components(
|
|
112
112
|
label_image,
|
|
113
|
-
connectivity=
|
|
113
|
+
connectivity=_SKIMAGE2CC3D[(ndim, connectivity)],
|
|
114
114
|
return_N=True,
|
|
115
115
|
**{'out_dtype': dtype} if python_version()[:3] != '3.6' else {},
|
|
116
116
|
)
|
|
@@ -174,19 +174,19 @@ def center_of_mass(
|
|
|
174
174
|
if (labels is None) ^ (index is None):
|
|
175
175
|
raise ValueError('`labels` and `index` should be both specified or both not specified.')
|
|
176
176
|
|
|
177
|
-
backend = resolve_backend(backend)
|
|
177
|
+
backend = resolve_backend(backend, warn_stacklevel=3)
|
|
178
178
|
|
|
179
179
|
if backend.name not in ('Scipy', 'Cython'):
|
|
180
180
|
raise ValueError(f'Unsupported backend "{backend.name}".')
|
|
181
181
|
|
|
182
|
-
num_threads = normalize_num_threads(num_threads, backend)
|
|
182
|
+
num_threads = normalize_num_threads(num_threads, backend, warn_stacklevel=3)
|
|
183
183
|
|
|
184
184
|
if backend.name == 'Scipy':
|
|
185
185
|
return scipy_center_of_mass(array, labels, index)
|
|
186
186
|
|
|
187
187
|
ndim = array.ndim
|
|
188
188
|
if ndim > 3:
|
|
189
|
-
warn("Fast center-of-mass is only supported for ndim<=3. Falling back to scipy's implementation.")
|
|
189
|
+
warn("Fast center-of-mass is only supported for ndim<=3. Falling back to scipy's implementation.", stacklevel=2)
|
|
190
190
|
return scipy_center_of_mass(array, labels, index)
|
|
191
191
|
|
|
192
192
|
if labels is None:
|
|
@@ -205,7 +205,7 @@ def center_of_mass(
|
|
|
205
205
|
raise ValueError('`index` should consist of unique values.')
|
|
206
206
|
|
|
207
207
|
if num_threads > 1:
|
|
208
|
-
warn('Using single-threaded implementation as `labels` and `index` are specified.')
|
|
208
|
+
warn('Using single-threaded implementation as `labels` and `index` are specified.', stacklevel=2)
|
|
209
209
|
|
|
210
210
|
src_center_of_mass = _fast_labeled_center_of_mass if backend.fast else _labeled_center_of_mass
|
|
211
211
|
|
imops/morphology.py
CHANGED
|
@@ -28,12 +28,12 @@ def morphology_op_wrapper(
|
|
|
28
28
|
num_threads: int = -1,
|
|
29
29
|
backend: BackendLike = None,
|
|
30
30
|
) -> np.ndarray:
|
|
31
|
-
backend = resolve_backend(backend)
|
|
31
|
+
backend = resolve_backend(backend, warn_stacklevel=4)
|
|
32
32
|
if backend.name not in {x.name for x in backend2src_op.keys()}:
|
|
33
33
|
raise ValueError(f'Unsupported backend "{backend.name}".')
|
|
34
34
|
|
|
35
35
|
ndim = image.ndim
|
|
36
|
-
num_threads = normalize_num_threads(num_threads, backend)
|
|
36
|
+
num_threads = normalize_num_threads(num_threads, backend, warn_stacklevel=4)
|
|
37
37
|
|
|
38
38
|
if footprint is None:
|
|
39
39
|
footprint = generate_binary_structure(ndim, 1)
|
|
@@ -60,7 +60,8 @@ def morphology_op_wrapper(
|
|
|
60
60
|
if ndim > 3:
|
|
61
61
|
warn(
|
|
62
62
|
f"Fast {' '.join(op_name.split('_'))} is only supported for ndim<=3. "
|
|
63
|
-
"Falling back to scipy's implementation."
|
|
63
|
+
"Falling back to scipy's implementation.",
|
|
64
|
+
stacklevel=3,
|
|
64
65
|
)
|
|
65
66
|
output = backend2src_op[Scipy()](image, footprint)
|
|
66
67
|
|
|
@@ -70,13 +71,13 @@ def morphology_op_wrapper(
|
|
|
70
71
|
raise ValueError('Input image and footprint number of dimensions must be the same.')
|
|
71
72
|
|
|
72
73
|
if not image.any():
|
|
73
|
-
warn(f'{op_name} is applied to the fully False mask (mask.any() == False).')
|
|
74
|
+
warn(f'{op_name} is applied to the fully False mask (mask.any() == False).', stacklevel=3)
|
|
74
75
|
output.fill(False)
|
|
75
76
|
|
|
76
77
|
return output
|
|
77
78
|
|
|
78
79
|
if image.all():
|
|
79
|
-
warn(f'{op_name} is applied to the fully True mask (mask.all() == True).')
|
|
80
|
+
warn(f'{op_name} is applied to the fully True mask (mask.all() == True).', stacklevel=3)
|
|
80
81
|
output.fill(True)
|
|
81
82
|
|
|
82
83
|
return output
|
imops/numeric.py
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
from typing import Callable, Sequence, Union
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from .backend import BackendLike, resolve_backend
|
|
6
|
+
from .src._fast_numeric import (
|
|
7
|
+
_copy_3d as cython_fast_copy_3d,
|
|
8
|
+
_copy_3d_fp16 as cython_fast_copy_3d_fp16,
|
|
9
|
+
_copy_4d as cython_fast_copy_4d,
|
|
10
|
+
_copy_4d_fp16 as cython_fast_copy_4d_fp16,
|
|
11
|
+
_fill_3d as cython_fast_fill_3d,
|
|
12
|
+
_fill_4d as cython_fast_fill_4d,
|
|
13
|
+
_pointwise_add_array_3d as cython_fast_pointwise_add_array_3d,
|
|
14
|
+
_pointwise_add_array_3d_fp16 as cython_fast_pointwise_add_array_3d_fp16,
|
|
15
|
+
_pointwise_add_array_4d as cython_fast_pointwise_add_array_4d,
|
|
16
|
+
_pointwise_add_array_4d_fp16 as cython_fast_pointwise_add_array_4d_fp16,
|
|
17
|
+
_pointwise_add_value_3d as cython_fast_pointwise_add_value_3d,
|
|
18
|
+
_pointwise_add_value_3d_fp16 as cython_fast_pointwise_add_value_3d_fp16,
|
|
19
|
+
_pointwise_add_value_4d as cython_fast_pointwise_add_value_4d,
|
|
20
|
+
_pointwise_add_value_4d_fp16 as cython_fast_pointwise_add_value_4d_fp16,
|
|
21
|
+
)
|
|
22
|
+
from .src._numeric import (
|
|
23
|
+
_copy_3d as cython_copy_3d,
|
|
24
|
+
_copy_3d_fp16 as cython_copy_3d_fp16,
|
|
25
|
+
_copy_4d as cython_copy_4d,
|
|
26
|
+
_copy_4d_fp16 as cython_copy_4d_fp16,
|
|
27
|
+
_fill_3d as cython_fill_3d,
|
|
28
|
+
_fill_4d as cython_fill_4d,
|
|
29
|
+
_pointwise_add_array_3d as cython_pointwise_add_array_3d,
|
|
30
|
+
_pointwise_add_array_3d_fp16 as cython_pointwise_add_array_3d_fp16,
|
|
31
|
+
_pointwise_add_array_4d as cython_pointwise_add_array_4d,
|
|
32
|
+
_pointwise_add_array_4d_fp16 as cython_pointwise_add_array_4d_fp16,
|
|
33
|
+
_pointwise_add_value_3d as cython_pointwise_add_value_3d,
|
|
34
|
+
_pointwise_add_value_3d_fp16 as cython_pointwise_add_value_3d_fp16,
|
|
35
|
+
_pointwise_add_value_4d as cython_pointwise_add_value_4d,
|
|
36
|
+
_pointwise_add_value_4d_fp16 as cython_pointwise_add_value_4d_fp16,
|
|
37
|
+
)
|
|
38
|
+
from .utils import normalize_num_threads
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
_TYPES = (np.int16, np.int32, np.int64, np.float16, np.float32, np.float64)
|
|
42
|
+
_STR_TYPES = ('int16', 'int32', 'int64', 'float16', 'float32', 'float64')
|
|
43
|
+
# TODO: Decide which value to use. Functions below are quite fast and simple, so parallelization overhead is noticeable.
|
|
44
|
+
_NUMERIC_DEFAULT_NUM_THREADS = 4
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# TODO: Maybe dict is better?
|
|
48
|
+
def _choose_cython_pointwise_add(ndim: int, summand_is_array: bool, is_fp16: bool, fast: bool) -> Callable:
|
|
49
|
+
assert ndim <= 4, ndim
|
|
50
|
+
|
|
51
|
+
if ndim <= 3:
|
|
52
|
+
if summand_is_array:
|
|
53
|
+
if is_fp16:
|
|
54
|
+
return cython_fast_pointwise_add_array_3d_fp16 if fast else cython_pointwise_add_array_3d_fp16
|
|
55
|
+
|
|
56
|
+
return cython_fast_pointwise_add_array_3d if fast else cython_pointwise_add_array_3d
|
|
57
|
+
|
|
58
|
+
if is_fp16:
|
|
59
|
+
return cython_fast_pointwise_add_value_3d_fp16 if fast else cython_pointwise_add_value_3d_fp16
|
|
60
|
+
|
|
61
|
+
return cython_fast_pointwise_add_value_3d if fast else cython_pointwise_add_value_3d
|
|
62
|
+
|
|
63
|
+
if summand_is_array:
|
|
64
|
+
if is_fp16:
|
|
65
|
+
return cython_fast_pointwise_add_array_4d_fp16 if fast else cython_pointwise_add_array_4d_fp16
|
|
66
|
+
|
|
67
|
+
return cython_fast_pointwise_add_array_4d if fast else cython_pointwise_add_array_4d
|
|
68
|
+
|
|
69
|
+
if is_fp16:
|
|
70
|
+
return cython_fast_pointwise_add_value_4d_fp16 if fast else cython_pointwise_add_value_4d_fp16
|
|
71
|
+
|
|
72
|
+
return cython_fast_pointwise_add_value_4d if fast else cython_pointwise_add_value_4d
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _choose_cython_fill_(ndim: int, fast: bool) -> Callable:
|
|
76
|
+
assert ndim <= 4, ndim
|
|
77
|
+
|
|
78
|
+
if ndim <= 3:
|
|
79
|
+
return cython_fast_fill_3d if fast else cython_fill_3d
|
|
80
|
+
|
|
81
|
+
return cython_fast_fill_4d if fast else cython_fill_4d
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _choose_cython_copy(ndim: int, is_fp16: bool, fast: bool) -> Callable:
|
|
85
|
+
assert ndim <= 4, ndim
|
|
86
|
+
|
|
87
|
+
if ndim <= 3:
|
|
88
|
+
if is_fp16:
|
|
89
|
+
return cython_fast_copy_3d_fp16 if fast else cython_copy_3d_fp16
|
|
90
|
+
|
|
91
|
+
return cython_fast_copy_3d if fast else cython_copy_3d
|
|
92
|
+
|
|
93
|
+
if is_fp16:
|
|
94
|
+
return cython_fast_copy_4d_fp16 if fast else cython_copy_4d_fp16
|
|
95
|
+
|
|
96
|
+
return cython_fast_copy_4d if fast else cython_copy_4d
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def pointwise_add(
|
|
100
|
+
nums: np.ndarray,
|
|
101
|
+
summand: Union[np.array, int, float],
|
|
102
|
+
output: np.ndarray = None,
|
|
103
|
+
num_threads: int = _NUMERIC_DEFAULT_NUM_THREADS,
|
|
104
|
+
backend: BackendLike = None,
|
|
105
|
+
) -> np.ndarray:
|
|
106
|
+
"""
|
|
107
|
+
Perform pointwise addition between array and array or scalar.
|
|
108
|
+
|
|
109
|
+
Uses a fast parallelizable implementation for fp16-32-64 and int16-32-64 inputs and ndim <= 4.
|
|
110
|
+
|
|
111
|
+
Parameters
|
|
112
|
+
----------
|
|
113
|
+
nums: np.ndarray
|
|
114
|
+
n-dimensional array
|
|
115
|
+
summand: np.ndarray | int | float
|
|
116
|
+
array of the same shape or scalar
|
|
117
|
+
output: np.ndarray
|
|
118
|
+
array of the same shape as input, into which the output is placed. By default, a new
|
|
119
|
+
array is created
|
|
120
|
+
num_threads: int
|
|
121
|
+
the number of threads to use for computation. Default = 4. If negative value passed
|
|
122
|
+
cpu count + num_threads + 1 threads will be used
|
|
123
|
+
backend: BackendLike
|
|
124
|
+
which backend to use. `cython` and `scipy` are available, `cython` is used by default
|
|
125
|
+
|
|
126
|
+
Returns
|
|
127
|
+
-------
|
|
128
|
+
sum: np.ndarray
|
|
129
|
+
result of summation
|
|
130
|
+
|
|
131
|
+
Examples
|
|
132
|
+
--------
|
|
133
|
+
>>> sum = pointwise_add(x, 1, x) # inplace addition
|
|
134
|
+
>>> sum = pointwise_add(x, 1, backend='Scipy') # just `np.add`
|
|
135
|
+
>>> sum = pointwise_add(x.astype('float32'), x.astype('float16')) # will fail because of different dtypes
|
|
136
|
+
"""
|
|
137
|
+
backend = resolve_backend(backend, warn_stacklevel=3)
|
|
138
|
+
if backend.name not in ('Scipy', 'Cython'):
|
|
139
|
+
raise ValueError(f'Unsupported backend "{backend.name}".')
|
|
140
|
+
|
|
141
|
+
dtype = nums.dtype
|
|
142
|
+
|
|
143
|
+
if dtype not in _TYPES:
|
|
144
|
+
raise ValueError(f'Input array dtype must be one of {", ".join(_STR_TYPES)}, got {dtype}.')
|
|
145
|
+
|
|
146
|
+
if output is None:
|
|
147
|
+
output = np.empty_like(nums, dtype=dtype)
|
|
148
|
+
elif output.shape != nums.shape:
|
|
149
|
+
raise ValueError(f'Input array and output array shapes must be the same, got {nums.shape} vs {output.shape}.')
|
|
150
|
+
elif dtype != output.dtype:
|
|
151
|
+
raise ValueError(f'Input array and output array dtypes must be the same, got {dtype} vs {output.dtype}.')
|
|
152
|
+
|
|
153
|
+
summand_is_array = isinstance(summand, np.ndarray)
|
|
154
|
+
if summand_is_array:
|
|
155
|
+
if dtype != summand.dtype:
|
|
156
|
+
raise ValueError(f'Input and summand arrays must have same dtypes, got {dtype} vs {summand.dtype}.')
|
|
157
|
+
elif not isinstance(summand, (*_TYPES, *(int, float))):
|
|
158
|
+
raise ValueError(f'Summand dtype must be one of {", ".join(_STR_TYPES)}, got {type(summand)}.')
|
|
159
|
+
else:
|
|
160
|
+
summand = dtype.type(summand)
|
|
161
|
+
|
|
162
|
+
ndim = nums.ndim
|
|
163
|
+
num_threads = normalize_num_threads(num_threads, backend, warn_stacklevel=3)
|
|
164
|
+
|
|
165
|
+
if backend.name == 'Scipy' or ndim > 4:
|
|
166
|
+
np.add(nums, summand, out=output)
|
|
167
|
+
return output
|
|
168
|
+
|
|
169
|
+
is_fp16 = dtype == np.float16
|
|
170
|
+
src_pointwise_add = _choose_cython_pointwise_add(ndim, summand_is_array, is_fp16, backend.fast)
|
|
171
|
+
|
|
172
|
+
n_dummy = 3 - ndim if ndim <= 3 else 0
|
|
173
|
+
|
|
174
|
+
if n_dummy:
|
|
175
|
+
nums = nums[(None,) * n_dummy]
|
|
176
|
+
output = output[(None,) * n_dummy]
|
|
177
|
+
if summand_is_array:
|
|
178
|
+
summand = summand[(None,) * n_dummy]
|
|
179
|
+
|
|
180
|
+
if is_fp16:
|
|
181
|
+
output = src_pointwise_add(
|
|
182
|
+
nums.view(np.uint16), summand.view(np.uint16), output.view(np.uint16), num_threads
|
|
183
|
+
).view(np.float16)
|
|
184
|
+
else:
|
|
185
|
+
output = src_pointwise_add(nums, summand, output, num_threads)
|
|
186
|
+
|
|
187
|
+
if n_dummy:
|
|
188
|
+
output = output[(0,) * n_dummy]
|
|
189
|
+
|
|
190
|
+
return output
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def fill_(
|
|
194
|
+
nums: np.ndarray,
|
|
195
|
+
value: Union[np.number, int, float],
|
|
196
|
+
num_threads: int = _NUMERIC_DEFAULT_NUM_THREADS,
|
|
197
|
+
backend: BackendLike = None,
|
|
198
|
+
) -> None:
|
|
199
|
+
"""
|
|
200
|
+
Fill the array with a scalar value.
|
|
201
|
+
|
|
202
|
+
Uses a fast parallelizable implementation for fp16-32-64 and int16-32-64 inputs and ndim <= 4.
|
|
203
|
+
|
|
204
|
+
Parameters
|
|
205
|
+
----------
|
|
206
|
+
nums: np.ndarray
|
|
207
|
+
n-dimensional array
|
|
208
|
+
value: np.number | int | float
|
|
209
|
+
scalar
|
|
210
|
+
num_threads: int
|
|
211
|
+
the number of threads to use for computation. Default = 4. If negative value passed
|
|
212
|
+
cpu count + num_threads + 1 threads will be used
|
|
213
|
+
backend: BackendLike
|
|
214
|
+
which backend to use. `cython` and `scipy` are available, `cython` is used by default
|
|
215
|
+
|
|
216
|
+
Examples
|
|
217
|
+
--------
|
|
218
|
+
>>> fill_(x, 1)
|
|
219
|
+
>>> fill_(np.empty((2, 3, 4)), 42)
|
|
220
|
+
>>> fill_(x.astype('uint16'), 3) # will fail because of unsupported uint16 dtype
|
|
221
|
+
"""
|
|
222
|
+
backend = resolve_backend(backend, warn_stacklevel=3)
|
|
223
|
+
if backend.name not in ('Scipy', 'Cython'):
|
|
224
|
+
raise ValueError(f'Unsupported backend "{backend.name}".')
|
|
225
|
+
|
|
226
|
+
ndim = nums.ndim
|
|
227
|
+
dtype = nums.dtype
|
|
228
|
+
|
|
229
|
+
if dtype not in _TYPES or backend.name == 'Scipy' or ndim > 4:
|
|
230
|
+
nums.fill(value)
|
|
231
|
+
return
|
|
232
|
+
|
|
233
|
+
is_fp16 = dtype == np.float16
|
|
234
|
+
num_threads = normalize_num_threads(num_threads, backend, warn_stacklevel=3)
|
|
235
|
+
src_fill_ = _choose_cython_fill_(ndim, backend.fast)
|
|
236
|
+
value = dtype.type(value)
|
|
237
|
+
|
|
238
|
+
n_dummy = 3 - ndim if ndim <= 3 else 0
|
|
239
|
+
|
|
240
|
+
if n_dummy:
|
|
241
|
+
nums = nums[(None,) * n_dummy]
|
|
242
|
+
|
|
243
|
+
if is_fp16:
|
|
244
|
+
src_fill_(nums.view(np.uint16), value.view(np.uint16), num_threads)
|
|
245
|
+
else:
|
|
246
|
+
src_fill_(nums, value, num_threads)
|
|
247
|
+
|
|
248
|
+
if n_dummy:
|
|
249
|
+
nums = nums[(0,) * n_dummy]
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def full(
|
|
253
|
+
shape: Union[int, Sequence[int]],
|
|
254
|
+
fill_value: Union[np.number, int, float],
|
|
255
|
+
dtype: Union[type, str] = None,
|
|
256
|
+
order: str = 'C',
|
|
257
|
+
num_threads: int = _NUMERIC_DEFAULT_NUM_THREADS,
|
|
258
|
+
backend: BackendLike = None,
|
|
259
|
+
) -> np.ndarray:
|
|
260
|
+
"""
|
|
261
|
+
Return a new array of given shape and type, filled with `fill_value`.
|
|
262
|
+
|
|
263
|
+
Uses a fast parallelizable implementation for fp16-32-64 and int16-32-64 inputs and ndim <= 4.
|
|
264
|
+
|
|
265
|
+
Parameters
|
|
266
|
+
----------
|
|
267
|
+
shape: int | Sequence[int]
|
|
268
|
+
desired shape
|
|
269
|
+
fill_value: np.number | int | float
|
|
270
|
+
scalar to fill array with
|
|
271
|
+
dtype: type | str
|
|
272
|
+
desired dtype to which `fill_value` will be casted. If not specified, `np.array(fill_value).dtype` will be used
|
|
273
|
+
order: str
|
|
274
|
+
whether to store multidimensional data in C or F contiguous order in memory
|
|
275
|
+
num_threads: int
|
|
276
|
+
the number of threads to use for computation. Default = 4. If negative value passed
|
|
277
|
+
cpu count + num_threads + 1 threads will be used
|
|
278
|
+
backend: BackendLike
|
|
279
|
+
which backend to use. `cython` and `scipy` are available, `cython` is used by default
|
|
280
|
+
|
|
281
|
+
Examples
|
|
282
|
+
--------
|
|
283
|
+
>>> x = full((2, 3, 4), 1.0) # same as `np.ones((2, 3, 4))`
|
|
284
|
+
>>> x = full((2, 3, 4), 1.5, dtype=int) # same as np.ones((2, 3, 4), dtype=int)
|
|
285
|
+
>>> x = full((2, 3, 4), 1, dtype='uint16') # will fail because of unsupported uint16 dtype
|
|
286
|
+
"""
|
|
287
|
+
nums = np.empty(shape, dtype=dtype, order=order)
|
|
288
|
+
|
|
289
|
+
if dtype is not None:
|
|
290
|
+
fill_value = nums.dtype.type(fill_value)
|
|
291
|
+
|
|
292
|
+
fill_(nums, fill_value, num_threads, backend)
|
|
293
|
+
|
|
294
|
+
return nums
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def copy(
|
|
298
|
+
nums: np.ndarray,
|
|
299
|
+
output: np.ndarray = None,
|
|
300
|
+
order: str = 'K',
|
|
301
|
+
num_threads: int = _NUMERIC_DEFAULT_NUM_THREADS,
|
|
302
|
+
backend: BackendLike = None,
|
|
303
|
+
) -> np.ndarray:
|
|
304
|
+
"""
|
|
305
|
+
Return copy of the given array.
|
|
306
|
+
|
|
307
|
+
Uses a fast parallelizable implementation for fp16-32-64 and int16-32-64 inputs and ndim <= 4.
|
|
308
|
+
|
|
309
|
+
Parameters
|
|
310
|
+
----------
|
|
311
|
+
nums: np.ndarray
|
|
312
|
+
n-dimensional array
|
|
313
|
+
output: np.ndarray
|
|
314
|
+
array of the same shape and dtype as input, into which the copy is placed. By default, a new
|
|
315
|
+
array is created
|
|
316
|
+
order: str
|
|
317
|
+
controls the memory layout of the copy. `C` means C-order, `F` means F-order, `A` means `F` if a is Fortran
|
|
318
|
+
contiguous, `C` otherwise. `K` means match the layout of a as closely as possible
|
|
319
|
+
num_threads: int
|
|
320
|
+
the number of threads to use for computation. Default = 4. If negative value passed
|
|
321
|
+
cpu count + num_threads + 1 threads will be used
|
|
322
|
+
backend: BackendLike
|
|
323
|
+
which backend to use. `cython` and `scipy` are available, `cython` is used by default
|
|
324
|
+
|
|
325
|
+
Returns
|
|
326
|
+
-------
|
|
327
|
+
copy: np.ndarray
|
|
328
|
+
copy of array
|
|
329
|
+
|
|
330
|
+
Examples
|
|
331
|
+
--------
|
|
332
|
+
>>> copied = copy(x)
|
|
333
|
+
>>> copied = copy(x, backend='Scipy') # same as `np.copy`
|
|
334
|
+
>>> copy(x, output=y) # copied into `y`
|
|
335
|
+
"""
|
|
336
|
+
backend = resolve_backend(backend, warn_stacklevel=3)
|
|
337
|
+
if backend.name not in ('Scipy', 'Cython'):
|
|
338
|
+
raise ValueError(f'Unsupported backend "{backend.name}".')
|
|
339
|
+
|
|
340
|
+
ndim = nums.ndim
|
|
341
|
+
dtype = nums.dtype
|
|
342
|
+
num_threads = normalize_num_threads(num_threads, backend, warn_stacklevel=3)
|
|
343
|
+
|
|
344
|
+
if output is None:
|
|
345
|
+
output = np.empty_like(nums, dtype=dtype, order=order)
|
|
346
|
+
elif output.shape != nums.shape:
|
|
347
|
+
raise ValueError(f'Input array and output array shapes must be the same, got {nums.shape} vs {output.shape}.')
|
|
348
|
+
elif dtype != output.dtype:
|
|
349
|
+
raise ValueError(f'Input array and output array dtypes must be the same, got {dtype} vs {output.dtype}.')
|
|
350
|
+
|
|
351
|
+
if dtype not in _TYPES or backend.name == 'Scipy' or ndim > 4:
|
|
352
|
+
output = np.copy(nums, order=order)
|
|
353
|
+
return output
|
|
354
|
+
|
|
355
|
+
is_fp16 = dtype == np.float16
|
|
356
|
+
src_copy = _choose_cython_copy(ndim, is_fp16, backend.fast)
|
|
357
|
+
|
|
358
|
+
n_dummy = 3 - ndim if ndim <= 3 else 0
|
|
359
|
+
|
|
360
|
+
if n_dummy:
|
|
361
|
+
nums = nums[(None,) * n_dummy]
|
|
362
|
+
output = output[(None,) * n_dummy]
|
|
363
|
+
|
|
364
|
+
if is_fp16:
|
|
365
|
+
src_copy(nums.view(np.uint16), output.view(np.uint16), num_threads)
|
|
366
|
+
else:
|
|
367
|
+
src_copy(nums, output, num_threads)
|
|
368
|
+
|
|
369
|
+
if n_dummy:
|
|
370
|
+
nums = nums[(0,) * n_dummy]
|
|
371
|
+
output = output[(0,) * n_dummy]
|
|
372
|
+
|
|
373
|
+
return output
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
# TODO: add parallel astype?
|