Skip to content

Commit a308784

Browse files
author
Diptorup Deb
authored
Merge pull request #955 from IntelPython/new_features_dpnpndarray
Extends the DpnpNdArray type.
2 parents 547a28b + 501c1e7 commit a308784

File tree

1 file changed

+182
-4
lines changed

1 file changed

+182
-4
lines changed

numba_dpex/core/types/dpnp_ndarray_type.py

Lines changed: 182 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
#
33
# SPDX-License-Identifier: Apache-2.0
44

5-
from numba.core import cgutils
5+
6+
import dpnp
7+
from numba.core import cgutils, ir, types
68
from numba.core.errors import NumbaNotImplementedError
9+
from numba.core.ir_utils import get_np_ufunc_typ, mk_unique_var
710
from numba.core.pythonapi import NativeValue, PythonAPI, box, unbox
811
from numba.np import numpy_support
912

@@ -21,8 +24,174 @@ class DpnpNdArray(USMNdArray):
2124

2225
@property
2326
def is_internal(self):
27+
"""Sets the property so that DpnpNdArray expressions can be converted
28+
to Numba array_expression objects.
29+
30+
Returns:
31+
bool: Always returns True.
32+
"""
2433
return True
2534

35+
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
36+
"""Used to derive the output of array operations involving DpnpNdArray
37+
type objects.
38+
39+
Note that the __array_ufunc__ returns the LHS value of an expression
40+
involving DpnpNdArray. However, the resulting type is not complete and
41+
is default initialized. As such, the LHS type does not meet compute
42+
follows data and usm_type propagation rules.
43+
44+
The ParforLegalizeCFDPass fixes the typing of the LHS array expression.
45+
46+
Returns: The DpnpNdArray class.
47+
"""
48+
if method == "__call__":
49+
if not all(
50+
(
51+
isinstance(inp, DpnpNdArray)
52+
or isinstance(inp, types.abstract.Number)
53+
)
54+
for inp in inputs
55+
):
56+
return NotImplemented
57+
return DpnpNdArray
58+
else:
59+
return
60+
61+
def __allocate__(
62+
self,
63+
typingctx,
64+
typemap,
65+
calltypes,
66+
lhs,
67+
size_var,
68+
dtype,
69+
scope,
70+
loc,
71+
lhs_typ,
72+
size_typ,
73+
out,
74+
):
75+
"""Generates the Numba typed IR representing the allocation of a new
76+
DpnpNdArray using the dpnp.ndarray overload.
77+
78+
Args:
79+
typingctx: The typing context used when generating the IR.
80+
typemap: The current IR state's typemap
81+
calltypes: The calltype of the dpnp.empty function
82+
lhs: The LHS IR value for the result of __allocate__
83+
size_var: The size of the allocation
84+
dtype: The dtype of the array to be allocated.
85+
scope: The scope in which the allocated array is alive
86+
loc: Location in the source
87+
lhs_typ: The dtype of the LHS value of the allocate call.
88+
size_typ: The dtype of the size Value
89+
out: The output array.
90+
91+
Raises:
92+
NotImplementedError: Thrown if the allocation is for a F-contiguous
93+
array
94+
95+
Returns: The IR Value for the allocated array
96+
"""
97+
g_np_var = ir.Var(scope, mk_unique_var("$np_g_var"), loc)
98+
if typemap:
99+
typemap[g_np_var.name] = types.misc.Module(dpnp)
100+
g_np = ir.Global("np", dpnp, loc)
101+
g_np_assign = ir.Assign(g_np, g_np_var, loc)
102+
# attr call: empty_attr = getattr(g_np_var, empty)
103+
empty_attr_call = ir.Expr.getattr(g_np_var, "empty", loc)
104+
attr_var = ir.Var(scope, mk_unique_var("$empty_attr_attr"), loc)
105+
if typemap:
106+
typemap[attr_var.name] = get_np_ufunc_typ(dpnp.empty)
107+
attr_assign = ir.Assign(empty_attr_call, attr_var, loc)
108+
# Assume str(dtype) returns a valid type
109+
dtype_str = str(dtype)
110+
# alloc call: lhs = empty_attr(size_var, typ_var)
111+
typ_var = ir.Var(scope, mk_unique_var("$np_typ_var"), loc)
112+
if typemap:
113+
typemap[typ_var.name] = types.functions.NumberClass(dtype)
114+
# If dtype is a datetime/timedelta with a unit,
115+
# then it won't return a valid type and instead can be created
116+
# with a string. i.e. "datetime64[ns]")
117+
if (
118+
isinstance(dtype, (types.NPDatetime, types.NPTimedelta))
119+
and dtype.unit != ""
120+
):
121+
typename_const = ir.Const(dtype_str, loc)
122+
typ_var_assign = ir.Assign(typename_const, typ_var, loc)
123+
else:
124+
if dtype_str == "bool":
125+
# empty doesn't like 'bool' sometimes (e.g. kmeans example)
126+
dtype_str = "bool_"
127+
np_typ_getattr = ir.Expr.getattr(g_np_var, dtype_str, loc)
128+
typ_var_assign = ir.Assign(np_typ_getattr, typ_var, loc)
129+
alloc_call = ir.Expr.call(attr_var, [size_var, typ_var], (), loc)
130+
131+
if calltypes:
132+
cac = typemap[attr_var.name].get_call_type(
133+
typingctx, [size_typ, types.functions.NumberClass(dtype)], {}
134+
)
135+
# By default, all calls to "empty" are typed as returning a standard
136+
# NumPy ndarray. If we are allocating a ndarray subclass here then
137+
# just change the return type to be that of the subclass.
138+
cac._return_type = (
139+
lhs_typ.copy(layout="C") if lhs_typ.layout == "F" else lhs_typ
140+
)
141+
calltypes[alloc_call] = cac
142+
143+
if lhs_typ.layout == "F":
144+
# The F contiguous layout needs to be properly tested before we
145+
# enable this code path.
146+
raise NotImplementedError("F Arrays are not yet supported")
147+
148+
empty_c_typ = lhs_typ.copy(layout="C")
149+
empty_c_var = ir.Var(scope, mk_unique_var("$empty_c_var"), loc)
150+
if typemap:
151+
typemap[empty_c_var.name] = lhs_typ.copy(layout="C")
152+
empty_c_assign = ir.Assign(alloc_call, empty_c_var, loc)
153+
154+
# attr call: asfortranarray = getattr(g_np_var, asfortranarray)
155+
asfortranarray_attr_call = ir.Expr.getattr(
156+
g_np_var, "asfortranarray", loc
157+
)
158+
afa_attr_var = ir.Var(
159+
scope, mk_unique_var("$asfortran_array_attr"), loc
160+
)
161+
if typemap:
162+
typemap[afa_attr_var.name] = get_np_ufunc_typ(
163+
dpnp.asfortranarray
164+
)
165+
afa_attr_assign = ir.Assign(
166+
asfortranarray_attr_call, afa_attr_var, loc
167+
)
168+
# call asfortranarray
169+
asfortranarray_call = ir.Expr.call(
170+
afa_attr_var, [empty_c_var], (), loc
171+
)
172+
if calltypes:
173+
calltypes[asfortranarray_call] = typemap[
174+
afa_attr_var.name
175+
].get_call_type(typingctx, [empty_c_typ], {})
176+
177+
asfortranarray_assign = ir.Assign(asfortranarray_call, lhs, loc)
178+
179+
out.extend(
180+
[
181+
g_np_assign,
182+
attr_assign,
183+
typ_var_assign,
184+
empty_c_assign,
185+
afa_attr_assign,
186+
asfortranarray_assign,
187+
]
188+
)
189+
else:
190+
alloc_assign = ir.Assign(alloc_call, lhs, loc)
191+
out.extend([g_np_assign, attr_assign, typ_var_assign, alloc_assign])
192+
193+
return out
194+
26195

