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
68from numba .core .errors import NumbaNotImplementedError
9+ from numba .core .ir_utils import get_np_ufunc_typ , mk_unique_var
710from numba .core .pythonapi import NativeValue , PythonAPI , box , unbox
811from 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 )
100268def 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