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 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.2'
1
+ __version__ = '0.8.3'
imops/backend.py CHANGED
@@ -1,14 +1,15 @@
1
- import contextlib
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 AVAILABLE_BACKENDS:
10
+ if name in _AVAILABLE_BACKENDS:
10
11
  raise ValueError(f'The name "{name}" is already in use.')
11
- AVAILABLE_BACKENDS[name] = cls
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
- AVAILABLE_BACKENDS: Dict[str, Type[Backend]] = {}
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 AVAILABLE_BACKENDS:
34
- raise ValueError(f'"{value}" is not in the list of available backends: {tuple(AVAILABLE_BACKENDS)}.')
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 AVAILABLE_BACKENDS[value]()
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
- @contextlib.contextmanager
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
- BACKEND2NUM_THREADS_VAR_NAME = {Cython.__name__: 'OMP_NUM_THREADS', Numba.__name__: 'NUMBA_NUM_THREADS'}
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(x: np.ndarray, box: np.ndarray, axis: AxesLike = None, padding_values: AxesParams = None) -> np.ndarray:
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
- self.x = np.copy(x) if copy else x
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 = np.copy(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
- skimage2cc3d = {
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=skimage2cc3d[(ndim, 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?