27196
# --------------- Boxing/Unboxing logic for dpnp.ndarray ----------------------#
28197

@@ -34,10 +203,9 @@ def unbox_dpnp_nd_array(typ, obj, c):
34203
Args:
35204
typ : The Numba type of the PyObject
36205
obj : The actual PyObject to be unboxed
37-
c :
206+
c : The unboxing context
38207
39-
Returns:
40-
_type_: _description_
208+
Returns: A NativeValue object representing an unboxed dpnp.ndarray
41209
"""
42210
# Reusing the numba.core.base.BaseContext's make_array function to get a
43211
# struct allocated. The same struct is used for numpy.ndarray
@@ -98,6 +266,16 @@ def unbox_dpnp_nd_array(typ, obj, c):
98266

99267
@box(DpnpNdArray)
100268
def box_array(typ, val, c):
269+
"""Boxes a NativeValue representation of DpnpNdArray type into a
270+
dpnp.ndarray PyObject
271+
272+
Args:
273+
typ: The representation of the DpnpNdArray type.
274+
val: A native representation of a Numba DpnpNdArray type object.
275+
c: The boxing context.
276+
277+
Returns: A Pyobject for a dpnp.ndarray boxed from the Numba native value.
278+
"""
101279
if c.context.enable_nrt:
102280
np_dtype = numpy_support.as_dtype(typ.dtype)
103281
dtypeptr = c.env_manager.read_const(c.env_manager.add_const(np_dtype))

0 commit comments

Comments
 (0)