Skip to content

Commit 3fb04d2

Browse files
Merge pull request #427 from IntelPython/feature/tensor-usm-array
Feature/tensor usm array
2 parents 83b7359 + bc15b77 commit 3fb04d2

File tree

11 files changed

+1256
-14
lines changed

11 files changed

+1256
-14
lines changed

.flake8

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ per-file-ignores =
2222
dpctl/_sycl_queue_manager.pyx: E999, E225
2323
dpctl/memory/_memory.pyx: E999, E225, E226, E227
2424
dpctl/program/_program.pyx: E999, E225, E226, E227
25+
dpctl/tensor/_usmarray.pyx: E999, E225, E226, E227
2526
dpctl/tensor/numpy_usm_shared.py: F821
2627
examples/cython/sycl_buffer/_buffer_example.pyx: E999, E225, E402
2728
examples/cython/sycl_direct_linkage/_buffer_example.pyx: E999, E225, E402

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,4 @@ dpctl/_sycl_event.h
9696
dpctl/_sycl_queue.h
9797
dpctl/_sycl_queue_manager.h
9898
dpctl/memory/_memory.h
99+
dpctl/tensor/_usmarray.h

dpctl/tensor/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,9 @@
2828
is allocated with a USM shared memory allocator.
2929
3030
"""
31+
32+
from dpctl.tensor._usmarray import usm_ndarray
33+
34+
__all__ = [
35+
"usm_ndarray",
36+
]

dpctl/tensor/_slicing.pxi

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Data Parallel Control (dpctl)
2+
#
3+
# Copyright 2020-2021 Intel Corporation
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
import numbers
18+
19+
20+
cdef object _basic_slice_meta(object ind, tuple shape,
21+
tuple strides, Py_ssize_t offset):
22+
"""
23+
24+
"""
25+
if ind is Ellipsis:
26+
return (shape, strides, offset)
27+
elif ind is None:
28+
return ((1,) + shape, (0,) + strides, offset)
29+
elif isinstance(ind, slice):
30+
sl_start, sl_stop, sl_step = ind.indices(shape[0])
31+
sh0 = (sl_stop - sl_start) // sl_step
32+
str0 = sl_step * strides[0]
33+
new_strides = strides if (sl_step == 1) else (str0,) + strides[1:]
34+
return (
35+
(sh0, ) + shape[1:],
36+
new_strides,
37+
offset + sl_start * strides[0]
38+
)
39+
elif isinstance(ind, numbers.Integral):
40+
if 0 <= ind < shape[0]:
41+
return (shape[1:], strides[1:], offset + ind * strides[0])
42+
elif -shape[0] <= ind < 0:
43+
return (shape[1:], strides[1:],
44+
offset + (shape[0] + ind) * strides[0])
45+
else:
46+
raise IndexError(
47+
"Index {0} is out of range for axes 0 with "
48+
"size {1}".format(ind, shape[0]))
49+
elif isinstance(ind, list):
50+
raise NotImplemented
51+
elif isinstance(ind, tuple):
52+
axes_referenced = 0
53+
ellipses_count = 0
54+
newaxis_count = 0
55+
explicit_index = 0
56+
for i in ind:
57+
if i is None:
58+
newaxis_count = newaxis_count + 1
59+
elif i is Ellipsis:
60+
ellipses_count = ellipses_count + 1
61+
elif isinstance(i, slice):
62+
axes_referenced = axes_referenced + 1
63+
elif isinstance(i, numbers.Integral):
64+
explicit_index = explicit_index + 1
65+
axes_referenced = axes_referenced + 1
66+
elif isinstance(i, list):
67+
raise NotImplemented
68+
else:
69+
raise TypeError
70+
if ellipses_count > 1:
71+
raise IndexError(
72+
"an index can only have a sinlge ellipsis ('...')")
73+
if axes_referenced > len(shape):
74+
raise IndexError(
75+
"too many indices for an array, array is "
76+
"{0}-dimensional, but {1} were indexed".format(
77+
len(shape), axes_referenced))
78+
if ellipses_count:
79+
ellipses_count = len(shape) - axes_referenced
80+
new_shape_len = (newaxis_count + ellipses_count
81+
+ axes_referenced - explicit_index)
82+
new_shape = list()
83+
new_strides = list()
84+
k = 0
85+
new_offset = offset
86+
for i in range(len(ind)):
87+
ind_i = ind[i]
88+
if (ind_i is Ellipsis):
89+
k_new = k + ellipses_count
90+
new_shape.extend(shape[k:k_new])
91+
new_strides.extend(strides[k:k_new])
92+
k = k_new
93+
elif ind_i is None:
94+
new_shape.append(1)
95+
new_strides.append(0)
96+
elif isinstance(ind_i, slice):
97+
k_new = k + 1
98+
sl_start, sl_stop, sl_step = ind_i.indices(shape[k])
99+
sh_i = (sl_stop - sl_start) // sl_step
100+
str_i = sl_step * strides[k]
101+
new_shape.append(sh_i)
102+
new_strides.append(str_i)
103+
new_offset = new_offset + sl_start * strides[k]
104+
k = k_new
105+
elif isinstance(ind_i, numbers.Integral):
106+
if 0 <= ind_i < shape[k]:
107+
k_new = k + 1
108+
new_offset = new_offset + ind_i * strides[k]
109+
k = k_new
110+
elif -shape[k] <= ind_i < 0:
111+
k_new = k + 1
112+
new_offset = new_offset + (shape[k] + ind_i) * strides[k]
113+
k = k_new
114+
else:
115+
raise IndexError(
116+
"Index {0} is out of range for "
117+
"axes {1} with size {2}".format(ind_i, k, shape[k]))
118+
new_shape.extend(shape[k:])
119+
new_strides.extend(strides[k:])
120+
return (tuple(new_shape), tuple(new_strides), new_offset)
121+
else:
122+
raise TypeError

dpctl/tensor/_stride_utils.pxi

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
# Data Parallel Control (dpctl)
2+
#
3+
# Copyright 2020-2021 Intel Corporation
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# distutils: language = c++
18+
# cython: language_level=3
19+
20+
from cpython.mem cimport PyMem_Malloc
21+
from cpython.ref cimport Py_INCREF
22+
from cpython.tuple cimport PyTuple_New, PyTuple_SetItem
23+
24+
25+
cdef int ERROR_MALLOC = 1
26+
cdef int ERROR_INTERNAL = -1
27+
cdef int ERROR_INCORRECT_ORDER = 2
28+
cdef int ERROR_UNEXPECTED_STRIDES = 3
29+
30+
cdef int USM_ARRAY_C_CONTIGUOUS = 1
31+
cdef int USM_ARRAY_F_CONTIGUOUS = 2
32+
cdef int USM_ARRAY_WRITEABLE = 4
33+
34+
35+
cdef Py_ssize_t shape_to_elem_count(int nd, Py_ssize_t *shape_arr):
36+
"""
37+
Computes number of elements in an array.
38+
"""
39+
cdef Py_ssize_t count = 1
40+
for i in range(nd):
41+
count *= shape_arr[i]
42+
return count
43+
44+
45+
cdef int _from_input_shape_strides(
46+
int nd, object shape, object strides, int itemsize, char order,
47+
Py_ssize_t **shape_ptr, Py_ssize_t **strides_ptr,
48+
Py_ssize_t *nelems, Py_ssize_t *min_disp, Py_ssize_t *max_disp,
49+
int *contig):
50+
"""
51+
Arguments: nd, shape, strides, itemsize, order
52+
Modifies:
53+
shape_ptr - pointer to C array for shape values
54+
stride_ptr - pointer to C array for strides values
55+
nelems - Number of elements in array
56+
min_disp = min( dot(strides, index), index for shape)
57+
max_disp = max( dor(strides, index), index for shape)
58+
contig = enumation for array contiguity
59+
Returns: 0 on success, error code otherwise.
60+
On success pointers point to allocated arrays,
61+
Otherwise they are set to NULL
62+
"""
63+
cdef int i
64+
cdef int all_incr = 1
65+
cdef int all_decr = 1
66+
cdef Py_ssize_t elem_count = 1
67+
cdef Py_ssize_t min_shift = 0
68+
cdef Py_ssize_t max_shift = 0
69+
cdef Py_ssize_t str_i
70+
cdef Py_ssize_t* shape_arr
71+
cdef Py_ssize_t* strides_arr
72+
73+
# 0-d array
74+
if (nd == 0):
75+
contig[0] = USM_ARRAY_C_CONTIGUOUS
76+
nelems[0] = 1
77+
min_disp[0] = 0
78+
max_disp[0] = 0
79+
shape_ptr[0] = <Py_ssize_t *>(<size_t>0)
80+
strides_ptr[0] = <Py_ssize_t *>(<size_t>0)
81+
return 0
82+
83+
shape_arr = <Py_ssize_t*>PyMem_Malloc(nd * sizeof(Py_ssize_t))
84+
if (not shape_arr):
85+
return ERROR_MALLOC
86+
shape_ptr[0] = shape_arr
87+
for i in range(0, nd):
88+
shape_arr[i] = <Py_ssize_t> shape[i]
89+
elem_count *= shape_arr[i]
90+
if elem_count == 0:
91+
contig[0] = USM_ARRAY_C_CONTIGUOUS
92+
nelems[0] = 1
93+
min_disp[0] = 0
94+
max_disp[0] = 0
95+
strides_ptr[0] = <Py_ssize_t *>(<size_t>0)
96+
return 0
97+
nelems[0] = elem_count
98+
99+
if (strides is None):
100+
# no need to allocate and populate strides
101+
if (int(order) not in [ord('C'), ord('F'), ord('c'), ord('f')]):
102+
return ERROR_INCORRECT_ORDER
103+
if order == <char> ord('C') or order == <char> ord('c'):
104+
contig[0] = USM_ARRAY_C_CONTIGUOUS
105+
else:
106+
contig[0] = USM_ARRAY_F_CONTIGUOUS
107+
min_disp[0] = 0
108+
max_disp[0] = (elem_count - 1)
109+
strides_ptr[0] = <Py_ssize_t *>(<size_t>0)
110+
return 0
111+
elif ((isinstance(strides, (list, tuple)) or hasattr(strides, 'tolist'))
112+
and len(strides) == nd):
113+
strides_arr = <Py_ssize_t*>PyMem_Malloc(nd * sizeof(Py_ssize_t))
114+
if (not strides_arr):
115+
return ERROR_MALLOC
116+
strides_ptr[0] = strides_arr
117+
for i in range(0, nd):
118+
str_i = <Py_ssize_t> strides[i]
119+
strides_arr[i] = str_i
120+
if str_i > 0:
121+
max_shift += strides_arr[i] * (shape_arr[i] - 1)
122+
else:
123+
min_shift += strides_arr[i] * (shape_arr[i] - 1)
124+
min_disp[0] = min_shift
125+
max_disp[0] = max_shift
126+
if max_shift == min_shift + (elem_count - 1):
127+
if nd == 1:
128+
contig[0] = USM_ARRAY_C_CONTIGUOUS
129+
return 0
130+
for i in range(0, nd - 1):
131+
if all_incr:
132+
all_incr = strides_arr[i] < strides_arr[i + 1]
133+
if all_decr:
134+
all_decr = strides_arr[i] > strides_arr[i + 1]
135+
if all_incr:
136+
contig[0] = USM_ARRAY_C_CONTIGUOUS
137+
elif all_decr:
138+
contig[0] = USM_ARRAY_F_CONTIGUOUS
139+
else:
140+
contig[0] = 0
141+
return 0
142+
else:
143+
contig[0] = 0 # non-contiguous
144+
return 0
145+
else:
146+
return ERROR_UNEXPECTED_STRIDES
147+
# return ERROR_INTERNAL
148+
149+
150+
cdef object _make_int_tuple(int nd, Py_ssize_t *ary):
151+
"""
152+
Makes Python tuple from C array
153+
"""
154+
cdef tuple res
155+
cdef object tmp
156+
if (ary):
157+
res = PyTuple_New(nd)
158+
for i in range(nd):
159+
tmp = <object>ary[i]
160+
Py_INCREF(tmp) # SetItem steals the reference
161+
PyTuple_SetItem(res, i, tmp)
162+
return res
163+
else:
164+
return None
165+
166+
167+
cdef object _make_reversed_int_tuple(int nd, Py_ssize_t *ary):
168+
"""
169+
Makes Python reversed tuple from C array
170+
"""
171+
cdef tuple res
172+
cdef object tmp
173+
cdef int i
174+
cdef int nd_1
175+
if (ary):
176+
res = PyTuple_New(nd)
177+
nd_1 = nd - 1
178+
for i in range(nd):
179+
tmp = <object>ary[i]
180+
Py_INCREF(tmp) # SetItem steals the reference
181+
PyTuple_SetItem(res, nd_1 - i, tmp)
182+
return res
183+
else:
184+
return None
185+
186+
187+
cdef object _c_contig_strides(int nd, Py_ssize_t *shape):
188+
"""
189+
Makes Python tuple for C-contiguous array
190+
"""
191+
cdef tuple cc_strides = PyTuple_New(nd)
192+
cdef object si = 1
193+
cdef int i
194+
cdef int nd_1 = nd - 1
195+
for i in range(0, nd):
196+
Py_INCREF(si) # SetItem steals the reference
197+
PyTuple_SetItem(cc_strides, nd_1 - i, si)
198+
si = si * shape[nd_1 - i]
199+
return cc_strides
200+
201+
202+
cdef object _f_contig_strides(int nd, Py_ssize_t *shape):
203+
"""
204+
Makes Python t
205+
"""
206+
cdef tuple fc_strides = PyTuple_New(nd)
207+
cdef object si = 1
208+
for i in range(0, nd):
209+
Py_INCREF(si) # SetItem steals the reference
210+
PyTuple_SetItem(fc_strides, i, si)
211+
si = si * shape[i]
212+
return fc_strides

0 commit comments

Comments
 (0)