已合并
卷积算子golden和input能力支持 #8206
huafeng793创建于 20 天前
卷积算子golden和input能力支持 #8206
已合并
huafeng793创建于 20 天前
10 个文件变更+1241-528
Mconv/conv2d_v2/tests/assets/golden.py+82-47
@@ -29,10 +29,7 @@ Supported dtypes: float16, float32, bfloat16, hifloat8, float8_e4m3fn, int8, int
29import numpy as np29import numpy as np
30 30 
31__golden__ = {31__golden__ = {
32- "kernel": {32+ "kernel": {"conv2dv2": "conv2d_v2_golden", "conv2d_v2": "conv2d_v2_golden"}
33- "conv2dv2": "conv2d_v2_golden",
34- "conv2d_v2": "conv2d_v2_golden"
35- }
36}33}
37 34 
38FP32_STR = "float32"35FP32_STR = "float32"
@@ -92,10 +89,14 @@ def convert_output_dtype(out, output_dtype, enable_hf32=False):
92 if isinstance(dtype_ref, str):89 if isinstance(dtype_ref, str):
93 module_name, dtype_name = dtype_ref.split(".")90 module_name, dtype_name = dtype_ref.split(".")
94 try:91 try:
95- dtype_cls = getattr(__import__(module_name, fromlist=[dtype_name]), dtype_name)92+ dtype_cls = getattr(
93+ __import__(module_name, fromlist=[dtype_name]), dtype_name
94+ )
96 except (ImportError, AttributeError):95 except (ImportError, AttributeError):
97- raise RuntimeError(f"{module_name} is required for {output_dtype}. "96+ raise RuntimeError(
98- f"Install: pip install {module_name}")97+ f"{module_name} is required for {output_dtype}. "
98+ f"Install: pip install {module_name}"
99+ )
99 out = out.astype(dtype_cls)100 out = out.astype(dtype_cls)
100 else:101 else:
101 out = out.astype(dtype_ref)102 out = out.astype(dtype_ref)
@@ -111,13 +112,13 @@ def process_formats_a5(x, filter, input_formats):
111 - filter supports: NCHW (when x is NCHW) or HWCN (when x is NHWC)112 - filter supports: NCHW (when x is NCHW) or HWCN (when x is NHWC)
112 """113 """
113 input_data_format, input_filter_format = input_formats[0], input_formats[1]114 input_data_format, input_filter_format = input_formats[0], input_formats[1]
114- 115+ 
115 if input_data_format == "NHWC":116 if input_data_format == "NHWC":
116 x = x.transpose(0, 3, 1, 2)117 x = x.transpose(0, 3, 1, 2)
117- 118+ 
118 if input_filter_format == "HWCN":119 if input_filter_format == "HWCN":
119 filter = filter.transpose(3, 2, 0, 1)120 filter = filter.transpose(3, 2, 0, 1)
120- 121+ 
121 return x, filter122 return x, filter
122 123 
123 124 
@@ -129,14 +130,16 @@ def process_output_format_a5(out, output_format):
129 """130 """
130 if output_format == "NHWC":131 if output_format == "NHWC":
131 out = out.transpose((0, 2, 3, 1))132 out = out.transpose((0, 2, 3, 1))
132- 133+ 
133 return out134 return out
134 135 
135 136 
136-def get_ori_pad_from_pad_mode(x_np, filter_np, pads, pad_mode, stride_h, stride_w, dilation_h, dilation_w):137+def get_ori_pad_from_pad_mode(
138+ x_np, filter_np, pads, pad_mode, stride_h, stride_w, dilation_h, dilation_w
139+):
137 """140 """
138 Calculate original padding values based on pad_mode, corresponding to C++ GetOriPadFromPadMode.141 Calculate original padding values based on pad_mode, corresponding to C++ GetOriPadFromPadMode.
139- 142+ 
140 Args:143 Args:
141 x_np: Input feature map numpy array144 x_np: Input feature map numpy array
142 filter_np: Weight filter numpy array145 filter_np: Weight filter numpy array
@@ -146,7 +149,7 @@ def get_ori_pad_from_pad_mode(x_np, filter_np, pads, pad_mode, stride_h, stride_
146 stride_w: Stride in W dimension149 stride_w: Stride in W dimension
147 dilation_h: Dilation in H dimension150 dilation_h: Dilation in H dimension
148 dilation_w: Dilation in W dimension151 dilation_w: Dilation in W dimension
149- 152+ 
150 Returns:153 Returns:
151 Tuple (pad_top, pad_bottom, pad_left, pad_right)154 Tuple (pad_top, pad_bottom, pad_left, pad_right)
152 """155 """
@@ -161,9 +164,19 @@ def get_ori_pad_from_pad_mode(x_np, filter_np, pads, pad_mode, stride_h, stride_
161 if pad_mode_upper == "SPECIFIC":164 if pad_mode_upper == "SPECIFIC":
162 if isinstance(pads, (list, tuple)):165 if isinstance(pads, (list, tuple)):
163 if len(pads) == 4:166 if len(pads) == 4:
164- pad_top, pad_bottom, pad_left, pad_right = int(pads[0]), int(pads[1]), int(pads[2]), int(pads[3])167+ pad_top, pad_bottom, pad_left, pad_right = (
168+ int(pads[0]),
169+ int(pads[1]),
170+ int(pads[2]),
171+ int(pads[3]),
172+ )
165 elif len(pads) == 2:173 elif len(pads) == 2:
166- pad_top, pad_bottom, pad_left, pad_right = int(pads[0]), int(pads[0]), int(pads[1]), int(pads[1])174+ pad_top, pad_bottom, pad_left, pad_right = (
175+ int(pads[0]),
176+ int(pads[0]),
177+ int(pads[1]),
178+ int(pads[1]),
179+ )
167 else:180 else:
168 pad_val = int(pads[0])181 pad_val = int(pads[0])
169 pad_top = pad_bottom = pad_left = pad_right = pad_val182 pad_top = pad_bottom = pad_left = pad_right = pad_val
@@ -173,8 +186,18 @@ def get_ori_pad_from_pad_mode(x_np, filter_np, pads, pad_mode, stride_h, stride_
173 elif pad_mode_upper == "VALID":186 elif pad_mode_upper == "VALID":
174 pad_top = pad_bottom = pad_left = pad_right = 0187 pad_top = pad_bottom = pad_left = pad_right = 0
175 else:188 else:
176- pad_h = (conv_ceil_div(in_h, stride_h) - 1) * stride_h + dilation_h * (k_h - 1) - in_h + 1189+ pad_h = (
177- pad_w = (conv_ceil_div(in_w, stride_w) - 1) * stride_w + dilation_w * (k_w - 1) - in_w + 1190+ (conv_ceil_div(in_h, stride_h) - 1) * stride_h
191+ + dilation_h * (k_h - 1)
192+ - in_h
193+ + 1
194+ )
195+ pad_w = (
196+ (conv_ceil_div(in_w, stride_w) - 1) * stride_w
197+ + dilation_w * (k_w - 1)
198+ - in_w
199+ + 1
200+ )
178 if pad_mode_upper == "SAME" or pad_mode_upper == "SAME_UPPER":201 if pad_mode_upper == "SAME" or pad_mode_upper == "SAME_UPPER":
179 if pad_mode_upper == "SAME":202 if pad_mode_upper == "SAME":
180 pad_h = max(0, pad_h)203 pad_h = max(0, pad_h)
@@ -193,18 +216,22 @@ def get_ori_pad_from_pad_mode(x_np, filter_np, pads, pad_mode, stride_h, stride_
193 return pad_top, pad_bottom, pad_left, pad_right216 return pad_top, pad_bottom, pad_left, pad_right
194 217 
195 218 
196-def conv2d_v2_golden(x, filter, bias=None, offset_w=None,219+def conv2d_v2_golden(
197- *,220+ x,
198- strides: list,221+ filter,
199- pads: list = [0, 0, 0, 0],222+ bias=None,
200- dilations: list = [1, 1, 1, 1],223+ offset_w=None,
201- groups: int = 1,224+ *,
202- data_format: str = NCHW_FORMAT,225+ strides: list,
203- offset_x: int = 0,226+ pads: list = [0, 0, 0, 0],
204- pad_mode: str = "SPECIFIC",227+ dilations: list = [1, 1, 1, 1],
205- enable_hf32: bool = False,228+ groups: int = 1,
206- **kwargs,229+ data_format: str = NCHW_FORMAT,
207- ):230+ offset_x: int = 0,
231+ pad_mode: str = "SPECIFIC",
232+ enable_hf32: bool = False,
233+ **kwargs,
234+):
208 """235 """
209 Kernel golden for conv2d_v2.236 Kernel golden for conv2d_v2.
210 All parameters follow @conv2d_v2_def.cpp without outputs.237 All parameters follow @conv2d_v2_def.cpp without outputs.
@@ -215,43 +242,49 @@ def conv2d_v2_golden(x, filter, bias=None, offset_w=None,
215 """242 """
216 import torch243 import torch
217 import torch.nn.functional as F244 import torch.nn.functional as F
218- 245+ 
219 input_formats = kwargs.get("input_formats", [NCHW_FORMAT, NCHW_FORMAT])246 input_formats = kwargs.get("input_formats", [NCHW_FORMAT, NCHW_FORMAT])
220 input_format = input_formats[0]247 input_format = input_formats[0]
221 short_soc_version = kwargs.get("short_soc_version", None)248 short_soc_version = kwargs.get("short_soc_version", None)
222 249 
223- x_dtype_str = x.dtype.name 250+ x_dtype_str = x.dtype.name
224- filter_dtype_str = filter.dtype.name
225-
226 x_np, filter_np = process_formats_a5(x, filter, input_formats)251 x_np, filter_np = process_formats_a5(x, filter, input_formats)
227- 252+ 
228 if enable_hf32 and x_dtype_str == FP32_STR:253 if enable_hf32 and x_dtype_str == FP32_STR:
229 calc_dtype = np.float32254 calc_dtype = np.float32
230 x_np = simulate_hf32_precision(x_np.astype(np.float32), short_soc_version)255 x_np = simulate_hf32_precision(x_np.astype(np.float32), short_soc_version)
231- filter_np = simulate_hf32_precision(filter_np.astype(np.float32), short_soc_version)256+ filter_np = simulate_hf32_precision(
257+ filter_np.astype(np.float32), short_soc_version
258+ )
232 else:259 else:
233- calc_dtype = np.float64 if x_dtype_str == FP32_STR else np.float32260+ calc_dtype = np.float32
234 x_np = x_np.astype(calc_dtype)261 x_np = x_np.astype(calc_dtype)
235 filter_np = filter_np.astype(calc_dtype)262 filter_np = filter_np.astype(calc_dtype)
236- 263+ 
237 if bias is not None:264 if bias is not None:
238 bias_np = bias.astype(calc_dtype)265 bias_np = bias.astype(calc_dtype)
239 else:266 else:
240 bias_np = None267 bias_np = None
241- 268+ 
242 if isinstance(strides, (list, tuple)):269 if isinstance(strides, (list, tuple)):
243 if len(strides) == 4:270 if len(strides) == 4:
244- stride_h, stride_w = int(strides[2]), int(strides[3])271+ if input_format == "NHWC":
272+ stride_h, stride_w = int(strides[1]), int(strides[2])
273+ else:
274+ stride_h, stride_w = int(strides[2]), int(strides[3])
245 elif len(strides) == 2:275 elif len(strides) == 2:
246 stride_h, stride_w = int(strides[0]), int(strides[1])276 stride_h, stride_w = int(strides[0]), int(strides[1])
247 else:277 else:
248 stride_h = stride_w = int(strides[0])278 stride_h = stride_w = int(strides[0])
249 else:279 else:
250 stride_h = stride_w = int(strides)280 stride_h = stride_w = int(strides)
251- 281+ 
252 if isinstance(dilations, (list, tuple)):282 if isinstance(dilations, (list, tuple)):
253 if len(dilations) == 4:283 if len(dilations) == 4:
254- dilation_h, dilation_w = int(dilations[2]), int(dilations[3])284+ if input_format == "NHWC":
285+ dilation_h, dilation_w = int(dilations[1]), int(dilations[2])
286+ else:
287+ dilation_h, dilation_w = int(dilations[2]), int(dilations[3])
255 elif len(dilations) == 2:288 elif len(dilations) == 2:
256 dilation_h, dilation_w = int(dilations[0]), int(dilations[1])289 dilation_h, dilation_w = int(dilations[0]), int(dilations[1])
257 else:290 else:
@@ -259,16 +292,18 @@ def conv2d_v2_golden(x, filter, bias=None, offset_w=None,
259 else:292 else:
260 dilation_h = dilation_w = int(dilations)293 dilation_h = dilation_w = int(dilations)
261 294 
262- pad_top, pad_bottom, pad_left, pad_right = get_ori_pad_from_pad_mode(x_np, filter_np, pads, pad_mode, stride_h, stride_w, dilation_h, dilation_w)295+ pad_top, pad_bottom, pad_left, pad_right = get_ori_pad_from_pad_mode(
296+ x_np, filter_np, pads, pad_mode, stride_h, stride_w, dilation_h, dilation_w
297+ )
263 input_torch = torch.from_numpy(x_np)298 input_torch = torch.from_numpy(x_np)
264 weight_torch = torch.from_numpy(filter_np)299 weight_torch = torch.from_numpy(filter_np)
265 bias_torch = torch.from_numpy(bias_np) if bias_np is not None else None300 bias_torch = torch.from_numpy(bias_np) if bias_np is not None else None
266- 301+ 
267 torch_pad = (pad_left, pad_right, pad_top, pad_bottom)302 torch_pad = (pad_left, pad_right, pad_top, pad_bottom)
268 if any(pad > 0 for pad in (pad_top, pad_bottom, pad_left, pad_right)):303 if any(pad > 0 for pad in (pad_top, pad_bottom, pad_left, pad_right)):
269 pad_value = float(offset_x) if offset_x != 0 else 0.0304 pad_value = float(offset_x) if offset_x != 0 else 0.0
270 input_torch = F.pad(input_torch, torch_pad, "constant", pad_value)305 input_torch = F.pad(input_torch, torch_pad, "constant", pad_value)
271- 306+ 
272 out = torch.nn.functional.conv2d(307 out = torch.nn.functional.conv2d(
273 input_torch,308 input_torch,
274 weight_torch,309 weight_torch,
@@ -285,7 +320,7 @@ def conv2d_v2_golden(x, filter, bias=None, offset_w=None,
285 output_format = output_formats[0]320 output_format = output_formats[0]
286 321 
287 out = convert_output_dtype(out, output_dtype, enable_hf32)322 out = convert_output_dtype(out, output_dtype, enable_hf32)
288- 323+ 
289 out = process_output_format_a5(out, output_format)324 out = process_output_format_a5(out, output_format)
290- 325+ 
291 return out326 return out
Mconv/conv3d_v2/op_kernel/arch35/conv3d_v2_tilingkey.h+17-6
@@ -48,7 +48,18 @@ ASCENDC_TPL_ARGS_DECL(
48#define CONV3D_KERNEL_TYPE_SEL ASCENDC_TPL_KERNEL_TYPE_SEL(ASCENDC_TPL_MIX_AIC_1_2)48#define CONV3D_KERNEL_TYPE_SEL ASCENDC_TPL_KERNEL_TYPE_SEL(ASCENDC_TPL_MIX_AIC_1_2)
49#define CONV3D_OPT_GROUP_SEL()49#define CONV3D_OPT_GROUP_SEL()
50#define CONV3D_ORI_GROUP_SEL()50#define CONV3D_ORI_GROUP_SEL()
51- 51+#define CONV3D_BIG_KERNEL_SEL() \
52+ ASCENDC_TPL_KERNEL_TYPE_SEL(ASCENDC_TPL_MIX_AIC_1_2), \
53+ ASCENDC_TPL_UINT_SEL(FmapTiling, ASCENDC_TPL_UI_LIST, CONV_FMAP_TILING_OTHER), \
54+ ASCENDC_TPL_UINT_SEL(WeightTiling, ASCENDC_TPL_UI_LIST, CONV_WEIGHT_TILING_OTHER), \
55+ ASCENDC_TPL_UINT_SEL(L1PingPong, ASCENDC_TPL_UI_LIST, CONV_L1_PINGPONG_ALL_OPEN), \
56+ ASCENDC_TPL_UINT_SEL(L0PingPong, ASCENDC_TPL_UI_LIST, CONV_L0_PINGPONG_ALL_CLOSE, CONV_L0_PINGPONG_AL0_OPEN, \
57+ CONV_L0_PINGPONG_BL0_OPEN, CONV_L0_PINGPONG_ALL_OPEN), \
58+ ASCENDC_TPL_UINT_SEL(OutputOrder, ASCENDC_TPL_UI_LIST, CONV_OUTPUT_ORDER_HW_MODE), \
59+ ASCENDC_TPL_UINT_SEL(IterOrder, ASCENDC_TPL_UI_LIST, CONV_ITER_ORDER_MITER_FIRST, \
60+ CONV_ITER_ORDER_NITER_FIRST), \
61+ ASCENDC_TPL_UINT_SEL(GroupType, ASCENDC_TPL_UI_LIST, CONV_GROUP_TYPE_NORMAL_CONV), \
62+ ASCENDC_TPL_UINT_SEL(BigKernel, ASCENDC_TPL_UI_LIST, CONV_BIG_KERNEL)
52#else63#else
53#define CONV3D_KERNEL_TYPE_SEL ASCENDC_TPL_KERNEL_TYPE_SEL(ASCENDC_TPL_AIC_ONLY)64#define CONV3D_KERNEL_TYPE_SEL ASCENDC_TPL_KERNEL_TYPE_SEL(ASCENDC_TPL_AIC_ONLY)
54#define CONV3D_OPT_GROUP_SEL() \65#define CONV3D_OPT_GROUP_SEL() \
@@ -59,10 +70,9 @@ ASCENDC_TPL_ARGS_DECL(
59 ASCENDC_TPL_KERNEL_TYPE_SEL(ASCENDC_TPL_AIC_ONLY), CONV_COMMON_ORI_GROUP_SEL(), \70 ASCENDC_TPL_KERNEL_TYPE_SEL(ASCENDC_TPL_AIC_ONLY), CONV_COMMON_ORI_GROUP_SEL(), \
60 ASCENDC_TPL_UINT_SEL(BigKernel, ASCENDC_TPL_UI_LIST, CONV_NORMAL_KERNEL)71 ASCENDC_TPL_UINT_SEL(BigKernel, ASCENDC_TPL_UI_LIST, CONV_NORMAL_KERNEL)
61 72 
62-#endif
63- 
64#define CONV3D_BIG_KERNEL_SEL() \73#define CONV3D_BIG_KERNEL_SEL() \
65- ASCENDC_TPL_UINT_SEL(FmapTiling, ASCENDC_TPL_UI_LIST, CONV_FMAP_TILING_OTHER), \74+ ASCENDC_TPL_KERNEL_TYPE_SEL(ASCENDC_TPL_MIX_AIC_1_2), \
75+ ASCENDC_TPL_UINT_SEL(FmapTiling, ASCENDC_TPL_UI_LIST, CONV_FMAP_TILING_OTHER), \
66 ASCENDC_TPL_UINT_SEL(WeightTiling, ASCENDC_TPL_UI_LIST, CONV_WEIGHT_TILING_OTHER), \76 ASCENDC_TPL_UINT_SEL(WeightTiling, ASCENDC_TPL_UI_LIST, CONV_WEIGHT_TILING_OTHER), \
67 ASCENDC_TPL_UINT_SEL(L1PingPong, ASCENDC_TPL_UI_LIST, CONV_L1_PINGPONG_ALL_OPEN), \77 ASCENDC_TPL_UINT_SEL(L1PingPong, ASCENDC_TPL_UI_LIST, CONV_L1_PINGPONG_ALL_OPEN), \
68 ASCENDC_TPL_UINT_SEL(L0PingPong, ASCENDC_TPL_UI_LIST, CONV_L0_PINGPONG_ALL_CLOSE, CONV_L0_PINGPONG_AL0_OPEN, \78 ASCENDC_TPL_UINT_SEL(L0PingPong, ASCENDC_TPL_UI_LIST, CONV_L0_PINGPONG_ALL_CLOSE, CONV_L0_PINGPONG_AL0_OPEN, \
@@ -73,6 +83,7 @@ ASCENDC_TPL_ARGS_DECL(
73 ASCENDC_TPL_UINT_SEL(GroupType, ASCENDC_TPL_UI_LIST, CONV_GROUP_TYPE_NORMAL_CONV, \83 ASCENDC_TPL_UINT_SEL(GroupType, ASCENDC_TPL_UI_LIST, CONV_GROUP_TYPE_NORMAL_CONV, \
74 CONV_GROUP_TYPE_ORI_GROUP_CONV), \84 CONV_GROUP_TYPE_ORI_GROUP_CONV), \
75 ASCENDC_TPL_UINT_SEL(BigKernel, ASCENDC_TPL_UI_LIST, CONV_BIG_KERNEL)85 ASCENDC_TPL_UINT_SEL(BigKernel, ASCENDC_TPL_UI_LIST, CONV_BIG_KERNEL)
86+#endif
76 87 
77#define CONV3D_COMMON_ONLY_MN_FULLLOAD_SEL() \88#define CONV3D_COMMON_ONLY_MN_FULLLOAD_SEL() \
78 CONV_COMMON_ONLY_MN_FULLLOAD_SEL(), ASCENDC_TPL_UINT_SEL(BigKernel, ASCENDC_TPL_UI_LIST, CONV_NORMAL_KERNEL)89 CONV_COMMON_ONLY_MN_FULLLOAD_SEL(), ASCENDC_TPL_UINT_SEL(BigKernel, ASCENDC_TPL_UI_LIST, CONV_NORMAL_KERNEL)
@@ -107,8 +118,8 @@ ASCENDC_TPL_SEL(ASCENDC_TPL_ARGS_SEL(CONV3D_KERNEL_TYPE_SEL, CONV3D_COMMON_ONLY_
107 ASCENDC_TPL_ARGS_SEL(CONV3D_KERNEL_TYPE_SEL, CONV3D_COMMON_ABL1_FULLLOAD_M_FIRST_SEL()),118 ASCENDC_TPL_ARGS_SEL(CONV3D_KERNEL_TYPE_SEL, CONV3D_COMMON_ABL1_FULLLOAD_M_FIRST_SEL()),
108 ASCENDC_TPL_ARGS_SEL(CONV3D_KERNEL_TYPE_SEL, CONV3D_COMMON_ABL1_FULLLOAD_N_FIRST_SEL()),119 ASCENDC_TPL_ARGS_SEL(CONV3D_KERNEL_TYPE_SEL, CONV3D_COMMON_ABL1_FULLLOAD_N_FIRST_SEL()),
109 ASCENDC_TPL_ARGS_SEL(CONV3D_OPT_GROUP_SEL()), ASCENDC_TPL_ARGS_SEL(CONV3D_ORI_GROUP_SEL()),120 ASCENDC_TPL_ARGS_SEL(CONV3D_OPT_GROUP_SEL()), ASCENDC_TPL_ARGS_SEL(CONV3D_ORI_GROUP_SEL()),
110- ASCENDC_TPL_ARGS_SEL(CONV3D_KERNEL_TYPE_SEL, CONV3D_BIG_KERNEL_SEL()));121+ ASCENDC_TPL_ARGS_SEL(CONV3D_BIG_KERNEL_SEL()));
111 122 
112} // namespace Conv3DV2Key123} // namespace Conv3DV2Key
113 124 
114-#endif // CONV3D_V2_TILINGKEY_H125+#endif // CONV3D_V2_TILINGKEY_H
Mconv/conv3d_v2/tests/assets/golden.py+183-94
@@ -37,10 +37,7 @@ import numpy as np
37# Set to False to use PyTorch implementation37# Set to False to use PyTorch implementation
38 38 
39__golden__ = {39__golden__ = {
40- "kernel": {40+ "kernel": {"conv3dv2": "conv3d_v2_golden", "conv3d_v2": "conv3d_v2_golden"}
41- "conv3dv2": "conv3d_v2_golden",
42- "conv3d_v2": "conv3d_v2_golden"
43- }
44}41}
45 42 
46 43 
@@ -96,10 +93,14 @@ def convert_output_dtype(out, output_dtype, enable_hf32=False, short_soc_version
96 if isinstance(dtype_ref, str):93 if isinstance(dtype_ref, str):
97 module_name, dtype_name = dtype_ref.split(".")94 module_name, dtype_name = dtype_ref.split(".")
98 try:95 try:
99- dtype_cls = getattr(__import__(module_name, fromlist=[dtype_name]), dtype_name)96+ dtype_cls = getattr(
97+ __import__(module_name, fromlist=[dtype_name]), dtype_name
98+ )
100 except (ImportError, AttributeError):99 except (ImportError, AttributeError):
101- raise RuntimeError(f"{module_name} is required for {output_dtype}. "100+ raise RuntimeError(
102- f"Install: pip install {module_name}")101+ f"{module_name} is required for {output_dtype}. "
102+ f"Install: pip install {module_name}"
103+ )
103 out = out.astype(dtype_cls)104 out = out.astype(dtype_cls)
104 else:105 else:
105 out = out.astype(dtype_ref)106 out = out.astype(dtype_ref)
@@ -120,6 +121,7 @@ def align(a, b):
120 121 
121def _lcm(a, b):122def _lcm(a, b):
122 import math123 import math
124+ 
123 return abs(a * b) // math.gcd(a, b) if a and b else 0125 return abs(a * b) // math.gcd(a, b) if a and b else 0
124 126 
125 127 
@@ -132,6 +134,7 @@ def determine_c0(dtype, target_shape):
132 "float4_e2m1",134 "float4_e2m1",
133 "float4_e1m2",135 "float4_e1m2",
134 "hifloat8",136 "hifloat8",
137+ "int8",
135 ]:138 ]:
136 return 32139 return 32
137 else:140 else:
@@ -176,7 +179,7 @@ def to_NDC1HWC0(data, ori_format, target_shape):
176 179 
177def is_ascend950(short_soc_version):180def is_ascend950(short_soc_version):
178 """Check if the target is Ascend 950PR/950DT"""181 """Check if the target is Ascend 950PR/950DT"""
179- return (short_soc_version == "Ascend950")182+ return short_soc_version == "Ascend950"
180 183 
181 184 
182def process_formats_a2_a3(x, filter, input_formats, input_ori_shapes, groups):185def process_formats_a2_a3(x, filter, input_formats, input_ori_shapes, groups):
@@ -189,34 +192,37 @@ def process_formats_a2_a3(x, filter, input_formats, input_ori_shapes, groups):
189 - DHWCN format is not supported192 - DHWCN format is not supported
190 """193 """
191 input_data_format, input_filter_format = input_formats[0], input_formats[1]194 input_data_format, input_filter_format = input_formats[0], input_formats[1]
192- 195+ 
193 if input_data_format == "NDC1HWC0":196 if input_data_format == "NDC1HWC0":
194 if input_ori_shapes is not None:197 if input_ori_shapes is not None:
195 x = to_NCDHW_from_NDC1HWC0(x, input_ori_shapes[0])198 x = to_NCDHW_from_NDC1HWC0(x, input_ori_shapes[0])
196- 199+ 
197 if input_filter_format == "FRACTAL_Z_3D":200 if input_filter_format == "FRACTAL_Z_3D":
198 if input_ori_shapes is not None:201 if input_ori_shapes is not None:
199 filter = to_NCDHW_from_FRACTAL_Z_3D(filter, input_ori_shapes[1], groups)202 filter = to_NCDHW_from_FRACTAL_Z_3D(filter, input_ori_shapes[1], groups)
200- 203+ 
201 return x, filter204 return x, filter
202 205 
203 206 
204-def process_formats_a5(x, filter, input_formats):207+def process_formats_a5(x, filter, input_formats, input_ori_shapes=None, groups=1):
205 """208 """
206 Process format conversion for Ascend 950PR/950DT (A5).209 Process format conversion for Ascend 950PR/950DT (A5).
207 210 
208 Constraints:211 Constraints:
209 - x supports: NCDHW, NDHWC212 - x supports: NCDHW, NDHWC
210- - filter supports: NCDHW, DHWCN213+ - filter supports: NCDHW, DHWCN, FRACTAL_Z_3D
211 """214 """
212 input_data_format, input_filter_format = input_formats[0], input_formats[1]215 input_data_format, input_filter_format = input_formats[0], input_formats[1]
213- 216+ 
214 if input_data_format == "NDHWC":217 if input_data_format == "NDHWC":
215 x = x.transpose(0, 4, 1, 2, 3)218 x = x.transpose(0, 4, 1, 2, 3)
216- 219+ 
217 if input_filter_format == "DHWCN":220 if input_filter_format == "DHWCN":
218 filter = filter.transpose(4, 3, 0, 1, 2)221 filter = filter.transpose(4, 3, 0, 1, 2)
219- 222+ elif input_filter_format == "FRACTAL_Z_3D":
223+ if input_ori_shapes is not None and input_ori_shapes[1] is not None:
224+ filter = to_NCDHW_from_FRACTAL_Z_3D(filter, input_ori_shapes[1], groups)
225+ 
220 return x, filter226 return x, filter
221 227 
222 228 
@@ -233,7 +239,7 @@ def process_output_format_a2_a3(out, output_format, input_format, output_ori_sha
233 out = to_NDC1HWC0_from_NCDHW(out, target_shape)239 out = to_NDC1HWC0_from_NCDHW(out, target_shape)
234 elif output_format == NCDHW_FORMAT and input_format == NDHWC_FORMAT:240 elif output_format == NCDHW_FORMAT and input_format == NDHWC_FORMAT:
235 out = out.transpose((0, 4, 1, 2, 3))241 out = out.transpose((0, 4, 1, 2, 3))
236- 242+ 
237 return out243 return out
238 244 
239 245 
@@ -248,7 +254,7 @@ def process_output_format_a5(out, output_format, input_format):
248 out = out.transpose((0, 2, 3, 4, 1))254 out = out.transpose((0, 2, 3, 4, 1))
249 elif output_format == NCDHW_FORMAT and input_format == NDHWC_FORMAT:255 elif output_format == NCDHW_FORMAT and input_format == NDHWC_FORMAT:
250 out = out.transpose((0, 4, 1, 2, 3))256 out = out.transpose((0, 4, 1, 2, 3))
251- 257+ 
252 return out258 return out
253 259 
254 260 
@@ -257,7 +263,7 @@ def to_NCDHW_from_NDC1HWC0(data, ori_shape):
257 n, d, h, w, c = ori_shape263 n, d, h, w, c = ori_shape
258 c0 = determine_c0(data.dtype.name, None)264 c0 = determine_c0(data.dtype.name, None)
259 c1 = ceil_div(c, c0)265 c1 = ceil_div(c, c0)
260- 266+ 
261 # NDC1HWC0 shape: (n, d, c1, h, w, c0)267 # NDC1HWC0 shape: (n, d, c1, h, w, c0)
262 # Transpose to (n, c1, d, h, w, c0)268 # Transpose to (n, c1, d, h, w, c0)
263 data = data.transpose(0, 2, 1, 3, 4, 5)269 data = data.transpose(0, 2, 1, 3, 4, 5)
@@ -266,30 +272,31 @@ def to_NCDHW_from_NDC1HWC0(data, ori_shape):
266 # Slice to (n, c, d, h, w) if there's padding272 # Slice to (n, c, d, h, w) if there's padding
267 if c1 * c0 > c:273 if c1 * c0 > c:
268 data = data[:, :c, :, :, :]274 data = data[:, :c, :, :, :]
269- 275+ 
270 return data276 return data
271 277 
272 278 
273def to_NCDHW_from_FRACTAL_Z_3D(data, ori_shape, groups=1):279def to_NCDHW_from_FRACTAL_Z_3D(data, ori_shape, groups=1):
274 """Convert from FRACTAL_Z_3D to NCDHW format"""280 """Convert from FRACTAL_Z_3D to NCDHW format"""
275 n, c_in, d, h, w = ori_shape281 n, c_in, d, h, w = ori_shape
276- c0 = 16282+ c0 = determine_c0(data.dtype.name, None)
283+ n0 = 16
277 cin_ori = c_in // groups284 cin_ori = c_in // groups
278 cout_ori = n // groups285 cout_ori = n // groups
279- 286+ 
280 mag_factor0 = _lcm(cin_ori, c0) // cin_ori287 mag_factor0 = _lcm(cin_ori, c0) // cin_ori
281- mag_factor1 = _lcm(cout_ori, c0) // cout_ori288+ mag_factor1 = _lcm(cout_ori, n0) // cout_ori
282 mag_factor = min(_lcm(mag_factor0, mag_factor1), groups)289 mag_factor = min(_lcm(mag_factor0, mag_factor1), groups)
283- 290+ 
284 cin_g = align(mag_factor * cin_ori, c0)291 cin_g = align(mag_factor * cin_ori, c0)
285- cout_g = align(mag_factor * cout_ori, c0)292+ cout_g = align(mag_factor * cout_ori, n0)
286 real_g = ceil_div(groups, mag_factor)293 real_g = ceil_div(groups, mag_factor)
287 cin1_g = cin_g // c0294 cin1_g = cin_g // c0
288- cout1_g = cout_g // c0295+ cout1_g = cout_g // n0
289- 296+ 
290- # FRACTAL_Z_3D shape: (real_g * d * cin1_g * h * w, cout1_g, c0, c0)297+ # FRACTAL_Z_3D shape: (real_g * d * cin1_g * h * w, cout1_g, n0, c0)
291- data = data.reshape((real_g, d, cin1_g, h, w, cout_g, c0))298+ data = data.reshape((real_g, d, cin1_g, h, w, cout1_g, n0, c0))
292- 299+ 
293 # Reverse the mapping300 # Reverse the mapping
294 result = np.zeros((n, c_in, d, h, w), dtype=data.dtype)301 result = np.zeros((n, c_in, d, h, w), dtype=data.dtype)
295 for g in range(groups):302 for g in range(groups):
@@ -299,9 +306,17 @@ def to_NCDHW_from_FRACTAL_Z_3D(data, ori_shape, groups=1):
299 dst_cin = e * cin_ori + ci306 dst_cin = e * cin_ori + ci
300 dst_cout = e * cout_ori + co307 dst_cout = e * cout_ori + co
301 src_cout = g * cout_ori + co308 src_cout = g * cout_ori + co
302- result[src_cout, ci, :, :, :] = \309+ result[src_cout, ci, :, :, :] = data[
303- data[g // mag_factor, :, dst_cin // c0, :, :, dst_cout, dst_cin % c0]310+ g // mag_factor,
304- 311+ :,
312+ dst_cin // c0,
313+ :,
314+ :,
315+ dst_cout // n0,
316+ dst_cout % n0,
317+ dst_cin % c0,
318+ ]
319+ 
305 return result320 return result
306 321 
307 322 
@@ -310,16 +325,18 @@ def to_NDC1HWC0_from_NCDHW(data, ori_shape):
310 n, c, d, h, w = ori_shape325 n, c, d, h, w = ori_shape
311 c0 = determine_c0(data.dtype.name, None)326 c0 = determine_c0(data.dtype.name, None)
312 c1 = ceil_div(c, c0)327 c1 = ceil_div(c, c0)
313- 328+ 
314 # NCDHW -> (n, c1, c0, d, h, w) -> (n, d, c1, h, w, c0)329 # NCDHW -> (n, c1, c0, d, h, w) -> (n, d, c1, h, w, c0)
315 if c1 * c0 > c:330 if c1 * c0 > c:
316 num_2_padding_in_c = c1 * c0 - c331 num_2_padding_in_c = c1 * c0 - c
317- zero_padding_array = np.zeros((n, num_2_padding_in_c, d, h, w), dtype=data.dtype)332+ zero_padding_array = np.zeros(
333+ (n, num_2_padding_in_c, d, h, w), dtype=data.dtype
334+ )
318 data = np.concatenate((data, zero_padding_array), axis=1)335 data = np.concatenate((data, zero_padding_array), axis=1)
319- 336+ 
320 data = data.reshape((n, c1, c0, d, h, w))337 data = data.reshape((n, c1, c0, d, h, w))
321 data = data.transpose(0, 3, 1, 4, 5, 2)338 data = data.transpose(0, 3, 1, 4, 5, 2)
322- 339+ 
323 return data340 return data
324 341 
325 342 
@@ -329,19 +346,19 @@ def to_FRACTAL_Z_3D_from_NCDHW(data, ori_shape, groups=1):
329 c0 = 16346 c0 = 16
330 cin_ori = c_in // groups347 cin_ori = c_in // groups
331 cout_ori = n // groups348 cout_ori = n // groups
332- 349+ 
333 mag_factor0 = _lcm(cin_ori, c0) // cin_ori350 mag_factor0 = _lcm(cin_ori, c0) // cin_ori
334 mag_factor1 = _lcm(cout_ori, c0) // cout_ori351 mag_factor1 = _lcm(cout_ori, c0) // cout_ori
335 mag_factor = min(_lcm(mag_factor0, mag_factor1), groups)352 mag_factor = min(_lcm(mag_factor0, mag_factor1), groups)
336- 353+ 
337 cin_g = align(mag_factor * cin_ori, c0)354 cin_g = align(mag_factor * cin_ori, c0)
338 cout_g = align(mag_factor * cout_ori, c0)355 cout_g = align(mag_factor * cout_ori, c0)
339 real_g = ceil_div(groups, mag_factor)356 real_g = ceil_div(groups, mag_factor)
340 cin1_g = cin_g // c0357 cin1_g = cin_g // c0
341 cout1_g = cout_g // c0358 cout1_g = cout_g // c0
342- 359+ 
343 weight_group = np.zeros((real_g, d, cin1_g, h, w, cout_g, c0), dtype=data.dtype)360 weight_group = np.zeros((real_g, d, cin1_g, h, w, cout_g, c0), dtype=data.dtype)
344- 361+ 
345 for g in range(groups):362 for g in range(groups):
346 for ci in range(c_in):363 for ci in range(c_in):
347 for co in range(n // groups):364 for co in range(n // groups):
@@ -349,11 +366,12 @@ def to_FRACTAL_Z_3D_from_NCDHW(data, ori_shape, groups=1):
349 dst_cin = e * cin_ori + ci366 dst_cin = e * cin_ori + ci
350 dst_cout = e * cout_ori + co367 dst_cout = e * cout_ori + co
351 src_cout = g * cout_ori + co368 src_cout = g * cout_ori + co
352- weight_group[g // mag_factor, :, dst_cin // c0, :, :, dst_cout, dst_cin % c0] = \369+ weight_group[
353- data[src_cout, ci, :, :, :]370+ g // mag_factor, :, dst_cin // c0, :, :, dst_cout, dst_cin % c0
354- 371+ ] = data[src_cout, ci, :, :, :]
372+ 
355 weight_group = weight_group.reshape((real_g * d * cin1_g * h * w, cout1_g, c0, c0))373 weight_group = weight_group.reshape((real_g * d * cin1_g * h * w, cout1_g, c0, c0))
356- 374+ 
357 return weight_group375 return weight_group
358 376 
359 377 
@@ -389,8 +407,17 @@ def _parse_padding(pads):
389 return val, val, val, val, val, val407 return val, val, val, val, val, val
390 408 
391 409 
392-def _apply_pad_mode(pad_mode, input_shape, filter_shape, stride_d, stride_h, stride_w,410+def _apply_pad_mode(
393- dilation_d, dilation_h, dilation_w):411+ pad_mode,
412+ input_shape,
413+ filter_shape,
414+ stride_d,
415+ stride_h,
416+ stride_w,
417+ dilation_d,
418+ dilation_h,
419+ dilation_w,
420+):
394 """421 """
395 Calculate padding based on pad_mode, aligned with C++ GetOriPadFromPadMode and ApplySamesPad logic.422 Calculate padding based on pad_mode, aligned with C++ GetOriPadFromPadMode and ApplySamesPad logic.
396 423 
@@ -464,8 +491,18 @@ def _apply_pad_mode(pad_mode, input_shape, filter_shape, stride_d, stride_h, str
464 return pad_d_front, pad_d_back, pad_top, pad_bottom, pad_left, pad_right491 return pad_d_front, pad_d_back, pad_top, pad_bottom, pad_left, pad_right
465 492 
466 493 
467-def _process_conv3d_padding(x_np, pads, pad_mode, filter_shape, stride_d, stride_h, stride_w,494+def _process_conv3d_padding(
468- dilation_d, dilation_h, dilation_w):495+ x_np,
496+ pads,
497+ pad_mode,
498+ filter_shape,
499+ stride_d,
500+ stride_h,
501+ stride_w,
502+ dilation_d,
503+ dilation_h,
504+ dilation_w,
505+):
469 """506 """
470 Process padding for conv3d operation.507 Process padding for conv3d operation.
471 508 
@@ -487,13 +524,24 @@ def _process_conv3d_padding(x_np, pads, pad_mode, filter_shape, stride_d, stride
487 - torch_pad_list: symmetric padding [d, h, w] for torch.conv3d524 - torch_pad_list: symmetric padding [d, h, w] for torch.conv3d
488 """525 """
489 # Parse explicit padding into 6-element format526 # Parse explicit padding into 6-element format
490- pad_d_front, pad_d_back, pad_top, pad_bottom, pad_left, pad_right = _parse_padding(pads)527+ pad_d_front, pad_d_back, pad_top, pad_bottom, pad_left, pad_right = _parse_padding(
528+ pads
529+ )
491 530 
492 # Apply pad_mode if not SPECIFIC531 # Apply pad_mode if not SPECIFIC
493 if pad_mode.upper() != "SPECIFIC":532 if pad_mode.upper() != "SPECIFIC":
494- pad_d_front, pad_d_back, pad_top, pad_bottom, pad_left, pad_right = _apply_pad_mode(533+ pad_d_front, pad_d_back, pad_top, pad_bottom, pad_left, pad_right = (
495- pad_mode, x_np.shape, filter_shape, stride_d, stride_h, stride_w,534+ _apply_pad_mode(
496- dilation_d, dilation_h, dilation_w535+ pad_mode,
536+ x_np.shape,
537+ filter_shape,
538+ stride_d,
539+ stride_h,
540+ stride_w,
541+ dilation_d,
542+ dilation_h,
543+ dilation_w,
544+ )
497 )545 )
498 546 
499 # Calculate symmetric padding (minimum of front/back, top/bottom, left/right)547 # Calculate symmetric padding (minimum of front/back, top/bottom, left/right)
@@ -529,18 +577,24 @@ def _process_conv3d_padding(x_np, pads, pad_mode, filter_shape, stride_d, stride
529 return input_pad, torch_pad577 return input_pad, torch_pad
530 578 
531 579 
532-def conv3d_v2_golden(x, filter, bias=None, scale=None, offset=None, offset_w=None,580+def conv3d_v2_golden(
533- *,581+ x,
534- strides,582+ filter,
535- pads: list=[0, 0, 0, 0, 0, 0],583+ bias=None,
536- dilations: list=[1, 1, 1, 1, 1],584+ scale=None,
537- groups: int=1,585+ offset=None,
538- data_format:str=NCDHW_FORMAT,586+ offset_w=None,
539- offset_x: int= 0,587+ *,
540- pad_mod: str='SPECIFIC',588+ strides,
541- enable_hf32: bool=False,589+ pads: list = [0, 0, 0, 0, 0, 0],
542- **kwargs,590+ dilations: list = [1, 1, 1, 1, 1],
543- ):591+ groups: int = 1,
592+ data_format: str = NCDHW_FORMAT,
593+ offset_x: int = 0,
594+ pad_mod: str = "SPECIFIC",
595+ enable_hf32: bool = False,
596+ **kwargs,
597+):
544 """598 """
545 Kernel golden for conv3d_v2.599 Kernel golden for conv3d_v2.
546 All parameters follow @conv3d_v2_def.cpp without outputs.600 All parameters follow @conv3d_v2_def.cpp without outputs.
@@ -550,41 +604,46 @@ def conv3d_v2_golden(x, filter, bias=None, scale=None, offset=None, offset_w=Non
550 input_dtypes, output_dtypes.604 input_dtypes, output_dtypes.
551 """605 """
552 import torch606 import torch
553- import torch.nn.functional as F607+ 
554-
555 short_soc_version = kwargs.get("short_soc_version", "")608 short_soc_version = kwargs.get("short_soc_version", "")
556 is_950 = is_ascend950(short_soc_version)609 is_950 = is_ascend950(short_soc_version)
557- 610+ 
558 input_formats = kwargs.get("input_formats", [NCDHW_FORMAT, NCDHW_FORMAT])611 input_formats = kwargs.get("input_formats", [NCDHW_FORMAT, NCDHW_FORMAT])
559 input_ori_shapes = kwargs.get("input_ori_shapes", None)612 input_ori_shapes = kwargs.get("input_ori_shapes", None)
560 613 
561- x_dtype_str = x.dtype.name 614+ x_dtype_str = x.dtype.name
562- 615+ 
563 if is_950:616 if is_950:
564- x_np, filter_np = process_formats_a5(x, filter, input_formats)617+ x_np, filter_np = process_formats_a5(
618+ x, filter, input_formats, input_ori_shapes, groups
619+ )
565 else:620 else:
566- x_np, filter_np = process_formats_a2_a3(x, filter, input_formats, input_ori_shapes, groups)621+ x_np, filter_np = process_formats_a2_a3(
567- 622+ x, filter, input_formats, input_ori_shapes, groups
623+ )
624+ 
568 if enable_hf32 and x_dtype_str == FP32_STR:625 if enable_hf32 and x_dtype_str == FP32_STR:
569 calc_dtype = np.float32626 calc_dtype = np.float32
570 x_np = simulate_hf32_precision(x_np.astype(np.float32), short_soc_version)627 x_np = simulate_hf32_precision(x_np.astype(np.float32), short_soc_version)
571- filter_np = simulate_hf32_precision(filter_np.astype(np.float32), short_soc_version)628+ filter_np = simulate_hf32_precision(
629+ filter_np.astype(np.float32), short_soc_version
630+ )
572 else:631 else:
573 calc_dtype = np.float64 if x_dtype_str == FP32_STR else np.float32632 calc_dtype = np.float64 if x_dtype_str == FP32_STR else np.float32
574 x_np = x_np.astype(calc_dtype)633 x_np = x_np.astype(calc_dtype)
575 filter_np = filter_np.astype(calc_dtype)634 filter_np = filter_np.astype(calc_dtype)
576- 635+ 
577 if bias is not None:636 if bias is not None:
578 bias_np = bias.astype(calc_dtype)637 bias_np = bias.astype(calc_dtype)
579 else:638 else:
580 bias_np = None639 bias_np = None
581- 640+ 
582 if scale is not None:641 if scale is not None:
583 scale_arr = scale if isinstance(scale, np.ndarray) else np.array(scale)642 scale_arr = scale if isinstance(scale, np.ndarray) else np.array(scale)
584 scale_np = scale_arr.astype(np.float32)643 scale_np = scale_arr.astype(np.float32)
585 else:644 else:
586 scale_np = None645 scale_np = None
587- 646+ 
588 if isinstance(strides, (list, tuple)):647 if isinstance(strides, (list, tuple)):
589 if len(strides) == 5:648 if len(strides) == 5:
590 stride_d, stride_h, stride_w = strides[2], strides[3], strides[4]649 stride_d, stride_h, stride_w = strides[2], strides[3], strides[4]
@@ -594,17 +653,25 @@ def conv3d_v2_golden(x, filter, bias=None, scale=None, offset=None, offset_w=Non
594 stride_d = stride_h = stride_w = strides[0]653 stride_d = stride_h = stride_w = strides[0]
595 else:654 else:
596 stride_d = stride_h = stride_w = int(strides)655 stride_d = stride_h = stride_w = int(strides)
597- 656+ 
598 if isinstance(dilations, (list, tuple)):657 if isinstance(dilations, (list, tuple)):
599 if len(dilations) == 5:658 if len(dilations) == 5:
600- dilation_d, dilation_h, dilation_w = dilations[2], dilations[3], dilations[4]659+ dilation_d, dilation_h, dilation_w = (
660+ dilations[2],
661+ dilations[3],
662+ dilations[4],
663+ )
601 elif len(dilations) == 3:664 elif len(dilations) == 3:
602- dilation_d, dilation_h, dilation_w = dilations[0], dilations[1], dilations[2]665+ dilation_d, dilation_h, dilation_w = (
666+ dilations[0],
667+ dilations[1],
668+ dilations[2],
669+ )
603 else:670 else:
604 dilation_d = dilation_h = dilation_w = dilations[0]671 dilation_d = dilation_h = dilation_w = dilations[0]
605 else:672 else:
606 dilation_d = dilation_h = dilation_w = int(dilations)673 dilation_d = dilation_h = dilation_w = int(dilations)
607- 674+ 
608 output_dtypes = kwargs.get("output_dtypes", [FP32_STR])675 output_dtypes = kwargs.get("output_dtypes", [FP32_STR])
609 output_dtype = output_dtypes[0]676 output_dtype = output_dtypes[0]
610 output_formats = kwargs.get("output_formats", [NCDHW_FORMAT])677 output_formats = kwargs.get("output_formats", [NCDHW_FORMAT])
@@ -613,17 +680,29 @@ def conv3d_v2_golden(x, filter, bias=None, scale=None, offset=None, offset_w=Non
613 680 
614 # Process padding and prepare tensors for conv3d681 # Process padding and prepare tensors for conv3d
615 input_pad, pad_torch = _process_conv3d_padding(682 input_pad, pad_torch = _process_conv3d_padding(
616- x_np, pads, pad_mod, filter_np.shape, stride_d, stride_h, stride_w,683+ x_np,
617- dilation_d, dilation_h, dilation_w684+ pads,
685+ pad_mod,
686+ filter_np.shape,
687+ stride_d,
688+ stride_h,
689+ stride_w,
690+ dilation_d,
691+ dilation_h,
692+ dilation_w,
618 )693 )
619 694 
620 input_torch = torch.from_numpy(input_pad)695 input_torch = torch.from_numpy(input_pad)
621 weight_torch = torch.from_numpy(filter_np)696 weight_torch = torch.from_numpy(filter_np)
622- bias_torch = torch.from_numpy(bias_np) if bias_np is not None else None697+ 
698+ is_dequant = x_dtype_str == "int8" and scale_np is not None
699+ bias_torch = (
700+ torch.from_numpy(bias_np) if bias_np is not None and not is_dequant else None
701+ )
623 702 
624 stridedhw = [stride_d, stride_h, stride_w]703 stridedhw = [stride_d, stride_h, stride_w]
625 dilationdhw = [dilation_d, dilation_h, dilation_w]704 dilationdhw = [dilation_d, dilation_h, dilation_w]
626- 705+ 
627 out = torch.nn.functional.conv3d(706 out = torch.nn.functional.conv3d(
628 input_torch,707 input_torch,
629 weight_torch,708 weight_torch,
@@ -633,25 +712,35 @@ def conv3d_v2_golden(x, filter, bias=None, scale=None, offset=None, offset_w=Non
633 dilation=dilationdhw,712 dilation=dilationdhw,
634 groups=groups,713 groups=groups,
635 )714 )
636- 715+ 
637 if scale_np is not None:716 if scale_np is not None:
638 if scale_np.ndim == 1:717 if scale_np.ndim == 1:
639- scale_tensor = torch.from_numpy(scale_np.reshape(1, scale_np.shape[0], 1, 1, 1))718+ scale_tensor = torch.from_numpy(
719+ scale_np.reshape(1, scale_np.shape[0], 1, 1, 1)
720+ )
640 elif scale_np.ndim == 5:721 elif scale_np.ndim == 5:
641 scale_tensor = torch.from_numpy(scale_np)722 scale_tensor = torch.from_numpy(scale_np)
642 else:723 else:
643- raise ValueError(f"scale must be 1D or 5D, got {scale_np.ndim}D with shape {scale_np.shape}")724+ raise ValueError(
644- out = torch.multiply(out, scale_tensor).numpy()725+ f"scale must be 1D or 5D, got {scale_np.ndim}D with shape {scale_np.shape}"
726+ )
727+ out = torch.multiply(out, scale_tensor)
728+ if is_dequant and bias_np is not None:
729+ bias_tensor = torch.from_numpy(bias_np.reshape(1, -1, 1, 1, 1))
730+ out = torch.add(out, bias_tensor)
731+ out = out.numpy()
645 else:732 else:
646 out = out.numpy()733 out = out.numpy()
647- 734+ 
648 out = convert_output_dtype(out, output_dtype, enable_hf32, short_soc_version)735 out = convert_output_dtype(out, output_dtype, enable_hf32, short_soc_version)
649- 736+ 
650 output_ori_shapes = kwargs.get("output_ori_shapes", None)737 output_ori_shapes = kwargs.get("output_ori_shapes", None)
651- 738+ 
652 if not is_950:739 if not is_950:
653- out = process_output_format_a2_a3(out, output_format, input_format, output_ori_shapes)740+ out = process_output_format_a2_a3(
741+ out, output_format, input_format, output_ori_shapes
742+ )
654 else:743 else:
655 out = process_output_format_a5(out, output_format, input_format)744 out = process_output_format_a5(out, output_format, input_format)
656- 745+ 
657 return out746 return out
Mconv/conv3d_v2/tests/assets/input.py+42-23
@@ -2,9 +2,9 @@
2# -*- coding: UTF-8 -*-2# -*- coding: UTF-8 -*-
3# ----------------------------------------------------------------------------3# ----------------------------------------------------------------------------
4# Copyright (c) 2025-2026 Huawei Technologies Co., Ltd.4# Copyright (c) 2025-2026 Huawei Technologies Co., Ltd.
5-# This program is free software, you can redistribute it and/or modify it under terms and conditions of5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6# CANN Open Software License Agreement Version 2.0 (the "License").6# CANN Open Software License Agreement Version 2.0 (the "License").
7-# Please refer to License for details. You may not use this file except in compliance with License.7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,8# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.9# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10# See LICENSE in the root of the software repository for the full text of the License.10# See LICENSE in the root of the software repository for the full text of the License.
@@ -12,11 +12,7 @@
12 12 
13import numpy as np13import numpy as np
14 14 
15-__input__ = {15+__input__ = {"kernel": {"conv3dv2": "conv3dv2_input"}}
16- "kernel": {
17- "conv3dv2": "conv3dv2_input"
18- }
19-}
20 16 
21NCDHW_FORMAT = "NCDHW"17NCDHW_FORMAT = "NCDHW"
22 18 
@@ -27,12 +23,13 @@ def fp32_to_hf32(data):
27 HF32: 1 sign bit, 8 exponent bits, 10 mantissa bits (vs 23 for FP32)23 HF32: 1 sign bit, 8 exponent bits, 10 mantissa bits (vs 23 for FP32)
28 """24 """
29 import torch25 import torch
26+ 
30 is_torch = isinstance(data, torch.Tensor)27 is_torch = isinstance(data, torch.Tensor)
31 if is_torch:28 if is_torch:
32 data_np = data.cpu().numpy()29 data_np = data.cpu().numpy()
33 else:30 else:
34 data_np = np.asarray(data)31 data_np = np.asarray(data)
35- 32+ 
36 data_uint32 = data_np.view(np.uint32)33 data_uint32 = data_np.view(np.uint32)
37 sign = (data_uint32 >> 31) & 0x134 sign = (data_uint32 >> 31) & 0x1
38 exponent = (data_uint32 >> 23) & 0xFF35 exponent = (data_uint32 >> 23) & 0xFF
@@ -44,7 +41,7 @@ def fp32_to_hf32(data):
44 41 
45 result = (sign_out << 31) | (exponent_out << 23) | (mantissa_hf32 << 13)42 result = (sign_out << 31) | (exponent_out << 23) | (mantissa_hf32 << 13)
46 result_np = result.view(np.float32)43 result_np = result.view(np.float32)
47- 44+ 
48 if is_torch:45 if is_torch:
49 return torch.from_numpy(result_np)46 return torch.from_numpy(result_np)
50 return result_np47 return result_np
@@ -53,6 +50,7 @@ def fp32_to_hf32(data):
53def numpy_to_torch_tensor(arr):50def numpy_to_torch_tensor(arr):
54 """Convert numpy array to torch tensor"""51 """Convert numpy array to torch tensor"""
55 import torch52 import torch
53+ 
56 return torch.from_numpy(arr)54 return torch.from_numpy(arr)
57 55 
58 56 
@@ -61,18 +59,24 @@ def torch_to_numpy_tensor(tensor):
61 return tensor.cpu().numpy()59 return tensor.cpu().numpy()
62 60 
63 61 
64-def conv3dv2_input(x, filter, bias=None, scale=None, offset=None, offset_w=None,62+def conv3dv2_input(
65- *,63+ x,
66- strides,64+ filter,
67- pads: list=[0,0,0,0,0,0],65+ bias=None,
68- dilations: list=[1,1,1,1,1,1],66+ scale=None,
69- groups: int=1,67+ offset=None,
70- data_format:str=NCDHW_FORMAT,68+ offset_w=None,
71- offset_x: int= 0,69+ *,
72- pad_mod: str='SPECIFIC',70+ strides,
73- enable_hf32: bool=False,71+ pads: list = [0, 0, 0, 0, 0, 0],
74- **kwargs,72+ dilations: list = [1, 1, 1, 1, 1, 1],
75- ):73+ groups: int = 1,
74+ data_format: str = NCDHW_FORMAT,
75+ offset_x: int = 0,
76+ pad_mod: str = "SPECIFIC",
77+ enable_hf32: bool = False,
78+ **kwargs,
79+):
76 """80 """
77 Input function for conv3dv2.81 Input function for conv3dv2.
78 All the parameters (names and order) follow @conv3d_v2_def.cpp without outputs.82 All the parameters (names and order) follow @conv3d_v2_def.cpp without outputs.
@@ -102,8 +106,23 @@ def conv3dv2_input(x, filter, bias=None, scale=None, offset=None, offset_w=None,
102 offset_input = offset106 offset_input = offset
103 offset_w_input = offset_w107 offset_w_input = offset_w
104 108 
109+ if x.dtype == np.int8 and scale is not None:
110+ if bias is not None and bias.dtype != np.float32:
111+ bias_input = bias.astype(np.float32)
112+ if scale is not None and scale.dtype != np.float32:
113+ scale_input = scale.astype(np.float32)
114+ 
105 if x.dtype == np.float32 and enable_hf32:115 if x.dtype == np.float32 and enable_hf32:
106 x_input = torch_to_numpy_tensor(fp32_to_hf32(numpy_to_torch_tensor(x)))116 x_input = torch_to_numpy_tensor(fp32_to_hf32(numpy_to_torch_tensor(x)))
107- filter_input = torch_to_numpy_tensor(fp32_to_hf32(numpy_to_torch_tensor(filter)))117+ filter_input = torch_to_numpy_tensor(
118+ fp32_to_hf32(numpy_to_torch_tensor(filter))
119+ )
108 120 
109- return [x_input, filter_input, bias_input, scale_input, offset_input, offset_w_input]121+ return [
122+ x_input,
123+ filter_input,
124+ bias_input,
125+ scale_input,
126+ offset_input,
127+ offset_w_input,
128+ ]
Mconv/convolution_backward/tests/assets/golden.py+209-44
@@ -2,9 +2,9 @@
2# -*- coding: UTF-8 -*-2# -*- coding: UTF-8 -*-
3# ----------------------------------------------------------------------------3# ----------------------------------------------------------------------------
4# Copyright (c) 2025-2026 Huawei Technologies Co., Ltd.4# Copyright (c) 2025-2026 Huawei Technologies Co., Ltd.
5-# This program is free software, you can redistribute it and/or modify it under terms and conditions of5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6# CANN Open Software License Agreement Version 2.0 (the "License").6# CANN Open Software License Agreement Version 2.0 (the "License").
7-# Please refer to License for details. You may not use this file except in compliance with License.7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,8# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.9# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10# See LICENSE in the root of the software repository for the full text of the License.10# See LICENSE in the root of the software repository for the full text of the License.
@@ -19,9 +19,9 @@ __golden__ = {
19 "aclnnConvolutionBackward": "aclnn_convolution_backward_golden",19 "aclnnConvolutionBackward": "aclnn_convolution_backward_golden",
20 "aclnnConvTbcBackward": "aclnn_conv_tbc_backward_golden",20 "aclnnConvTbcBackward": "aclnn_conv_tbc_backward_golden",
21 },21 },
22- "torch_api": {22+ "e2e": {
23 "torch.ops.aten.convolution_backward": "aten_convolution_backward_golden",23 "torch.ops.aten.convolution_backward": "aten_convolution_backward_golden",
24- }24+ },
25}25}
26 26 
27 27 
@@ -41,7 +41,9 @@ def to_float32(t):
41 return None41 return None
42 if isinstance(t, torch.Tensor):42 if isinstance(t, torch.Tensor):
43 dtype_str = str(t.dtype)43 dtype_str = str(t.dtype)
44- if any(s in dtype_str for s in ['hifloat8', 'float8', 'float4', 'int4', 'bfloat16']):44+ if any(
45+ s in dtype_str for s in ["hifloat8", "float8", "float4", "int4", "bfloat16"]
46+ ):
45 return t.float()47 return t.float()
46 return t.to(torch.float32)48 return t.to(torch.float32)
47 return t.astype(np.float32)49 return t.astype(np.float32)
@@ -74,21 +76,81 @@ def simulate_hf32_precision(data, short_soc_version=None):
74 return input_hf32.view(np.float32)76 return input_hf32.view(np.float32)
75 77 
76 78 
77-def _compute_conv_backward(gradOutput, input, weight, stride, padding,79+def _compute_conv_backward(
78- dilation, groups, conv_dim, transposed=False, outputPadding=0,80+ gradOutput,
79- outputMask=[True, True, False], biasSizes=None,81+ input,
80- cubeMathType=0, short_soc_version=None):82+ weight,
83+ stride,
84+ padding,
85+ dilation,
86+ groups,
87+ conv_dim,
88+ transposed=False,
89+ outputPadding=0,
90+ outputMask=[True, True, False],
91+ biasSizes=None,
92+ cubeMathType=0,
93+ short_soc_version=None,
94+):
81 stride = ensure_list(stride, conv_dim)95 stride = ensure_list(stride, conv_dim)
82 padding = ensure_list(padding, conv_dim)96 padding = ensure_list(padding, conv_dim)
83 dilation = ensure_list(dilation, conv_dim)97 dilation = ensure_list(dilation, conv_dim)
84 outputPadding = ensure_list(outputPadding, conv_dim)98 outputPadding = ensure_list(outputPadding, conv_dim)
85 99 
86 if isinstance(gradOutput, np.ndarray):100 if isinstance(gradOutput, np.ndarray):
87- gradOutput = torch.from_numpy(gradOutput)101+ if gradOutput.dtype not in (
102+ np.float32,
103+ np.float16,
104+ np.float64,
105+ np.int64,
106+ np.int32,
107+ np.int16,
108+ np.int8,
109+ np.uint64,
110+ np.uint32,
111+ np.uint16,
112+ np.uint8,
113+ np.bool_,
114+ ):
115+ gradOutput = torch.from_numpy(gradOutput.astype(np.float32))
116+ else:
117+ gradOutput = torch.from_numpy(gradOutput)
88 if isinstance(input, np.ndarray):118 if isinstance(input, np.ndarray):
89- input = torch.from_numpy(input)119+ if input.dtype not in (
120+ np.float32,
121+ np.float16,
122+ np.float64,
123+ np.int64,
124+ np.int32,
125+ np.int16,
126+ np.int8,
127+ np.uint64,
128+ np.uint32,
129+ np.uint16,
130+ np.uint8,
131+ np.bool_,
132+ ):
133+ input = torch.from_numpy(input.astype(np.float32))
134+ else:
135+ input = torch.from_numpy(input)
90 if isinstance(weight, np.ndarray):136 if isinstance(weight, np.ndarray):
91- weight = torch.from_numpy(weight)137+ if weight.dtype not in (
138+ np.float32,
139+ np.float16,
140+ np.float64,
141+ np.int64,
142+ np.int32,
143+ np.int16,
144+ np.int8,
145+ np.uint64,
146+ np.uint32,
147+ np.uint16,
148+ np.uint8,
149+ np.bool_,
150+ ):
151+ weight = torch.from_numpy(weight.astype(np.float32))
152+ else:
153+ weight = torch.from_numpy(weight)
92 154 
93 if gradOutput is not None:155 if gradOutput is not None:
94 gradOutput = gradOutput.float()156 gradOutput = gradOutput.float()
@@ -97,12 +159,20 @@ def _compute_conv_backward(gradOutput, input, weight, stride, padding,
97 if weight is not None:159 if weight is not None:
98 weight = weight.float()160 weight = weight.float()
99 161 
100- input_dtype_str = str(input.dtype).split('.')[-1] if input is not None else 'float32'162+ input_dtype_str = (
101- if input_dtype_str == 'float32' and input is not None and weight is not None:163+ str(input.dtype).split(".")[-1] if input is not None else "float32"
164+ )
165+ if input_dtype_str == "float32" and input is not None and weight is not None:
102 if cubeMathType in [1, 3]:166 if cubeMathType in [1, 3]:
103- input_np = simulate_hf32_precision(input.numpy().astype(np.float32), short_soc_version)167+ input_np = simulate_hf32_precision(
104- weight_np = simulate_hf32_precision(weight.numpy().astype(np.float32), short_soc_version)168+ input.numpy().astype(np.float32), short_soc_version
105- gradOutput_np = simulate_hf32_precision(gradOutput.numpy().astype(np.float32), short_soc_version)169+ )
170+ weight_np = simulate_hf32_precision(
171+ weight.numpy().astype(np.float32), short_soc_version
172+ )
173+ gradOutput_np = simulate_hf32_precision(
174+ gradOutput.numpy().astype(np.float32), short_soc_version
175+ )
106 input = torch.from_numpy(input_np)176 input = torch.from_numpy(input_np)
107 weight = torch.from_numpy(weight_np)177 weight = torch.from_numpy(weight_np)
108 gradOutput = torch.from_numpy(gradOutput_np)178 gradOutput = torch.from_numpy(gradOutput_np)
@@ -113,12 +183,23 @@ def _compute_conv_backward(gradOutput, input, weight, stride, padding,
113 183 
114 if not outputMask[2]:184 if not outputMask[2]:
115 biasSizes = None185 biasSizes = None
116- elif biasSizes is None or (isinstance(biasSizes, (list, torch.Size)) and len(biasSizes) == 0):186+ elif biasSizes is None or (
187+ isinstance(biasSizes, (list, torch.Size)) and len(biasSizes) == 0
188+ ):
117 biasSizes = list(weight.shape[:1])189 biasSizes = list(weight.shape[:1])
118 190 
119 return torch.ops.aten.convolution_backward(191 return torch.ops.aten.convolution_backward(
120- gradOutput, input, weight, biasSizes,192+ gradOutput,
121- stride, padding, dilation, transposed, outputPadding, groups, outputMask193+ input,
194+ weight,
195+ biasSizes,
196+ stride,
197+ padding,
198+ dilation,
199+ transposed,
200+ outputPadding,
201+ groups,
202+ outputMask,
122 )203 )
123 204 
124 205 
@@ -138,7 +219,7 @@ def aclnn_convolution_backward_golden(
138 gradInput=None,219 gradInput=None,
139 gradWeight=None,220 gradWeight=None,
140 gradBias=None,221 gradBias=None,
141- **kwargs222+ **kwargs,
142):223):
143 """224 """
144 ACLNN API golden for aclnnConvolutionBackward.225 ACLNN API golden for aclnnConvolutionBackward.
@@ -147,21 +228,81 @@ def aclnn_convolution_backward_golden(
147 stride, padding, dilation, transposed,228 stride, padding, dilation, transposed,
148 outputPadding, groups, outputMask, cubeMathType,229 outputPadding, groups, outputMask, cubeMathType,
149 gradInput, gradWeight, gradBias)230 gradInput, gradWeight, gradBias)
150- 231+ 
151 Supports 1D, 2D, 3D convolution backward.232 Supports 1D, 2D, 3D convolution backward.
152 """233 """
153- input_shape = input.shape if isinstance(input, torch.Tensor) or hasattr(input, 'shape') else None234+ input_shape = (
154- weight_shape = weight.shape if isinstance(weight, torch.Tensor) or hasattr(weight, 'shape') else None235+ input.shape
236+ if isinstance(input, torch.Tensor) or hasattr(input, "shape")
237+ else None
238+ )
239+ weight_shape = (
240+ weight.shape
241+ if isinstance(weight, torch.Tensor) or hasattr(weight, "shape")
242+ else None
243+ )
155 conv_dim = get_conv_dim(input_shape, weight_shape)244 conv_dim = get_conv_dim(input_shape, weight_shape)
156 short_soc_version = kwargs.get("short_soc_version", None)245 short_soc_version = kwargs.get("short_soc_version", None)
157 246 
158 grad_input, grad_weight, grad_bias = _compute_conv_backward(247 grad_input, grad_weight, grad_bias = _compute_conv_backward(
159- gradOutput, input, weight, stride, padding,248+ gradOutput,
160- dilation, groups, conv_dim, transposed, outputPadding, outputMask, biasSizes,249+ input,
161- cubeMathType=cubeMathType, short_soc_version=short_soc_version250+ weight,
251+ stride,
252+ padding,
253+ dilation,
254+ groups,
255+ conv_dim,
256+ transposed,
257+ outputPadding,
258+ outputMask,
259+ biasSizes,
260+ cubeMathType=cubeMathType,
261+ short_soc_version=short_soc_version,
162 )262 )
163- 263+ 
164- return (grad_input, grad_weight, grad_bias)264+ tensor_dtypes = kwargs.get("tensor_dtypes", None)
265+ 
266+ def _convert_output(out, idx):
267+ if out is None:
268+ return None
269+ if isinstance(out, torch.Tensor):
270+ out_np = out.detach().numpy()
271+ else:
272+ out_np = np.asarray(out)
273+ if tensor_dtypes and idx < len(tensor_dtypes):
274+ dtype_str = str(tensor_dtypes[idx]) if tensor_dtypes[idx] else None
275+ if dtype_str in ("hifloat8", "float8_e4m3fn", "float8_e5m2"):
276+ from ttk.utilities import numpy_hifloat8
277+ import ml_dtypes
278+ 
279+ np_dtype_map = {
280+ "hifloat8": numpy_hifloat8(),
281+ "float8_e4m3fn": ml_dtypes.float8_e4m3fn,
282+ "float8_e5m2": ml_dtypes.float8_e5m2,
283+ }
284+ np_dtype = np_dtype_map.get(dtype_str)
285+ if np_dtype is not None:
286+ out_np = out_np.astype(np_dtype)
287+ return np.ascontiguousarray(out_np)
288+ elif dtype_str in ("float16", "bfloat16"):
289+ try:
290+ import ml_dtypes
291+ 
292+ dtype_map = {"float16": np.float16, "bfloat16": ml_dtypes.bfloat16}
293+ np_dtype = dtype_map.get(dtype_str)
294+ if np_dtype is not None:
295+ out_np = out_np.astype(np_dtype)
296+ return np.ascontiguousarray(out_np)
297+ except ImportError:
298+ pass
299+ return np.ascontiguousarray(out_np.astype(np.float32))
300+ 
301+ grad_input_result = _convert_output(grad_input, 3)
302+ grad_weight_result = _convert_output(grad_weight, 4)
303+ grad_bias_result = _convert_output(grad_bias, 5)
304+ 
305+ return (grad_input_result, grad_weight_result, grad_bias_result)
165 306 
166 307 
167def aclnn_conv_tbc_backward_golden(308def aclnn_conv_tbc_backward_golden(
@@ -174,7 +315,7 @@ def aclnn_conv_tbc_backward_golden(
174 gradInput=None,315 gradInput=None,
175 gradWeight=None,316 gradWeight=None,
176 gradBias=None,317 gradBias=None,
177- **kwargs318+ **kwargs,
178):319):
179 """320 """
180 ACLNN API golden for aclnnConvTbcBackward.321 ACLNN API golden for aclnnConvTbcBackward.
@@ -198,12 +339,18 @@ def aclnn_conv_tbc_backward_golden(
198 input = input.float()339 input = input.float()
199 weight = weight.float()340 weight = weight.float()
200 341 
201- input_dtype_str = str(input.dtype).split('.')[-1]342+ input_dtype_str = str(input.dtype).split(".")[-1]
202- if input_dtype_str == 'float32':343+ if input_dtype_str == "float32":
203 if cubeMathType in [1, 3]:344 if cubeMathType in [1, 3]:
204- self_np = simulate_hf32_precision(self.numpy().astype(np.float32), short_soc_version)345+ self_np = simulate_hf32_precision(
205- input_np = simulate_hf32_precision(input.numpy().astype(np.float32), short_soc_version)346+ self.numpy().astype(np.float32), short_soc_version
206- weight_np = simulate_hf32_precision(weight.numpy().astype(np.float32), short_soc_version)347+ )
348+ input_np = simulate_hf32_precision(
349+ input.numpy().astype(np.float32), short_soc_version
350+ )
351+ weight_np = simulate_hf32_precision(
352+ weight.numpy().astype(np.float32), short_soc_version
353+ )
207 self = torch.from_numpy(self_np)354 self = torch.from_numpy(self_np)
208 input = torch.from_numpy(input_np)355 input = torch.from_numpy(input_np)
209 weight = torch.from_numpy(weight_np)356 weight = torch.from_numpy(weight_np)
@@ -226,7 +373,7 @@ def aclnn_conv_tbc_backward_golden(
226 False,373 False,
227 [0],374 [0],
228 1,375 1,
229- output_mask376+ output_mask,
230 )377 )
231 378 
232 if grad_input_ncl is not None:379 if grad_input_ncl is not None:
@@ -247,22 +394,40 @@ def aten_convolution_backward_golden(
247 output_padding: Union[int, List[int]] = 0,394 output_padding: Union[int, List[int]] = 0,
248 groups: int = 1,395 groups: int = 1,
249 output_mask: List[bool] = [True, True, False],396 output_mask: List[bool] = [True, True, False],
250- **kwargs397+ **kwargs,
251):398):
252 """399 """
253 Golden for torch.ops.aten.convolution_backward.400 Golden for torch.ops.aten.convolution_backward.
254 Supports 1D, 2D, 3D convolution backward.401 Supports 1D, 2D, 3D convolution backward.
255 """402 """
256 403 
257- input_shape = input.shape if isinstance(input, torch.Tensor) or hasattr(input, 'shape') else None404+ input_shape = (
258- weight_shape = weight.shape if isinstance(weight, torch.Tensor) or hasattr(weight, 'shape') else None405+ input.shape
406+ if isinstance(input, torch.Tensor) or hasattr(input, "shape")
407+ else None
408+ )
409+ weight_shape = (
410+ weight.shape
411+ if isinstance(weight, torch.Tensor) or hasattr(weight, "shape")
412+ else None
413+ )
259 conv_dim = get_conv_dim(input_shape, weight_shape)414 conv_dim = get_conv_dim(input_shape, weight_shape)
260 short_soc_version = kwargs.get("short_soc_version", None)415 short_soc_version = kwargs.get("short_soc_version", None)
261 416 
262 grad_input, grad_weight, grad_bias = _compute_conv_backward(417 grad_input, grad_weight, grad_bias = _compute_conv_backward(
263- grad_output, input, weight, stride, padding,418+ grad_output,
264- dilation, groups, conv_dim, transposed, output_padding, output_mask, bias_sizes,419+ input,
265- short_soc_version=short_soc_version420+ weight,
421+ stride,
422+ padding,
423+ dilation,
424+ groups,
425+ conv_dim,
426+ transposed,
427+ output_padding,
428+ output_mask,
429+ bias_sizes,
430+ short_soc_version=short_soc_version,
266 )431 )
267- 432+ 
268- return (grad_input, grad_weight, grad_bias)433+ return (grad_input, grad_weight, grad_bias)
Mconv/convolution_backward/tests/assets/input.py+12-14
@@ -2,26 +2,24 @@
2# -*- coding: UTF-8 -*-2# -*- coding: UTF-8 -*-
3# ----------------------------------------------------------------------------3# ----------------------------------------------------------------------------
4# Copyright (c) 2025-2026 Huawei Technologies Co., Ltd.4# Copyright (c) 2025-2026 Huawei Technologies Co., Ltd.
5-# This program is free software, you can redistribute it and/or modify it under terms and conditions of5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6# CANN Open Software License Agreement Version 2.0 (the "License").6# CANN Open Software License Agreement Version 2.0 (the "License").
7-# Please refer to License for details. You may not use this file except in compliance with License.7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,8# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.9# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10# See LICENSE in the root of the software repository for the full text of the License.10# See LICENSE in the root of the software repository for the full text of the License.
11# ----------------------------------------------------------------------------11# ----------------------------------------------------------------------------
12 12 
13from typing import List, Optional, Union13from typing import List, Optional, Union
14-import numpy as np
15-import torch
16 14 
17__input__ = {15__input__ = {
18 "aclnn": {16 "aclnn": {
19 "aclnnConvolutionBackward": "aclnn_convolution_backward_input",17 "aclnnConvolutionBackward": "aclnn_convolution_backward_input",
20 "aclnnConvTbcBackward": "aclnn_conv_tbc_backward_input",18 "aclnnConvTbcBackward": "aclnn_conv_tbc_backward_input",
21 },19 },
22- "torch_api": {20+ "e2e": {
23 "torch.ops.aten.convolution_backward": "aten_convolution_backward_input",21 "torch.ops.aten.convolution_backward": "aten_convolution_backward_input",
24- }22+ },
25}23}
26 24 
27 25 
@@ -41,7 +39,7 @@ def aclnn_convolution_backward_input(
41 gradInput=None,39 gradInput=None,
42 gradWeight=None,40 gradWeight=None,
43 gradBias=None,41 gradBias=None,
44- **kwargs42+ **kwargs,
45):43):
46 """44 """
47 Input function for aclnnConvolutionBackward.45 Input function for aclnnConvolutionBackward.
@@ -50,7 +48,7 @@ def aclnn_convolution_backward_input(
50 stride, padding, dilation, transposed,48 stride, padding, dilation, transposed,
51 outputPadding, groups, outputMask, cubeMathType,49 outputPadding, groups, outputMask, cubeMathType,
52 gradInput, gradWeight, gradBias)50 gradInput, gradWeight, gradBias)
53- 51+ 
54 Args:52 Args:
55 gradOutput: Gradient of output (NCL/NCHW/NCDHW)53 gradOutput: Gradient of output (NCL/NCHW/NCDHW)
56 input: Original input tensor54 input: Original input tensor
@@ -68,7 +66,7 @@ def aclnn_convolution_backward_input(
68 gradWeight: Pre-allocated gradWeight output66 gradWeight: Pre-allocated gradWeight output
69 gradBias: Pre-allocated gradBias output67 gradBias: Pre-allocated gradBias output
70 **kwargs: Additional context68 **kwargs: Additional context
71- 69+ 
72 Returns:70 Returns:
73 List of inputs71 List of inputs
74 """72 """
@@ -85,14 +83,14 @@ def aclnn_conv_tbc_backward_input(
85 gradInput=None,83 gradInput=None,
86 gradWeight=None,84 gradWeight=None,
87 gradBias=None,85 gradBias=None,
88- **kwargs86+ **kwargs,
89):87):
90 """88 """
91 Input function for aclnnConvTbcBackward.89 Input function for aclnnConvTbcBackward.
92 Parameter names and order follow aclnn_convolution_backward.h:90 Parameter names and order follow aclnn_convolution_backward.h:
93 aclnnConvTbcBackwardGetWorkspaceSize(self, input, weight, bias, pad, cubeMathType,91 aclnnConvTbcBackwardGetWorkspaceSize(self, input, weight, bias, pad, cubeMathType,
94 gradInput, gradWeight, gradBias)92 gradInput, gradWeight, gradBias)
95- 93+ 
96 Args:94 Args:
97 self: Convolution output gradient (ND/NCL)95 self: Convolution output gradient (ND/NCL)
98 input: Convolution input (ND/NCL)96 input: Convolution input (ND/NCL)
@@ -104,7 +102,7 @@ def aclnn_conv_tbc_backward_input(
104 gradWeight: Pre-allocated gradWeight output102 gradWeight: Pre-allocated gradWeight output
105 gradBias: Pre-allocated gradBias output103 gradBias: Pre-allocated gradBias output
106 **kwargs: Additional context104 **kwargs: Additional context
107- 105+ 
108 Returns:106 Returns:
109 List of inputs107 List of inputs
110 """108 """
@@ -123,10 +121,10 @@ def aten_convolution_backward_input(
123 output_padding: Union[int, List[int]] = 0,121 output_padding: Union[int, List[int]] = 0,
124 groups: int = 1,122 groups: int = 1,
125 output_mask: List[bool] = [True, True, False],123 output_mask: List[bool] = [True, True, False],
126- **kwargs124+ **kwargs,
127):125):
128 """126 """
129 Input function for torch.ops.aten.convolution_backward.127 Input function for torch.ops.aten.convolution_backward.
130 Supports 1D, 2D, 3D convolution backward.128 Supports 1D, 2D, 3D convolution backward.
131 """129 """
132- return [grad_output, input, weight]130+ return [grad_output, input, weight]
Mconv/convolution_forward/tests/assets/golden.py+424-141
@@ -2,15 +2,15 @@
2# -*- coding: UTF-8 -*-2# -*- coding: UTF-8 -*-
3# ----------------------------------------------------------------------------3# ----------------------------------------------------------------------------
4# Copyright (c) 2025-2026 Huawei Technologies Co., Ltd.4# Copyright (c) 2025-2026 Huawei Technologies Co., Ltd.
5-# This program is free software, you can redistribute it and/or modify it under terms and conditions of5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6# CANN Open Software License Agreement Version 2.0 (the "License").6# CANN Open Software License Agreement Version 2.0 (the "License").
7-# Please refer to License for details. You may not use this file except in compliance with License.7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,8# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.9# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10# See LICENSE in the root of the software repository for the full text of the License.10# See LICENSE in the root of the software repository for the full text of the License.
11# ----------------------------------------------------------------------------11# ----------------------------------------------------------------------------
12 12 
13-from typing import List, Optional, Tuple, Union13+from typing import List, Tuple, Union
14import numpy as np14import numpy as np
15import torch15import torch
16import torch.nn.functional as F16import torch.nn.functional as F
@@ -33,7 +33,7 @@ __golden__ = {
33 "torch_npu.npu_conv2d": "torch_conv2d_golden",33 "torch_npu.npu_conv2d": "torch_conv2d_golden",
34 "torch_npu.npu_conv3d": "torch_conv3d_golden",34 "torch_npu.npu_conv3d": "torch_conv3d_golden",
35 "torch_npu.npu_quant_conv2d": "torch_npu_quant_conv2d_golden",35 "torch_npu.npu_quant_conv2d": "torch_npu_quant_conv2d_golden",
36- }36+ },
37}37}
38 38 
39 39 
@@ -55,7 +55,9 @@ def to_float32(t):
55 return np.float32(t)55 return np.float32(t)
56 if isinstance(t, torch.Tensor):56 if isinstance(t, torch.Tensor):
57 dtype_str = str(t.dtype)57 dtype_str = str(t.dtype)
58- if any(s in dtype_str for s in ['hifloat8', 'float8', 'float4', 'int4', 'bfloat16']):58+ if any(
59+ s in dtype_str for s in ["hifloat8", "float8", "float4", "int4", "bfloat16"]
60+ ):
59 return t.float()61 return t.float()
60 return t.to(torch.float32)62 return t.to(torch.float32)
61 return t.astype(np.float32)63 return t.astype(np.float32)
@@ -98,11 +100,11 @@ def simulate_hf32_precision(data, short_soc_version=None):
98def convert_output_dtype(out, output_dtype):100def convert_output_dtype(out, output_dtype):
99 """101 """
100 Convert output array to target dtype with overflow handling.102 Convert output array to target dtype with overflow handling.
101- 103+ 
102 Args:104 Args:
103 out: Output numpy array105 out: Output numpy array
104 output_dtype: Target dtype - can be str, torch.dtype, int (torch_npu dtype code)106 output_dtype: Target dtype - can be str, torch.dtype, int (torch_npu dtype code)
105- 107+ 
106 Returns:108 Returns:
107 numpy array with target dtype109 numpy array with target dtype
108 """110 """
@@ -121,13 +123,13 @@ def convert_output_dtype(out, output_dtype):
121 258: "int8",123 258: "int8",
122 283: "bfloat16",124 283: "bfloat16",
123 290: "hifloat8",125 290: "hifloat8",
124- 292: "float8_e4m3fn"126+ 292: "float8_e4m3fn",
125 }127 }
126 128 
127 if isinstance(output_dtype, int):129 if isinstance(output_dtype, int):
128 dtype_name = dtype_torch_npu_map.get(output_dtype, "float32")130 dtype_name = dtype_torch_npu_map.get(output_dtype, "float32")
129 elif isinstance(output_dtype, torch.dtype):131 elif isinstance(output_dtype, torch.dtype):
130- dtype_name = str(output_dtype).split('.')[-1]132+ dtype_name = str(output_dtype).split(".")[-1]
131 else:133 else:
132 dtype_name = output_dtype134 dtype_name = output_dtype
133 135 
@@ -142,10 +144,14 @@ def convert_output_dtype(out, output_dtype):
142 if isinstance(dtype_ref, str):144 if isinstance(dtype_ref, str):
143 module_name, dtype_cls_name = dtype_ref.split(".")145 module_name, dtype_cls_name = dtype_ref.split(".")
144 try:146 try:
145- dtype_cls = getattr(__import__(module_name, fromlist=[dtype_cls_name]), dtype_cls_name)147+ dtype_cls = getattr(
148+ __import__(module_name, fromlist=[dtype_cls_name]), dtype_cls_name
149+ )
146 except (ImportError, AttributeError):150 except (ImportError, AttributeError):
147- raise RuntimeError(f"{module_name} is required for {output_dtype}. "151+ raise RuntimeError(
148- f"Install: pip install {module_name}")152+ f"{module_name} is required for {output_dtype}. "
153+ f"Install: pip install {module_name}"
154+ )
149 out = out.astype(dtype_cls)155 out = out.astype(dtype_cls)
150 else:156 else:
151 out = out.astype(dtype_ref)157 out = out.astype(dtype_ref)
@@ -166,30 +172,49 @@ def decode_scale_tensor(scale_tensor):
166 if scale_tensor.dtype == np.int64 or scale_tensor.dtype == np.uint64:172 if scale_tensor.dtype == np.int64 or scale_tensor.dtype == np.uint64:
167 return scale_tensor.astype(np.uint32).view(np.float32)173 return scale_tensor.astype(np.uint32).view(np.float32)
168 return scale_tensor.astype(np.float32)174 return scale_tensor.astype(np.float32)
169- 175+ 
170 if isinstance(scale_tensor, torch.Tensor):176 if isinstance(scale_tensor, torch.Tensor):
171 np_arr = scale_tensor.cpu().numpy()177 np_arr = scale_tensor.cpu().numpy()
172 if np_arr.dtype == np.int64 or np_arr.dtype == np.uint64:178 if np_arr.dtype == np.int64 or np_arr.dtype == np.uint64:
173 return np_arr.astype(np.uint32).view(np.float32)179 return np_arr.astype(np.uint32).view(np.float32)
174 return np_arr.astype(np.float32)180 return np_arr.astype(np.float32)
175- 181+ 
176 return np.array(scale_tensor, dtype=np.float32)182 return np.array(scale_tensor, dtype=np.float32)
177 183 
178 184 
179-def _compute_conv_forward(input, weight, bias, stride, padding, 185+def _compute_conv_forward(
180- dilation, groups, conv_dim, transposed=False, 186+ input,
181- outputPadding=0, cubeMathType=0, short_soc_version=None):187+ weight,
188+ bias,
189+ stride,
190+ padding,
191+ dilation,
192+ groups,
193+ conv_dim,
194+ transposed=False,
195+ outputPadding=0,
196+ cubeMathType=0,
197+ short_soc_version=None,
198+):
182 stride = ensure_list(stride, conv_dim)199 stride = ensure_list(stride, conv_dim)
183 dilation = ensure_list(dilation, conv_dim)200 dilation = ensure_list(dilation, conv_dim)
184 outputPadding = ensure_list(outputPadding, conv_dim)201 outputPadding = ensure_list(outputPadding, conv_dim)
185 202 
186- input_dtype_str = str(input.dtype).split('.')[-1] if isinstance(input, torch.Tensor) else (str(input.dtype) if hasattr(input, 'dtype') else '')203+ input_dtype_str = (
187- weight_dtype_str = str(weight.dtype).split('.')[-1] if isinstance(weight, torch.Tensor) else (str(weight.dtype) if hasattr(weight, 'dtype') else '')204+ str(input.dtype).split(".")[-1]
205+ if isinstance(input, torch.Tensor)
206+ else (str(input.dtype) if hasattr(input, "dtype") else "")
207+ )
208+ weight_dtype_str = (
209+ str(weight.dtype).split(".")[-1]
210+ if isinstance(weight, torch.Tensor)
211+ else (str(weight.dtype) if hasattr(weight, "dtype") else "")
212+ )
188 213 
189 need_upcast = False214 need_upcast = False
190- if 'hifloat8' in input_dtype_str or 'hifloat8' in weight_dtype_str:215+ if "hifloat8" in input_dtype_str or "hifloat8" in weight_dtype_str:
191 need_upcast = True216 need_upcast = True
192- elif 'bfloat16' in input_dtype_str:217+ elif "bfloat16" in input_dtype_str:
193 need_upcast = True218 need_upcast = True
194 219 
195 if need_upcast:220 if need_upcast:
@@ -213,19 +238,35 @@ def _compute_conv_forward(input, weight, bias, stride, padding,
213 input = F.pad(input, (padding[2], padding[3], padding[0], padding[1]))238 input = F.pad(input, (padding[2], padding[3], padding[0], padding[1]))
214 padding = [0, 0]239 padding = [0, 0]
215 elif conv_dim == 3 and len(padding) == 6:240 elif conv_dim == 3 and len(padding) == 6:
216- input = F.pad(input, (padding[4], padding[5], padding[2], padding[3], padding[0], padding[1]))241+ input = F.pad(
242+ input,
243+ (
244+ padding[4],
245+ padding[5],
246+ padding[2],
247+ padding[3],
248+ padding[0],
249+ padding[1],
250+ ),
251+ )
217 padding = [0, 0, 0]252 padding = [0, 0, 0]
218 else:253 else:
219 padding = ensure_list(padding, conv_dim)254 padding = ensure_list(padding, conv_dim)
220 255 
221- if input_dtype_str == 'float32':256+ if input_dtype_str == "float32":
222 if cubeMathType in [1, 3]:257 if cubeMathType in [1, 3]:
223- input_np = simulate_hf32_precision(input.numpy().astype(np.float32), short_soc_version)258+ input_np = simulate_hf32_precision(
224- weight_np = simulate_hf32_precision(weight.numpy().astype(np.float32), short_soc_version)259+ input.numpy().astype(np.float32), short_soc_version
260+ )
261+ weight_np = simulate_hf32_precision(
262+ weight.numpy().astype(np.float32), short_soc_version
263+ )
225 input = torch.from_numpy(input_np)264 input = torch.from_numpy(input_np)
226 weight = torch.from_numpy(weight_np)265 weight = torch.from_numpy(weight_np)
227 if bias is not None:266 if bias is not None:
228- bias_np = simulate_hf32_precision(bias.numpy().astype(np.float32), short_soc_version)267+ bias_np = simulate_hf32_precision(
268+ bias.numpy().astype(np.float32), short_soc_version
269+ )
229 bias = torch.from_numpy(bias_np)270 bias = torch.from_numpy(bias_np)
230 elif cubeMathType == 2:271 elif cubeMathType == 2:
231 input = input.to(torch.float16).to(torch.float32)272 input = input.to(torch.float16).to(torch.float32)
@@ -234,12 +275,20 @@ def _compute_conv_forward(input, weight, bias, stride, padding,
234 bias = bias.to(torch.float16).to(torch.float32)275 bias = bias.to(torch.float16).to(torch.float32)
235 276 
236 out = torch.ops.aten.convolution(277 out = torch.ops.aten.convolution(
237- input, weight, bias,278+ input,
238- stride, padding, dilation, transposed, outputPadding, groups279+ weight,
280+ bias,
281+ stride,
282+ padding,
283+ dilation,
284+ transposed,
285+ outputPadding,
286+ groups,
239 )287 )
240 288 
241- if 'hifloat8' in input_dtype_str or 'hifloat8' in weight_dtype_str:289+ if "hifloat8" in input_dtype_str or "hifloat8" in weight_dtype_str:
242 from ttk.utilities import numpy_hifloat8290 from ttk.utilities import numpy_hifloat8
291+ 
243 out = torch.from_numpy(out.numpy().astype(numpy_hifloat8()).astype(np.float32))292 out = torch.from_numpy(out.numpy().astype(numpy_hifloat8()).astype(np.float32))
244 293 
245 return out294 return out
@@ -257,32 +306,52 @@ def aclnn_convolution_golden(
257 groups: int = 1,306 groups: int = 1,
258 output=None,307 output=None,
259 cubeMathType: int = 0,308 cubeMathType: int = 0,
260- **kwargs309+ **kwargs,
261):310):
262 """311 """
263 ACLNN API golden for aclnnConvolution.312 ACLNN API golden for aclnnConvolution.
264 Parameter names and order follow aclnn_convolution.h:313 Parameter names and order follow aclnn_convolution.h:
265 aclnnConvolutionGetWorkspaceSize(input, weight, bias, stride, padding, dilation,314 aclnnConvolutionGetWorkspaceSize(input, weight, bias, stride, padding, dilation,
266 transposed, outputPadding, groups, output, cubeMathType)315 transposed, outputPadding, groups, output, cubeMathType)
267- 316+ 
268 Supports 1D, 2D, 3D convolutions based on input tensor dimensions.317 Supports 1D, 2D, 3D convolutions based on input tensor dimensions.
269 Data types: FLOAT, FLOAT16, BFLOAT16, HIFLOAT8, FLOAT8_E4M3FN318 Data types: FLOAT, FLOAT16, BFLOAT16, HIFLOAT8, FLOAT8_E4M3FN
270 Formats: NCL, NCHW, NCDHW319 Formats: NCL, NCHW, NCDHW
271 """320 """
272- input_shape = input.shape if isinstance(input, torch.Tensor) or hasattr(input, 'shape') else None321+ input_shape = (
273- weight_shape = weight.shape if isinstance(weight, torch.Tensor) or hasattr(weight, 'shape') else None322+ input.shape
323+ if isinstance(input, torch.Tensor) or hasattr(input, "shape")
324+ else None
325+ )
326+ weight_shape = (
327+ weight.shape
328+ if isinstance(weight, torch.Tensor) or hasattr(weight, "shape")
329+ else None
330+ )
274 conv_dim = get_conv_dim(input_shape, weight_shape)331 conv_dim = get_conv_dim(input_shape, weight_shape)
275 short_soc_version = kwargs.get("short_soc_version", None)332 short_soc_version = kwargs.get("short_soc_version", None)
276- 333+ 
277- out = _compute_conv_forward(input, weight, bias, stride, padding,334+ out = _compute_conv_forward(
278- dilation, groups, conv_dim, transposed, outputPadding,335+ input,
279- cubeMathType, short_soc_version)336+ weight,
280- 337+ bias,
338+ stride,
339+ padding,
340+ dilation,
341+ groups,
342+ conv_dim,
343+ transposed,
344+ outputPadding,
345+ cubeMathType,
346+ short_soc_version,
347+ )
348+ 
281 output_tensor_index = kwargs.get("output_tensor_indexes", [-1])[0]349 output_tensor_index = kwargs.get("output_tensor_indexes", [-1])[0]
282- output_dtype = kwargs.get('tensor_dtypes')[output_tensor_index]350+ output_dtype = kwargs.get("tensor_dtypes")[output_tensor_index]
283- 351+ 
284- if output_dtype == 'hifloat8':352+ if output_dtype == "hifloat8":
285 from ttk.utilities import numpy_hifloat8353 from ttk.utilities import numpy_hifloat8
354+ 
286 out = out.numpy().astype(numpy_hifloat8(), copy=False)355 out = out.numpy().astype(numpy_hifloat8(), copy=False)
287 else:356 else:
288 dtype_map = {357 dtype_map = {
@@ -292,24 +361,18 @@ def aclnn_convolution_golden(
292 }361 }
293 target_dtype = dtype_map.get(output_dtype, torch.bfloat16)362 target_dtype = dtype_map.get(output_dtype, torch.bfloat16)
294 out = out.to(target_dtype)363 out = out.to(target_dtype)
295- 364+ 
296 return out365 return out
297 366 
298 367 
299def aclnn_conv_tbc_golden(368def aclnn_conv_tbc_golden(
300- self,369+ self, weight, bias=None, pad: int = 0, output=None, cubeMathType: int = 0, **kwargs
301- weight,
302- bias=None,
303- pad: int = 0,
304- output=None,
305- cubeMathType: int = 0,
306- **kwargs
307):370):
308 """371 """
309 ACLNN API golden for aclnnConvTbc.372 ACLNN API golden for aclnnConvTbc.
310 Parameter names and order follow aclnn_convolution.h:373 Parameter names and order follow aclnn_convolution.h:
311 aclnnConvTbcGetWorkspaceSize(self, weight, bias, pad, output, cubeMathType)374 aclnnConvTbcGetWorkspaceSize(self, weight, bias, pad, output, cubeMathType)
312- 375+ 
313 TBC format: (T, B, C) where T is time/sequence, B is batch, C is channels.376 TBC format: (T, B, C) where T is time/sequence, B is batch, C is channels.
314 Equivalent to conv1d with input shape (B, C, T).377 Equivalent to conv1d with input shape (B, C, T).
315 Data types: FLOAT, FLOAT16, BFLOAT16, HIFLOAT8378 Data types: FLOAT, FLOAT16, BFLOAT16, HIFLOAT8
@@ -318,26 +381,38 @@ def aclnn_conv_tbc_golden(
318 short_soc_version = kwargs.get("short_soc_version", None)381 short_soc_version = kwargs.get("short_soc_version", None)
319 382 
320 if isinstance(self, np.ndarray):383 if isinstance(self, np.ndarray):
321- self = torch.from_numpy(self)384+ self = torch.from_numpy(self.astype(np.float32))
322 if isinstance(weight, np.ndarray):385 if isinstance(weight, np.ndarray):
323- weight = torch.from_numpy(weight)386+ weight = torch.from_numpy(weight.astype(np.float32))
324 if bias is not None and isinstance(bias, np.ndarray):387 if bias is not None and isinstance(bias, np.ndarray):
325- bias = torch.from_numpy(bias)388+ bias = torch.from_numpy(bias.astype(np.float32))
326- 389+ 
327 self = to_float32(self)390 self = to_float32(self)
328 weight = to_float32(weight)391 weight = to_float32(weight)
329 if bias is not None:392 if bias is not None:
330 bias = to_float32(bias)393 bias = to_float32(bias)
331 394 
332- input_dtype_str = str(self.dtype).split('.')[-1]395+ if weight.dim() == 3:
333- if input_dtype_str == 'float32':396+ weight = weight.permute(2, 1, 0).contiguous()
397+ 
398+ if self.dim() == 3:
399+ self = self.permute(1, 2, 0).contiguous()
400+ 
401+ input_dtype_str = str(self.dtype).split(".")[-1]
402+ if input_dtype_str == "float32":
334 if cubeMathType in [1, 3]:403 if cubeMathType in [1, 3]:
335- self_np = simulate_hf32_precision(self.numpy().astype(np.float32), short_soc_version)404+ self_np = simulate_hf32_precision(
336- weight_np = simulate_hf32_precision(weight.numpy().astype(np.float32), short_soc_version)405+ self.numpy().astype(np.float32), short_soc_version
406+ )
407+ weight_np = simulate_hf32_precision(
408+ weight.numpy().astype(np.float32), short_soc_version
409+ )
337 self = torch.from_numpy(self_np)410 self = torch.from_numpy(self_np)
338 weight = torch.from_numpy(weight_np)411 weight = torch.from_numpy(weight_np)
339 if bias is not None:412 if bias is not None:
340- bias_np = simulate_hf32_precision(bias.numpy().astype(np.float32), short_soc_version)413+ bias_np = simulate_hf32_precision(
414+ bias.numpy().astype(np.float32), short_soc_version
415+ )
341 bias = torch.from_numpy(bias_np)416 bias = torch.from_numpy(bias_np)
342 elif cubeMathType == 2:417 elif cubeMathType == 2:
343 self = self.to(torch.float16).to(torch.float32)418 self = self.to(torch.float16).to(torch.float32)
@@ -345,11 +420,32 @@ def aclnn_conv_tbc_golden(
345 if bias is not None:420 if bias is not None:
346 bias = bias.to(torch.float16).to(torch.float32)421 bias = bias.to(torch.float16).to(torch.float32)
347 422 
348- out = torch.ops.aten.convolution(423+ out = torch.ops.aten.convolution(self, weight, bias, [1], [pad], [1], False, [0], 1)
349- self, weight, bias, [1], [pad], [1], False, [0], 1424+ 
350- )425+ if out.dim() == 3:
351- 426+ out = out.permute(2, 0, 1).contiguous()
352- return out427+ 
428+ out_np = out.numpy().astype(np.float32)
429+ tensor_dtypes = kwargs.get("tensor_dtypes", [])
430+ if tensor_dtypes:
431+ out_dtype = tensor_dtypes[-1]
432+ if out_dtype in ("hifloat8", "float8_e4m3fn", "float8_e5m2"):
433+ from ttk.utilities import numpy_hifloat8
434+ import ml_dtypes
435+ 
436+ np_dtype_map = {
437+ "hifloat8": numpy_hifloat8(),
438+ "float8_e4m3fn": ml_dtypes.float8_e4m3fn,
439+ "float8_e5m2": ml_dtypes.float8_e5m2,
440+ }
441+ np_dtype = np_dtype_map.get(out_dtype)
442+ if np_dtype is not None:
443+ out_np = out_np.astype(np_dtype)
444+ return out_np
445+ else:
446+ out_np = convert_output_dtype(out_np, out_dtype)
447+ 
448+ return out_np
353 449 
354 450 
355def aclnn_conv_depthwise2d_golden(451def aclnn_conv_depthwise2d_golden(
@@ -362,14 +458,14 @@ def aclnn_conv_depthwise2d_golden(
362 dilation: Union[int, List[int]] = 1,458 dilation: Union[int, List[int]] = 1,
363 out=None,459 out=None,
364 cubeMathType: int = 0,460 cubeMathType: int = 0,
365- **kwargs461+ **kwargs,
366):462):
367 """463 """
368 ACLNN API golden for aclnnConvDepthwise2d.464 ACLNN API golden for aclnnConvDepthwise2d.
369 Parameter names and order follow aclnn_convolution.h:465 Parameter names and order follow aclnn_convolution.h:
370 aclnnConvDepthwise2dGetWorkspaceSize(self, weight, kernelSize, bias, stride,466 aclnnConvDepthwise2dGetWorkspaceSize(self, weight, kernelSize, bias, stride,
371 padding, dilation, out, cubeMathType)467 padding, dilation, out, cubeMathType)
372- 468+ 
373 Depthwise convolution where groups = input_channels.469 Depthwise convolution where groups = input_channels.
374 Data types: FLOAT, FLOAT16, BFLOAT16, HIFLOAT8470 Data types: FLOAT, FLOAT16, BFLOAT16, HIFLOAT8
375 Formats: NCHW471 Formats: NCHW
@@ -382,42 +478,49 @@ def aclnn_conv_depthwise2d_golden(
382 weight = torch.from_numpy(weight)478 weight = torch.from_numpy(weight)
383 if bias is not None and isinstance(bias, np.ndarray):479 if bias is not None and isinstance(bias, np.ndarray):
384 bias = torch.from_numpy(bias)480 bias = torch.from_numpy(bias)
385- 481+ 
386 self = to_float32(self)482 self = to_float32(self)
387 weight = to_float32(weight)483 weight = to_float32(weight)
388 if bias is not None:484 if bias is not None:
389 bias = to_float32(bias)485 bias = to_float32(bias)
390 486 
391- input_dtype_str = str(self.dtype).split('.')[-1]487+ input_dtype_str = str(self.dtype).split(".")[-1]
392- if input_dtype_str == 'float32':488+ if input_dtype_str == "float32":
393 if cubeMathType in [1, 3]:489 if cubeMathType in [1, 3]:
394- self_np = simulate_hf32_precision(self.numpy().astype(np.float32), short_soc_version)490+ self_np = simulate_hf32_precision(
395- weight_np = simulate_hf32_precision(weight.numpy().astype(np.float32), short_soc_version)491+ self.numpy().astype(np.float32), short_soc_version
492+ )
493+ weight_np = simulate_hf32_precision(
494+ weight.numpy().astype(np.float32), short_soc_version
495+ )
396 self = torch.from_numpy(self_np)496 self = torch.from_numpy(self_np)
397 weight = torch.from_numpy(weight_np)497 weight = torch.from_numpy(weight_np)
398 if bias is not None:498 if bias is not None:
399- bias_np = simulate_hf32_precision(bias.numpy().astype(np.float32), short_soc_version)499+ bias_np = simulate_hf32_precision(
500+ bias.numpy().astype(np.float32), short_soc_version
501+ )
400 bias = torch.from_numpy(bias_np)502 bias = torch.from_numpy(bias_np)
401 elif cubeMathType == 2:503 elif cubeMathType == 2:
402 self = self.to(torch.float16).to(torch.float32)504 self = self.to(torch.float16).to(torch.float32)
403 weight = weight.to(torch.float16).to(torch.float32)505 weight = weight.to(torch.float16).to(torch.float32)
404 if bias is not None:506 if bias is not None:
405 bias = bias.to(torch.float16).to(torch.float32)507 bias = bias.to(torch.float16).to(torch.float32)
406- 508+ 
407 groups = self.shape[1]509 groups = self.shape[1]
408- 510+ 
409 stride = ensure_list(stride, 2)511 stride = ensure_list(stride, 2)
410 padding = ensure_list(padding, 2)512 padding = ensure_list(padding, 2)
411 dilation = ensure_list(dilation, 2)513 dilation = ensure_list(dilation, 2)
412- 514+ 
413 out = torch.ops.aten.convolution(515 out = torch.ops.aten.convolution(
414 self, weight, bias, stride, padding, dilation, False, [0, 0], groups516 self, weight, bias, stride, padding, dilation, False, [0, 0], groups
415 )517 )
416 518 
417 output_tensor_index = kwargs.get("output_tensor_indexes", [-1])[0]519 output_tensor_index = kwargs.get("output_tensor_indexes", [-1])[0]
418- output_dtype = kwargs.get('tensor_dtypes')[output_tensor_index]520+ output_dtype = kwargs.get("tensor_dtypes")[output_tensor_index]
419- if output_dtype == 'hifloat8':521+ if output_dtype == "hifloat8":
420 from ttk.utilities import numpy_hifloat8522 from ttk.utilities import numpy_hifloat8
523+ 
421 out = out.numpy().astype(numpy_hifloat8(), copy=False)524 out = out.numpy().astype(numpy_hifloat8(), copy=False)
422 else:525 else:
423 dtype_map = {526 dtype_map = {
@@ -444,15 +547,19 @@ def aclnn_quant_conv_golden(
444 outputPadding=0,547 outputPadding=0,
445 groups=1,548 groups=1,
446 offsetx=0,549 offsetx=0,
447- roundMode='',550+ roundMode="",
448 output=None,551 output=None,
449 output_dtype=None,552 output_dtype=None,
450- **kwargs553+ **kwargs,
451):554):
452 import torch.nn.functional as F555 import torch.nn.functional as F
453 556 
454- x_shape = x.shape if isinstance(x, torch.Tensor) or hasattr(x, 'shape') else None557+ x_shape = x.shape if isinstance(x, torch.Tensor) or hasattr(x, "shape") else None
455- w_shape = weight.shape if isinstance(weight, torch.Tensor) or hasattr(weight, 'shape') else None558+ w_shape = (
559+ weight.shape
560+ if isinstance(weight, torch.Tensor) or hasattr(weight, "shape")
561+ else None
562+ )
456 conv_dim = get_conv_dim(x_shape, w_shape)563 conv_dim = get_conv_dim(x_shape, w_shape)
457 564 
458 x_np = to_float32(x)565 x_np = to_float32(x)
@@ -460,7 +567,11 @@ def aclnn_quant_conv_golden(
460 x_calc = x_np.numpy() if isinstance(x_np, torch.Tensor) else x_np567 x_calc = x_np.numpy() if isinstance(x_np, torch.Tensor) else x_np
461 w_calc = w_np.numpy() if isinstance(w_np, torch.Tensor) else w_np568 w_calc = w_np.numpy() if isinstance(w_np, torch.Tensor) else w_np
462 569 
463- scale_np = decode_scale_tensor(scale) if scale is not None else np.ones(w_calc.shape[0], dtype=np.float32)570+ scale_np = (
571+ decode_scale_tensor(scale)
572+ if scale is not None
573+ else np.ones(w_calc.shape[0], dtype=np.float32)
574+ )
464 575 
465 bias_np = None576 bias_np = None
466 if bias is not None and not isinstance(bias, (int, float)):577 if bias is not None and not isinstance(bias, (int, float)):
@@ -484,7 +595,9 @@ def aclnn_quant_conv_golden(
484 pad_left = pad_right = pads[2]595 pad_left = pad_right = pads[2]
485 else:596 else:
486 pad_val = int(pads[0])597 pad_val = int(pads[0])
487- pad_front = pad_back = pad_top = pad_bottom = pad_left = pad_right = pad_val598+ pad_front = pad_back = pad_top = pad_bottom = pad_left = pad_right = (
599+ pad_val
600+ )
488 else:601 else:
489 pad_val = int(pads)602 pad_val = int(pads)
490 pad_front = pad_back = pad_top = pad_bottom = pad_left = pad_right = pad_val603 pad_front = pad_back = pad_top = pad_bottom = pad_left = pad_right = pad_val
@@ -503,40 +616,106 @@ def aclnn_quant_conv_golden(
503 pad_val = int(pads)616 pad_val = int(pads)
504 pad_top = pad_bottom = pad_left = pad_right = pad_val617 pad_top = pad_bottom = pad_left = pad_right = pad_val
505 618 
506- x_torch = torch.from_numpy(x_calc.astype(np.float32)) if x_calc.dtype != np.float32 else torch.from_numpy(x_calc)619+ x_torch = (
507- w_torch = torch.from_numpy(w_calc.astype(np.float32)) if w_calc.dtype != np.float32 else torch.from_numpy(w_calc)620+ torch.from_numpy(x_calc.astype(np.float32))
621+ if x_calc.dtype != np.float32
622+ else torch.from_numpy(x_calc)
623+ )
624+ w_torch = (
625+ torch.from_numpy(w_calc.astype(np.float32))
626+ if w_calc.dtype != np.float32
627+ else torch.from_numpy(w_calc)
628+ )
508 629 
509 if conv_dim == 3:630 if conv_dim == 3:
510- pad_needed = any(p > 0 for p in (pad_front, pad_back, pad_top, pad_bottom, pad_left, pad_right))631+ pad_needed = any(
632+ p > 0
633+ for p in (pad_front, pad_back, pad_top, pad_bottom, pad_left, pad_right)
634+ )
511 else:635 else:
512 pad_needed = any(p > 0 for p in (pad_top, pad_bottom, pad_left, pad_right))636 pad_needed = any(p > 0 for p in (pad_top, pad_bottom, pad_left, pad_right))
513 637 
514 if pad_needed:638 if pad_needed:
515 pad_value = float(offsetx) if offsetx != 0 else 0.0639 pad_value = float(offsetx) if offsetx != 0 else 0.0
516 if conv_dim == 3:640 if conv_dim == 3:
517- x_torch = F.pad(x_torch, (pad_left, pad_right, pad_top, pad_bottom, pad_front, pad_back),641+ x_torch = F.pad(
518- "constant", pad_value)642+ x_torch,
643+ (pad_left, pad_right, pad_top, pad_bottom, pad_front, pad_back),
644+ "constant",
645+ pad_value,
646+ )
519 else:647 else:
520- x_torch = F.pad(x_torch, (pad_left, pad_right, pad_top, pad_bottom),648+ x_torch = F.pad(
521- "constant", pad_value)649+ x_torch,
650+ (pad_left, pad_right, pad_top, pad_bottom),
651+ "constant",
652+ pad_value,
653+ )
522 654 
523- bias_torch = torch.from_numpy(bias_np) if bias_np is not None else None655+ bias_torch = None
524 656 
525 out = torch.ops.aten.convolution(657 out = torch.ops.aten.convolution(
526- x_torch, w_torch, bias_torch,658+ x_torch,
527- strides, [0] * conv_dim, dilations_list,659+ w_torch,
528- False, [0] * conv_dim, groups660+ bias_torch,
661+ strides,
662+ [0] * conv_dim,
663+ dilations_list,
664+ False,
665+ [0] * conv_dim,
666+ groups,
529 )667 )
530 668 
531 out_np = out.numpy()669 out_np = out.numpy()
532 670 
533- if scale_np is not None:671+ orig_x_dtype = (
534- scale_shape = (1, scale_np.shape[0]) + (1,) * conv_dim672+ str(x.dtype).split(".")[-1]
535- out_np = out_np * scale_np.reshape(scale_shape)673+ if isinstance(x, torch.Tensor)
674+ else (str(getattr(x, "dtype", "")))
675+ )
676+ offset_np = None
677+ if offset is not None and not isinstance(offset, (int, float)):
678+ offset_np = to_float32(offset)
679+ if isinstance(offset_np, torch.Tensor):
680+ offset_np = offset_np.numpy()
681+ 
682+ bias_is_quantized = False
683+ if bias is not None and not isinstance(bias, (int, float)):
684+ if isinstance(bias, torch.Tensor):
685+ bias_is_quantized = bias.dtype in (torch.int32, torch.int64)
686+ elif isinstance(bias, np.ndarray):
687+ bias_is_quantized = bias.dtype in (np.int32, np.int64)
688+ 
689+ if orig_x_dtype == "int8":
690+ if bias_is_quantized:
691+ if bias_np is not None:
692+ bias_shape = (1, bias_np.shape[0]) + (1,) * conv_dim
693+ out_np = out_np + bias_np.reshape(bias_shape)
694+ if scale_np is not None:
695+ scale_shape = (1, scale_np.shape[0]) + (1,) * conv_dim
696+ out_np = out_np * scale_np.reshape(scale_shape)
697+ else:
698+ if scale_np is not None:
699+ scale_shape = (1, scale_np.shape[0]) + (1,) * conv_dim
700+ out_np = out_np * scale_np.reshape(scale_shape)
701+ if bias_np is not None:
702+ bias_shape = (1, bias_np.shape[0]) + (1,) * conv_dim
703+ out_np = out_np + bias_np.reshape(bias_shape)
704+ else:
705+ if bias_np is not None:
706+ bias_shape = (1, bias_np.shape[0]) + (1,) * conv_dim
707+ out_np = out_np + bias_np.reshape(bias_shape)
708+ if scale_np is not None:
709+ scale_shape = (1, scale_np.shape[0]) + (1,) * conv_dim
710+ out_np = out_np * scale_np.reshape(scale_shape)
711+ 
712+ if offset_np is not None:
713+ offset_shape = (1, offset_np.shape[0]) + (1,) * conv_dim
714+ out_np = out_np + offset_np.reshape(offset_shape)
536 715 
537 resolved_dtype = output_dtype716 resolved_dtype = output_dtype
538 if resolved_dtype is None:717 if resolved_dtype is None:
539- tensor_dtypes = kwargs.get('tensor_dtypes', None)718+ tensor_dtypes = kwargs.get("tensor_dtypes", None)
540 if tensor_dtypes is not None and not isinstance(tensor_dtypes, str):719 if tensor_dtypes is not None and not isinstance(tensor_dtypes, str):
541 output_tensor_index = kwargs.get("output_tensor_indexes", [-1])[0]720 output_tensor_index = kwargs.get("output_tensor_indexes", [-1])[0]
542 resolved_dtype = tensor_dtypes[output_tensor_index]721 resolved_dtype = tensor_dtypes[output_tensor_index]
@@ -544,17 +723,41 @@ def aclnn_quant_conv_golden(
544 resolved_dtype = tensor_dtypes723 resolved_dtype = tensor_dtypes
545 724 
546 pre_round_dtypes = {"float16", "bfloat16"}725 pre_round_dtypes = {"float16", "bfloat16"}
547- dtype_name = resolved_dtype if isinstance(resolved_dtype, str) else str(resolved_dtype).split('.')[-1] if isinstance(resolved_dtype, torch.dtype) else None726+ np_float8_dtypes = {"hifloat8", "float8_e4m3fn", "float8_e5m2"}
727+ dtype_name = (
728+ resolved_dtype
729+ if isinstance(resolved_dtype, str)
730+ else str(resolved_dtype).split(".")[-1]
731+ if isinstance(resolved_dtype, torch.dtype)
732+ else None
733+ )
548 if dtype_name in pre_round_dtypes:734 if dtype_name in pre_round_dtypes:
549- out_np = convert_output_dtype(out_np, resolved_dtype)735+ out_np = out_np.astype(np.float32)
550- if roundMode in ["rint", "round"]:736+ if dtype_name == "float16":
551- out_np = np.rint(out_np).astype(out_np.dtype)737+ out_np = out_np.astype(np.float16)
552- elif roundMode == "floor":738+ elif dtype_name == "bfloat16":
553- out_np = np.floor(out_np).astype(out_np.dtype)739+ try:
554- elif roundMode == "ceil":740+ import ml_dtypes
555- out_np = np.ceil(out_np).astype(out_np.dtype)741+ 
742+ out_np = out_np.astype(ml_dtypes.bfloat16)
743+ except ImportError:
744+ out_np = out_np.astype(np.float32)
556 return out_np745 return out_np
557 746 
747+ if dtype_name in np_float8_dtypes:
748+ from ttk.utilities import numpy_hifloat8
749+ import ml_dtypes
750+ 
751+ np_dtype_map = {
752+ "hifloat8": numpy_hifloat8(),
753+ "float8_e4m3fn": ml_dtypes.float8_e4m3fn,
754+ "float8_e5m2": ml_dtypes.float8_e5m2,
755+ }
756+ np_dtype = np_dtype_map.get(dtype_name)
757+ if np_dtype is not None:
758+ out_np = out_np.astype(np_dtype)
759+ return out_np
760+ 
558 return convert_output_dtype(out_np, resolved_dtype)761 return convert_output_dtype(out_np, resolved_dtype)
559 762 
560 763 
@@ -573,7 +776,7 @@ def torch_npu_quant_conv2d_golden(
573 offset=None,776 offset=None,
574 input_dtype=None,777 input_dtype=None,
575 weight_dtype=None,778 weight_dtype=None,
576- **kwargs779+ **kwargs,
577):780):
578 """781 """
579 E2E golden for torch_npu.npu_quant_conv2d.782 E2E golden for torch_npu.npu_quant_conv2d.
@@ -595,7 +798,7 @@ def torch_npu_quant_conv2d_golden(
595 roundMode=round_mode,798 roundMode=round_mode,
596 output=None,799 output=None,
597 output_dtype=output_dtype,800 output_dtype=output_dtype,
598- **kwargs801+ **kwargs,
599 )802 )
600 803 
601 804 
@@ -607,13 +810,14 @@ def torch_conv1d_golden(
607 padding: int = 0,810 padding: int = 0,
608 dilation: int = 1,811 dilation: int = 1,
609 groups: int = 1,812 groups: int = 1,
610- **kwargs813+ **kwargs,
611):814):
612 """815 """
613 Golden for torch.conv1d / torch.nn.functional.conv1d.816 Golden for torch.conv1d / torch.nn.functional.conv1d.
614 """817 """
615- return aten_convolution_golden(input, weight, bias, stride, padding,818+ return aten_convolution_golden(
616- dilation, groups=groups, **kwargs)819+ input, weight, bias, stride, padding, dilation, groups=groups, **kwargs
820+ )
617 821 
618 822 
619def torch_conv2d_golden(823def torch_conv2d_golden(
@@ -624,22 +828,43 @@ def torch_conv2d_golden(
624 padding: Union[int, Tuple[int, int], str] = 0,828 padding: Union[int, Tuple[int, int], str] = 0,
625 dilation: Union[int, Tuple[int, int]] = 1,829 dilation: Union[int, Tuple[int, int]] = 1,
626 groups: int = 1,830 groups: int = 1,
627- **kwargs831+ **kwargs,
628):832):
629 """833 """
630 Golden for torch.conv2d / torch.nn.functional.conv2d.834 Golden for torch.conv2d / torch.nn.functional.conv2d.
631 """835 """
632- if isinstance(padding, str) and padding == 'same':836+ if isinstance(padding, str) and padding == "same":
633 import torch.nn.functional as F837 import torch.nn.functional as F
634- input = to_float32(input) if isinstance(input, torch.Tensor) else torch.from_numpy(input.astype(np.float32))
635- weight = to_float32(weight) if isinstance(weight, torch.Tensor) else torch.from_numpy(weight.astype(np.float32))
636- if bias is not None:
637- bias = to_float32(bias) if isinstance(bias, torch.Tensor) else torch.from_numpy(bias.astype(np.float32))
638- return F.conv2d(input, weight, bias=bias, stride=stride,
639- padding='same', dilation=dilation, groups=groups)
640 838 
641- return aten_convolution_golden(input, weight, bias, stride, padding,839+ input = (
642- dilation, groups=groups, **kwargs)840+ to_float32(input)
841+ if isinstance(input, torch.Tensor)
842+ else torch.from_numpy(input.astype(np.float32))
843+ )
844+ weight = (
845+ to_float32(weight)
846+ if isinstance(weight, torch.Tensor)
847+ else torch.from_numpy(weight.astype(np.float32))
848+ )
849+ if bias is not None:
850+ bias = (
851+ to_float32(bias)
852+ if isinstance(bias, torch.Tensor)
853+ else torch.from_numpy(bias.astype(np.float32))
854+ )
855+ return F.conv2d(
856+ input,
857+ weight,
858+ bias=bias,
859+ stride=stride,
860+ padding="same",
861+ dilation=dilation,
862+ groups=groups,
863+ )
864+ 
865+ return aten_convolution_golden(
866+ input, weight, bias, stride, padding, dilation, groups=groups, **kwargs
867+ )
643 868 
644 869 
645def torch_conv3d_golden(870def torch_conv3d_golden(
@@ -650,22 +875,43 @@ def torch_conv3d_golden(
650 padding: Union[int, Tuple[int, int, int], str] = 0,875 padding: Union[int, Tuple[int, int, int], str] = 0,
651 dilation: Union[int, Tuple[int, int, int]] = 1,876 dilation: Union[int, Tuple[int, int, int]] = 1,
652 groups: int = 1,877 groups: int = 1,
653- **kwargs878+ **kwargs,
654):879):
655 """880 """
656 Golden for torch.conv3d / torch.nn.functional.conv3d.881 Golden for torch.conv3d / torch.nn.functional.conv3d.
657 """882 """
658- if isinstance(padding, str) and padding == 'same':883+ if isinstance(padding, str) and padding == "same":
659 import torch.nn.functional as F884 import torch.nn.functional as F
660- input = to_float32(input) if isinstance(input, torch.Tensor) else torch.from_numpy(input.astype(np.float32))
661- weight = to_float32(weight) if isinstance(weight, torch.Tensor) else torch.from_numpy(weight.astype(np.float32))
662- if bias is not None:
663- bias = to_float32(bias) if isinstance(bias, torch.Tensor) else torch.from_numpy(bias.astype(np.float32))
664- return F.conv3d(input, weight, bias=bias, stride=stride,
665- padding='same', dilation=dilation, groups=groups)
666 885 
667- return aten_convolution_golden(input, weight, bias, stride, padding,886+ input = (
668- dilation, groups=groups, **kwargs)887+ to_float32(input)
888+ if isinstance(input, torch.Tensor)
889+ else torch.from_numpy(input.astype(np.float32))
890+ )
891+ weight = (
892+ to_float32(weight)
893+ if isinstance(weight, torch.Tensor)
894+ else torch.from_numpy(weight.astype(np.float32))
895+ )
896+ if bias is not None:
897+ bias = (
898+ to_float32(bias)
899+ if isinstance(bias, torch.Tensor)
900+ else torch.from_numpy(bias.astype(np.float32))
901+ )
902+ return F.conv3d(
903+ input,
904+ weight,
905+ bias=bias,
906+ stride=stride,
907+ padding="same",
908+ dilation=dilation,
909+ groups=groups,
910+ )
911+ 
912+ return aten_convolution_golden(
913+ input, weight, bias, stride, padding, dilation, groups=groups, **kwargs
914+ )
669 915 
670 916 
671def aten_convolution_golden(917def aten_convolution_golden(
@@ -682,21 +928,58 @@ def aten_convolution_golden(
682 benchmark: bool = False,928 benchmark: bool = False,
683 deterministic: bool = False,929 deterministic: bool = False,
684 cudnn_allow_tf32: bool = True,930 cudnn_allow_tf32: bool = True,
685- **kwargs931+ enable_hf32: bool = False,
932+ **kwargs,
686):933):
687 """934 """
688 Golden for torch.ops.aten.convolution.935 Golden for torch.ops.aten.convolution.
689 Supports 1D, 2D, 3D convolutions.936 Supports 1D, 2D, 3D convolutions.
690 """937 """
691- 938+ 
692- input_shape = input.shape if isinstance(input, torch.Tensor) or hasattr(input, 'shape') else None939+ input_shape = (
693- weight_shape = weight.shape if isinstance(weight, torch.Tensor) or hasattr(weight, 'shape') else None940+ input.shape
941+ if isinstance(input, torch.Tensor) or hasattr(input, "shape")
942+ else None
943+ )
944+ weight_shape = (
945+ weight.shape
946+ if isinstance(weight, torch.Tensor) or hasattr(weight, "shape")
947+ else None
948+ )
694 conv_dim = get_conv_dim(input_shape, weight_shape)949 conv_dim = get_conv_dim(input_shape, weight_shape)
695 short_soc_version = kwargs.get("short_soc_version", None)950 short_soc_version = kwargs.get("short_soc_version", None)
696 951 
697- out = _compute_conv_forward(input, weight, bias, stride, padding,952+ if enable_hf32:
698- dilation, groups, conv_dim, transposed, output_padding, 953+ cubeMathType = 1
699- cubeMathType=cubeMathType,954+ 
700- short_soc_version=short_soc_version)955+ out = _compute_conv_forward(
701- 956+ input,
702- return out957+ weight,
958+ bias,
959+ stride,
960+ padding,
961+ dilation,
962+ groups,
963+ conv_dim,
964+ transposed,
965+ output_padding,
966+ cubeMathType=cubeMathType,
967+ short_soc_version=short_soc_version,
968+ )
969+ 
970+ input_dtype_str = (
971+ str(input.dtype).split(".")[-1]
972+ if isinstance(input, torch.Tensor)
973+ else (str(getattr(input, "dtype", "")))
974+ )
975+ if input_dtype_str == "bfloat16":
976+ try:
977+ import ml_dtypes
978+ 
979+ out = out.to(torch.float32).numpy().astype(ml_dtypes.bfloat16)
980+ except ImportError:
981+ out = out.to(torch.float32).numpy().astype(np.float32)
982+ elif input_dtype_str == "float16":
983+ out = out.to(torch.float16)
984+ 
985+ return out
Mconv/convolution_forward/tests/assets/input.py+51-35
@@ -2,15 +2,15 @@
2# -*- coding: UTF-8 -*-2# -*- coding: UTF-8 -*-
3# ----------------------------------------------------------------------------3# ----------------------------------------------------------------------------
4# Copyright (c) 2025-2026 Huawei Technologies Co., Ltd.4# Copyright (c) 2025-2026 Huawei Technologies Co., Ltd.
5-# This program is free software, you can redistribute it and/or modify it under terms and conditions of5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6# CANN Open Software License Agreement Version 2.0 (the "License").6# CANN Open Software License Agreement Version 2.0 (the "License").
7-# Please refer to License for details. You may not use this file except in compliance with License.7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,8# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.9# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10# See LICENSE in the root of the software repository for the full text of the License.10# See LICENSE in the root of the software repository for the full text of the License.
11# ----------------------------------------------------------------------------11# ----------------------------------------------------------------------------
12 12 
13-from typing import List, Optional, Tuple, Union13+from typing import List, Tuple, Union
14import numpy as np14import numpy as np
15import torch15import torch
16 16 
@@ -32,7 +32,7 @@ __input__ = {
32 "torch_npu.npu_conv3d": "torch_conv3d_input",32 "torch_npu.npu_conv3d": "torch_conv3d_input",
33 "torch_npu.npu_quant_conv2d": "torch_npu_quant_conv2d_input",33 "torch_npu.npu_quant_conv2d": "torch_npu_quant_conv2d_input",
34 "torch.ops.aten.convolution": "aten_convolution_input",34 "torch.ops.aten.convolution": "aten_convolution_input",
35- }35+ },
36}36}
37 37 
38 38 
@@ -48,33 +48,27 @@ def aclnn_convolution_input(
48 groups: int = 1,48 groups: int = 1,
49 output=None,49 output=None,
50 cubeMathType: int = 0,50 cubeMathType: int = 0,
51- **kwargs51+ **kwargs,
52):52):
53 """53 """
54 Input function for aclnnConvolution.54 Input function for aclnnConvolution.
55 Parameter names and order follow aclnn_convolution.h:55 Parameter names and order follow aclnn_convolution.h:
56 aclnnConvolutionGetWorkspaceSize(input, weight, bias, stride, padding, dilation,56 aclnnConvolutionGetWorkspaceSize(input, weight, bias, stride, padding, dilation,
57 transposed, outputPadding, groups, output, cubeMathType)57 transposed, outputPadding, groups, output, cubeMathType)
58- 58+ 
59 Supports 1D, 2D, 3D convolutions.59 Supports 1D, 2D, 3D convolutions.
60 """60 """
61 return [input, weight, bias]61 return [input, weight, bias]
62 62 
63 63 
64def aclnn_conv_tbc_input(64def aclnn_conv_tbc_input(
65- self,65+ self, weight, bias=None, pad: int = 0, output=None, cubeMathType: int = 0, **kwargs
66- weight,
67- bias=None,
68- pad: int = 0,
69- output=None,
70- cubeMathType: int = 0,
71- **kwargs
72):66):
73 """67 """
74 Input function for aclnnConvTbc.68 Input function for aclnnConvTbc.
75 Parameter names and order follow aclnn_convolution.h:69 Parameter names and order follow aclnn_convolution.h:
76 aclnnConvTbcGetWorkspaceSize(self, weight, bias, pad, output, cubeMathType)70 aclnnConvTbcGetWorkspaceSize(self, weight, bias, pad, output, cubeMathType)
77- 71+ 
78 TBC format: (T, B, C) where T is time/sequence, B is batch, C is channels.72 TBC format: (T, B, C) where T is time/sequence, B is batch, C is channels.
79 """73 """
80 return [self, weight, bias]74 return [self, weight, bias]
@@ -90,14 +84,14 @@ def aclnn_conv_depthwise2d_input(
90 dilation: Union[int, List[int]] = 1,84 dilation: Union[int, List[int]] = 1,
91 out=None,85 out=None,
92 cubeMathType: int = 0,86 cubeMathType: int = 0,
93- **kwargs87+ **kwargs,
94):88):
95 """89 """
96 Input function for aclnnConvDepthwise2d.90 Input function for aclnnConvDepthwise2d.
97 Parameter names and order follow aclnn_convolution.h:91 Parameter names and order follow aclnn_convolution.h:
98 aclnnConvDepthwise2dGetWorkspaceSize(self, weight, kernelSize, bias, stride,92 aclnnConvDepthwise2dGetWorkspaceSize(self, weight, kernelSize, bias, stride,
99 padding, dilation, out, cubeMathType)93 padding, dilation, out, cubeMathType)
100- 94+ 
101 Depthwise convolution where groups = input_channels.95 Depthwise convolution where groups = input_channels.
102 """96 """
103 return [self, weight, bias]97 return [self, weight, bias]
@@ -117,8 +111,8 @@ def aclnn_quant_conv2d_input(
117 outputPadding=0,111 outputPadding=0,
118 groups=1,112 groups=1,
119 offsetx=0,113 offsetx=0,
120- roundMode='',114+ roundMode="",
121- **kwargs115+ **kwargs,
122):116):
123 """117 """
124 Input function for aclnnQuantConvolution.118 Input function for aclnnQuantConvolution.
@@ -128,14 +122,22 @@ def aclnn_quant_conv2d_input(
128 outputPadding, groups, offsetx, roundMode)122 outputPadding, groups, offsetx, roundMode)
129 """123 """
130 if scale is not None and not isinstance(scale, (int, float)):124 if scale is not None and not isinstance(scale, (int, float)):
131- n_channels = scale.shape[0] if hasattr(scale, 'shape') else len(scale)125+ n_channels = scale.shape[0] if hasattr(scale, "shape") else len(scale)
132 scale_float = np.random.uniform(0.01, 1.0, size=n_channels).astype(np.float32)126 scale_float = np.random.uniform(0.01, 1.0, size=n_channels).astype(np.float32)
133- scale_float = np.bitwise_and(scale_float.view(np.uint32), 0xffffe000).view(np.float32)127+ scale_float = np.bitwise_and(scale_float.view(np.uint32), 0xFFFFE000).view(
128+ np.float32
129+ )
134 scale_encoded = torch.from_numpy(scale_float.view(np.uint32).astype(np.int64))130 scale_encoded = torch.from_numpy(scale_float.view(np.uint32).astype(np.int64))
135 if isinstance(scale, torch.Tensor):131 if isinstance(scale, torch.Tensor):
136- scale.copy_(scale_encoded)132+ if scale.dtype == torch.int64:
133+ scale.copy_(scale_encoded)
134+ else:
135+ scale.copy_(torch.from_numpy(scale_float))
137 elif isinstance(scale, np.ndarray):136 elif isinstance(scale, np.ndarray):
138- scale[:] = scale_encoded.numpy()137+ if scale.dtype == np.int64:
138+ scale[:] = scale_encoded.numpy()
139+ else:
140+ scale[:] = scale_float
139 else:141 else:
140 scale = scale_encoded142 scale = scale_encoded
141 143 
@@ -151,13 +153,13 @@ def torch_npu_quant_conv2d_input(
151 dilations=1,153 dilations=1,
152 groups=1,154 groups=1,
153 offset_x=0,155 offset_x=0,
154- round_mode='rint',156+ round_mode="rint",
155 output_dtype=None,157 output_dtype=None,
156 bias=None,158 bias=None,
157 offset=None,159 offset=None,
158 input_dtype=None,160 input_dtype=None,
159 weight_dtype=None,161 weight_dtype=None,
160- **kwargs162+ **kwargs,
161):163):
162 """164 """
163 Input function for torch_npu.npu_quant_conv2d.165 Input function for torch_npu.npu_quant_conv2d.
@@ -169,12 +171,18 @@ def torch_npu_quant_conv2d_input(
169 For float8/hifloat8 types, scale encoding is left as-is.171 For float8/hifloat8 types, scale encoding is left as-is.
170 """172 """
171 if scale is not None and not isinstance(scale, (int, float)):173 if scale is not None and not isinstance(scale, (int, float)):
172- x_dtype = str(x.dtype).split('.')[-1] if hasattr(x, 'dtype') else ''174+ x_dtype = str(x.dtype).split(".")[-1] if hasattr(x, "dtype") else ""
173- if x_dtype == 'int8':175+ if x_dtype == "int8":
174- n_channels = scale.shape[0] if hasattr(scale, 'shape') else len(scale)176+ n_channels = scale.shape[0] if hasattr(scale, "shape") else len(scale)
175- scale_float = np.random.uniform(0.01, 1.0, size=n_channels).astype(np.float32)177+ scale_float = np.random.uniform(0.01, 1.0, size=n_channels).astype(
176- scale_float = np.bitwise_and(scale_float.view(np.uint32), 0xffffe000).view(np.float32)178+ np.float32
177- scale_encoded = torch.from_numpy(scale_float.view(np.uint32).astype(np.int64))179+ )
180+ scale_float = np.bitwise_and(scale_float.view(np.uint32), 0xFFFFE000).view(
181+ np.float32
182+ )
183+ scale_encoded = torch.from_numpy(
184+ scale_float.view(np.uint32).astype(np.int64)
185+ )
178 if isinstance(scale, torch.Tensor):186 if isinstance(scale, torch.Tensor):
179 scale.copy_(scale_encoded)187 scale.copy_(scale_encoded)
180 elif isinstance(scale, np.ndarray):188 elif isinstance(scale, np.ndarray):
@@ -193,11 +201,19 @@ def torch_conv1d_input(
193 padding: int = 0,201 padding: int = 0,
194 dilation: int = 1,202 dilation: int = 1,
195 groups: int = 1,203 groups: int = 1,
196- **kwargs204+ enable_hf32: bool = False,
205+ **kwargs,
197):206):
198 """207 """
199 Input function for torch.conv1d / torch.nn.functional.conv1d.208 Input function for torch.conv1d / torch.nn.functional.conv1d.
200 """209 """
210+ if enable_hf32:
211+ try:
212+ import torch_npu
213+ 
214+ torch_npu.conv.allow_hf32 = True
215+ except (ImportError, AttributeError):
216+ pass
201 return [input, weight, bias]217 return [input, weight, bias]
202 218 
203 219 
@@ -209,7 +225,7 @@ def torch_conv2d_input(
209 padding: Union[int, Tuple[int, int], str] = 0,225 padding: Union[int, Tuple[int, int], str] = 0,
210 dilation: Union[int, Tuple[int, int]] = 1,226 dilation: Union[int, Tuple[int, int]] = 1,
211 groups: int = 1,227 groups: int = 1,
212- **kwargs228+ **kwargs,
213):229):
214 """230 """
215 Input function for torch.conv2d / torch.nn.functional.conv2d.231 Input function for torch.conv2d / torch.nn.functional.conv2d.
@@ -225,7 +241,7 @@ def torch_conv3d_input(
225 padding: Union[int, Tuple[int, int, int], str] = 0,241 padding: Union[int, Tuple[int, int, int], str] = 0,
226 dilation: Union[int, Tuple[int, int, int]] = 1,242 dilation: Union[int, Tuple[int, int, int]] = 1,
227 groups: int = 1,243 groups: int = 1,
228- **kwargs244+ **kwargs,
229):245):
230 """246 """
231 Input function for torch.conv3d / torch.nn.functional.conv3d.247 Input function for torch.conv3d / torch.nn.functional.conv3d.
@@ -246,10 +262,10 @@ def aten_convolution_input(
246 benchmark: bool = False,262 benchmark: bool = False,
247 deterministic: bool = False,263 deterministic: bool = False,
248 cudnn_allow_tf32: bool = True,264 cudnn_allow_tf32: bool = True,
249- **kwargs265+ **kwargs,
250):266):
251 """267 """
252 Input function for torch.ops.aten.convolution.268 Input function for torch.ops.aten.convolution.
253 Supports 1D, 2D, 3D convolutions based on input tensor dimensions.269 Supports 1D, 2D, 3D convolutions based on input tensor dimensions.
254 """270 """
255- return [input, weight, bias]271+ return [input, weight, bias]
Mconv/deformable_conv2d/tests/assets/golden.py+127-89
@@ -34,7 +34,10 @@ import torch
34__golden__ = {34__golden__ = {
35 "aclnn": {35 "aclnn": {
36 "aclnnDeformableConv2d": "deformable_conv2d_golden",36 "aclnnDeformableConv2d": "deformable_conv2d_golden",
37- }37+ },
38+ "e2e": {
39+ "torch_npu.npu_deformable_conv2d": "deformable_conv2d_golden",
40+ },
38}41}
39 42 
40FP32_STR = "float32"43FP32_STR = "float32"
@@ -71,24 +74,6 @@ def due_fp16_overflow(data):
71 return data74 return data
72 75 
73 76 
74-def simulate_hf32_precision(data, short_soc_version=None):
75- """
76- Simulate HF32 (Half Float 32) precision.
77- Ascend910B: truncates lower 12 bits of float32 mantissa, keeping 20 bits with rounding.
78- Default: truncates lower 13 bits of float32 mantissa, keeping 19 bits with rounding.
79- """
80- if data.dtype == np.float32:
81- input_hf32 = data.view(np.int32)
82- if short_soc_version in ("Ascend910B",):
83- input_hf32 = np.right_shift(np.right_shift(input_hf32, 11) + 1, 1)
84- input_hf32 = np.left_shift(input_hf32, 12)
85- else:
86- input_hf32 = np.right_shift(np.right_shift(input_hf32, 12) + 1, 1)
87- input_hf32 = np.left_shift(input_hf32, 13)
88- return input_hf32.view(np.float32)
89- return data
90- 
91- 
92def convert_output_dtype(out, output_dtype):77def convert_output_dtype(out, output_dtype):
93 dtype_map = {78 dtype_map = {
94 "float16": (np.float16, True),79 "float16": (np.float16, True),
@@ -107,10 +92,14 @@ def convert_output_dtype(out, output_dtype):
107 if isinstance(dtype_ref, str):92 if isinstance(dtype_ref, str):
108 module_name, dtype_name = dtype_ref.split(".")93 module_name, dtype_name = dtype_ref.split(".")
109 try:94 try:
110- dtype_cls = getattr(__import__(module_name, fromlist=[dtype_name]), dtype_name)95+ dtype_cls = getattr(
96+ __import__(module_name, fromlist=[dtype_name]), dtype_name
97+ )
111 except (ImportError, AttributeError):98 except (ImportError, AttributeError):
112- raise RuntimeError(f"{module_name} is required for {output_dtype}. "99+ raise RuntimeError(
113- f"Install: pip install {module_name}")100+ f"{module_name} is required for {output_dtype}. "
101+ f"Install: pip install {module_name}"
102+ )
114 out = out.astype(dtype_cls)103 out = out.astype(dtype_cls)
115 else:104 else:
116 out = out.astype(dtype_ref)105 out = out.astype(dtype_ref)
@@ -125,82 +114,109 @@ def bilinear_interpolate(input_tensor, h, w, c, batch_offset, in_h, in_w):
125 """114 """
126 h_floor = int(np.floor(h))115 h_floor = int(np.floor(h))
127 w_floor = int(np.floor(w))116 w_floor = int(np.floor(w))
128- 117+ 
129 h_delta = h - h_floor118 h_delta = h - h_floor
130 w_delta = w - w_floor119 w_delta = w - w_floor
131- 120+ 
132 def get_value(hi, wi):121 def get_value(hi, wi):
133 if hi >= 0 and wi >= 0 and hi < in_h and wi < in_w:122 if hi >= 0 and wi >= 0 and hi < in_h and wi < in_w:
134 return input_tensor[batch_offset + hi * in_w + wi]123 return input_tensor[batch_offset + hi * in_w + wi]
135 return 0.0124 return 0.0
136- 125+ 
137 top_left = get_value(h_floor, w_floor)126 top_left = get_value(h_floor, w_floor)
138 top_right = get_value(h_floor, w_floor + 1)127 top_right = get_value(h_floor, w_floor + 1)
139 bottom_left = get_value(h_floor + 1, w_floor)128 bottom_left = get_value(h_floor + 1, w_floor)
140 bottom_right = get_value(h_floor + 1, w_floor + 1)129 bottom_right = get_value(h_floor + 1, w_floor + 1)
141- 130+ 
142 top = top_left * (1 - w_delta) + top_right * w_delta131 top = top_left * (1 - w_delta) + top_right * w_delta
143 bottom = bottom_left * (1 - w_delta) + bottom_right * w_delta132 bottom = bottom_left * (1 - w_delta) + bottom_right * w_delta
144- 133+ 
145 return top * (1 - h_delta) + bottom * h_delta134 return top * (1 - h_delta) + bottom * h_delta
146 135 
147 136 
148-def compute_deform_out(x, offset, kernel_size, stride, padding, dilation, deformable_groups, modulated):137+def compute_deform_out(
138+ x, offset, kernel_size, stride, padding, dilation, deformable_groups, modulated
139+):
149 """140 """
150 Compute deformOut - the bilinear-interpolated input at offset positions.141 Compute deformOut - the bilinear-interpolated input at offset positions.
151 Output shape: (N, inC, outH * kH, outW * kW) in NCHW.142 Output shape: (N, inC, outH * kH, outW * kW) in NCHW.
152- 143+ 
153 Matches NPU kernel ComputeDeformableOffset in deformable_offsets.h.144 Matches NPU kernel ComputeDeformableOffset in deformable_offsets.h.
154 """145 """
155 N, inC, inH, inW = x.shape146 N, inC, inH, inW = x.shape
156 kH, kW = kernel_size[0], kernel_size[1] if len(kernel_size) >= 2 else kernel_size147 kH, kW = kernel_size[0], kernel_size[1] if len(kernel_size) >= 2 else kernel_size
157 stride_h, stride_w = stride[2], stride[3]148 stride_h, stride_w = stride[2], stride[3]
158- pad_h, pad_w = padding[0], padding[2]149+ pad_top, pad_bottom = padding[0], padding[1]
150+ pad_left, pad_right = padding[2], padding[3]
151+ pad_h = pad_top
152+ pad_w = pad_left
159 dilation_h, dilation_w = dilation[2], dilation[3]153 dilation_h, dilation_w = dilation[2], dilation[3]
160- 154+ 
161- outH = (inH + pad_h + pad_h - (kH - 1) * dilation_h - 1) // stride_h + 1155+ outH = (inH + pad_top + pad_bottom - (kH - 1) * dilation_h - 1) // stride_h + 1
162- outW = (inW + pad_w + pad_w - (kW - 1) * dilation_w - 1) // stride_w + 1156+ outW = (inW + pad_left + pad_right - (kW - 1) * dilation_w - 1) // stride_w + 1
163- 157+ 
164 out_shape = (N, inC, outH * kH, outW * kW)158 out_shape = (N, inC, outH * kH, outW * kW)
165 deformOut = np.zeros(out_shape, dtype=x.dtype)159 deformOut = np.zeros(out_shape, dtype=x.dtype)
166- 160+ 
167 k_per_group = inC // deformable_groups161 k_per_group = inC // deformable_groups
168- offset_channels_per_group = offset.shape[1] // deformable_groups
169 k_elems = kH * kW162 k_elems = kH * kW
170- 163+ 
171 for n in range(N):164 for n in range(N):
172 for out_h_pos in range(outH):165 for out_h_pos in range(outH):
173 for out_w_pos in range(outW):166 for out_w_pos in range(outW):
174 base_h = out_h_pos * stride_h - pad_h167 base_h = out_h_pos * stride_h - pad_h
175 base_w = out_w_pos * stride_w - pad_w168 base_w = out_w_pos * stride_w - pad_w
176- 169+ 
177 for dg in range(deformable_groups):170 for dg in range(deformable_groups):
178- offset_group_start = dg * offset_channels_per_group
179-
180 for c in range(k_per_group):171 for c in range(k_per_group):
181 channel_idx = dg * k_per_group + c172 channel_idx = dg * k_per_group + c
182- 173+ 
183 for ki in range(kH):174 for ki in range(kH):
184 for kj in range(kW):175 for kj in range(kW):
185 kernel_idx = ki * kW + kj176 kernel_idx = ki * kW + kj
186- 177+ 
187- offset_addr = offset_group_start + kernel_idx178+ w_offset_base = 0 * deformable_groups * k_elems
188- height_offset_val = offset[n, offset_addr + k_elems, out_h_pos, out_w_pos]179+ h_offset_base = 1 * deformable_groups * k_elems
189- width_offset_val = offset[n, offset_addr, out_h_pos, out_w_pos]180+ m_offset_base = 2 * deformable_groups * k_elems
190- mask_val = offset[n, offset_addr + 2 * k_elems, out_h_pos, out_w_pos] if modulated else 1.0181+ offset_in_group = dg * k_elems + kernel_idx
191- 182+ 
183+ height_offset_val = offset[
184+ n,
185+ h_offset_base + offset_in_group,
186+ out_h_pos,
187+ out_w_pos,
188+ ]
189+ width_offset_val = offset[
190+ n,
191+ w_offset_base + offset_in_group,
192+ out_h_pos,
193+ out_w_pos,
194+ ]
195+ mask_val = (
196+ offset[
197+ n,
198+ m_offset_base + offset_in_group,
199+ out_h_pos,
200+ out_w_pos,
201+ ]
202+ if modulated
203+ else 1.0
204+ )
205+ 
192 point_h = base_h + ki * dilation_h + height_offset_val206 point_h = base_h + ki * dilation_h + height_offset_val
193 point_w = base_w + kj * dilation_w + width_offset_val207 point_w = base_w + kj * dilation_w + width_offset_val
194-
195- batch_offset = n * inH * inW * inC + channel_idx
196 input_1d = x[n, channel_idx].flatten()208 input_1d = x[n, channel_idx].flatten()
197- 209+ 
198- bilinear_val = bilinear_interpolate(input_1d, point_h, point_w, channel_idx, 0, inH, inW)210+ bilinear_val = bilinear_interpolate(
199- 211+ input_1d, point_h, point_w, channel_idx, 0, inH, inW
212+ )
213+ 
200 output_h = out_h_pos * kH + ki214 output_h = out_h_pos * kH + ki
201 output_w = out_w_pos * kW + kj215 output_w = out_w_pos * kW + kj
202- deformOut[n, channel_idx, output_h, output_w] = bilinear_val * mask_val216+ deformOut[n, channel_idx, output_h, output_w] = (
203- 217+ bilinear_val * mask_val
218+ )
219+ 
204 return deformOut220 return deformOut
205 221 
206 222 
@@ -218,12 +234,12 @@ def deformable_conv2d_golden(
218 modulated: bool = True,234 modulated: bool = True,
219 out=None,235 out=None,
220 deform_out_optional=None,236 deform_out_optional=None,
221- **kwargs237+ **kwargs,
222):238):
223 """239 """
224 Golden for aclnnDeformableConv2d - returns BOTH outputs.240 Golden for aclnnDeformableConv2d - returns BOTH outputs.
225 Parameters follow aclnnDeformableConv2dGetWorkspaceSize C header signature order.241 Parameters follow aclnnDeformableConv2dGetWorkspaceSize C header signature order.
226- 242+ 
227 Args:243 Args:
228 x: (N, C, H, W) Tensor - NCHW244 x: (N, C, H, W) Tensor - NCHW
229 weight: (outC, inC/groups, kH, kW) Tensor245 weight: (outC, inC/groups, kH, kW) Tensor
@@ -238,24 +254,34 @@ def deformable_conv2d_golden(
238 modulated: Whether modulated (DCNv2)254 modulated: Whether modulated (DCNv2)
239 out: Pre-allocated output tensor (not used in golden)255 out: Pre-allocated output tensor (not used in golden)
240 deform_out_optional: Pre-allocated deform output tensor (not used in golden)256 deform_out_optional: Pre-allocated deform output tensor (not used in golden)
241- 257+ 
242 Returns:258 Returns:
243 tuple: (out, deformOut) where259 tuple: (out, deformOut) where
244 out: (N, outC, outH, outW) Tensor260 out: (N, outC, outH, outW) Tensor
245 deformOut: (N, inC, outH*kH, outW*kW) Tensor261 deformOut: (N, inC, outH*kH, outW*kW) Tensor
246- 262+ 
247 Note: Parameters are passed by position order from TTK, matching C header signature.263 Note: Parameters are passed by position order from TTK, matching C header signature.
248 Default values are for documentation purpose only.264 Default values are for documentation purpose only.
249 """265 """
250 bias = bias_optional266 bias = bias_optional
251- kernelSize = kernel_size267+ kernelSize = kernel_size if kernel_size is not None else kwargs.get("kernelSize")
252- deformableGroups = deformable_groups268+ deformableGroups = (
253- 269+ deformable_groups
270+ if deformable_groups != 1
271+ else kwargs.get("deformableGroups", deformable_groups)
272+ )
273+ stride = stride if stride != [1, 1, 1, 1] else kwargs.get("stride", stride)
274+ padding = padding if padding != [0, 0, 0, 0] else kwargs.get("padding", padding)
275+ dilation = (
276+ dilation if dilation != [1, 1, 1, 1] else kwargs.get("dilation", dilation)
277+ )
278+ groups = groups if groups != 1 else kwargs.get("groups", groups)
279+ modulated = modulated if modulated else kwargs.get("modulated", modulated)
280+ 
254 input_dtypes = kwargs.get("input_dtypes", None)281 input_dtypes = kwargs.get("input_dtypes", None)
255 tensor_dtypes = kwargs.get("tensor_dtypes", None)282 tensor_dtypes = kwargs.get("tensor_dtypes", None)
256 output_dtypes = kwargs.get("output_dtypes", [FP32_STR, FP32_STR])283 output_dtypes = kwargs.get("output_dtypes", [FP32_STR, FP32_STR])
257- short_soc_version = kwargs.get("short_soc_version", None)284+ 
258-
259 # Determine original input dtype from kwargs (not numpy array dtype)285 # Determine original input dtype from kwargs (not numpy array dtype)
260 # tensor_dtypes is the original dtype from CSV, e.g., ('bfloat16', 'bfloat16', ...)286 # tensor_dtypes is the original dtype from CSV, e.g., ('bfloat16', 'bfloat16', ...)
261 # input_dtypes might be None, so use tensor_dtypes as fallback287 # input_dtypes might be None, so use tensor_dtypes as fallback
@@ -265,7 +291,7 @@ def deformable_conv2d_golden(
265 original_dtype_str = str(input_dtypes[0])291 original_dtype_str = str(input_dtypes[0])
266 else:292 else:
267 original_dtype_str = FP32_STR293 original_dtype_str = FP32_STR
268- 294+ 
269 if isinstance(x, torch.Tensor):295 if isinstance(x, torch.Tensor):
270 x_np = tensor_to_numpy(x)296 x_np = tensor_to_numpy(x)
271 weight_np = tensor_to_numpy(weight)297 weight_np = tensor_to_numpy(weight)
@@ -276,78 +302,90 @@ def deformable_conv2d_golden(
276 weight_np = np.asarray(weight)302 weight_np = np.asarray(weight)
277 offset_np = np.asarray(offset)303 offset_np = np.asarray(offset)
278 bias_np = tensor_to_numpy(bias)304 bias_np = tensor_to_numpy(bias)
279- 305+ 
280 # Determine calculation precision based on original dtype306 # Determine calculation precision based on original dtype
281 # float32 uses float64 for higher precision golden calculation307 # float32 uses float64 for higher precision golden calculation
282 # float16/bfloat16 uses float32 (bfloat16 has ~7 bits precision, similar to float16)308 # float16/bfloat16 uses float32 (bfloat16 has ~7 bits precision, similar to float16)
283 if original_dtype_str == FP32_STR:309 if original_dtype_str == FP32_STR:
284 calc_dtype = np.float64310 calc_dtype = np.float64
285- elif original_dtype_str in ('float16', 'bfloat16'):311+ elif original_dtype_str in ("float16", "bfloat16"):
286 calc_dtype = np.float32312 calc_dtype = np.float32
287 else:313 else:
288 # Fallback: use numpy array dtype to decide314 # Fallback: use numpy array dtype to decide
289 x_dtype_str = x_np.dtype.name315 x_dtype_str = x_np.dtype.name
290 calc_dtype = np.float64 if x_dtype_str == FP32_STR else np.float32316 calc_dtype = np.float64 if x_dtype_str == FP32_STR else np.float32
291- 317+ 
292 x_np = x_np.astype(calc_dtype)318 x_np = x_np.astype(calc_dtype)
293 weight_np = weight_np.astype(calc_dtype)319 weight_np = weight_np.astype(calc_dtype)
294 offset_np = offset_np.astype(calc_dtype)320 offset_np = offset_np.astype(calc_dtype)
295 if bias_np is not None:321 if bias_np is not None:
296 bias_np = bias_np.astype(calc_dtype)322 bias_np = bias_np.astype(calc_dtype)
297- 323+ 
298 x_torch = torch.from_numpy(x_np)324 x_torch = torch.from_numpy(x_np)
299 weight_torch = torch.from_numpy(weight_np)325 weight_torch = torch.from_numpy(weight_np)
300 offset_torch = torch.from_numpy(offset_np)326 offset_torch = torch.from_numpy(offset_np)
301 bias_torch = torch.from_numpy(bias_np) if bias_np is not None else None327 bias_torch = torch.from_numpy(bias_np) if bias_np is not None else None
302- 328+ 
303 stride = normalize_param(stride, 1)329 stride = normalize_param(stride, 1)
304 padding = normalize_param(padding, 0)330 padding = normalize_param(padding, 0)
305 dilation = normalize_param(dilation, 1)331 dilation = normalize_param(dilation, 1)
306- 332+ 
307 if kernelSize is None:333 if kernelSize is None:
308 kernelSize = [weight_torch.shape[2], weight_torch.shape[3]]334 kernelSize = [weight_torch.shape[2], weight_torch.shape[3]]
309 elif isinstance(kernelSize, int):335 elif isinstance(kernelSize, int):
310 kernelSize = [kernelSize, kernelSize]336 kernelSize = [kernelSize, kernelSize]
311 elif isinstance(kernelSize, torch.Tensor):337 elif isinstance(kernelSize, torch.Tensor):
312- kernelSize = kernelSize.tolist() if kernelSize.numel() > 1 else [kernelSize.item(), kernelSize.item()]338+ kernelSize = (
313- 339+ kernelSize.tolist()
340+ if kernelSize.numel() > 1
341+ else [kernelSize.item(), kernelSize.item()]
342+ )
343+ 
314 # Convert groups/deformableGroups to int if they are Tensor344 # Convert groups/deformableGroups to int if they are Tensor
315 if isinstance(groups, torch.Tensor):345 if isinstance(groups, torch.Tensor):
316 groups = int(groups[0].item()) if groups.numel() > 1 else int(groups.item())346 groups = int(groups[0].item()) if groups.numel() > 1 else int(groups.item())
317 if isinstance(deformableGroups, torch.Tensor):347 if isinstance(deformableGroups, torch.Tensor):
318- deformableGroups = int(deformableGroups[0].item()) if deformableGroups.numel() > 1 else int(deformableGroups.item())348+ deformableGroups = (
319- 349+ int(deformableGroups[0].item())
320- stride_h, stride_w = stride[2], stride[3]350+ if deformableGroups.numel() > 1
321- pad_h, pad_w = padding[0], padding[2]351+ else int(deformableGroups.item())
322- dilation_h, dilation_w = dilation[2], dilation[3]352+ )
323- 353+ 
324 deformOut_np = compute_deform_out(354 deformOut_np = compute_deform_out(
325 x_torch.numpy(),355 x_torch.numpy(),
326 offset_torch.numpy(),356 offset_torch.numpy(),
327- kernelSize, stride, padding, dilation,357+ kernelSize,
328- deformableGroups, modulated358+ stride,
359+ padding,
360+ dilation,
361+ deformableGroups,
362+ modulated,
329 )363 )
330 deformOut_torch = torch.from_numpy(deformOut_np)364 deformOut_torch = torch.from_numpy(deformOut_np)
331- 365+ 
332 out = torch.nn.functional.conv2d(366 out = torch.nn.functional.conv2d(
333- deformOut_torch, weight_torch, bias_torch,367+ deformOut_torch,
368+ weight_torch,
369+ bias_torch,
334 stride=kernelSize,370 stride=kernelSize,
335 padding=0,371 padding=0,
336 dilation=1,372 dilation=1,
337- groups=groups373+ groups=groups,
338 )374 )
339- 375+ 
340 out_dtype = output_dtypes[0] if output_dtypes else FP32_STR376 out_dtype = output_dtypes[0] if output_dtypes else FP32_STR
341 deform_out_dtype = output_dtypes[1] if len(output_dtypes) > 1 else FP32_STR377 deform_out_dtype = output_dtypes[1] if len(output_dtypes) > 1 else FP32_STR
342- 378+ 
343 # If output_dtypes not provided, infer from tensor_dtypes (output tensors index 4, 5)379 # If output_dtypes not provided, infer from tensor_dtypes (output tensors index 4, 5)
344 if output_dtypes is None or output_dtypes == [FP32_STR, FP32_STR]:380 if output_dtypes is None or output_dtypes == [FP32_STR, FP32_STR]:
345 if tensor_dtypes and len(tensor_dtypes) >= 6:381 if tensor_dtypes and len(tensor_dtypes) >= 6:
346 # tensor_dtypes format: (input, weight, offset, bias, out_placeholder, deformOut_placeholder)382 # tensor_dtypes format: (input, weight, offset, bias, out_placeholder, deformOut_placeholder)
347 out_dtype = str(tensor_dtypes[4]) if tensor_dtypes[4] else FP32_STR383 out_dtype = str(tensor_dtypes[4]) if tensor_dtypes[4] else FP32_STR
348 deform_out_dtype = str(tensor_dtypes[5]) if tensor_dtypes[5] else FP32_STR384 deform_out_dtype = str(tensor_dtypes[5]) if tensor_dtypes[5] else FP32_STR
349- 385+ 
350 out_result = convert_output_dtype(out.detach().numpy(), out_dtype)386 out_result = convert_output_dtype(out.detach().numpy(), out_dtype)
351- deformOut_result = convert_output_dtype(deformOut_torch.detach().numpy(), deform_out_dtype)387+ deformOut_result = convert_output_dtype(
352- 388+ deformOut_torch.detach().numpy(), deform_out_dtype
353- return out_result, deformOut_result389+ )
390+ 
391+ return out_result, deformOut_result
Mconv/deformable_conv2d/tests/assets/input.py+94-35
@@ -2,32 +2,64 @@
2# -*- coding: UTF-8 -*-2# -*- coding: UTF-8 -*-
3# ----------------------------------------------------------------------------3# ----------------------------------------------------------------------------
4# Copyright (c) 2025-2026 Huawei Technologies Co., Ltd.4# Copyright (c) 2025-2026 Huawei Technologies Co., Ltd.
5-# This program is free software, you can redistribute it and/or modify it under terms and conditions of5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6# CANN Open Software License Agreement Version 2.0 (the "License").6# CANN Open Software License Agreement Version 2.0 (the "License").
7-# Please refer to License for details. You may not use this file except in compliance with License.7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,8# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9-# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FIT FOR A PARTICULAR PURPOSE.9+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10# See LICENSE in the root of the software repository for the full text of the License.10# See LICENSE in the root of the software repository for the full text of the License.
11# ----------------------------------------------------------------------------11# ----------------------------------------------------------------------------
12 12 
13import numpy as np13import numpy as np
14 14 
15__input__ = {15__input__ = {
16- "aclnn": {16+ "aclnn": {"aclnnDeformableConv2d": "deformable_conv2d_input"},
17- "aclnnDeformableConv2d": "deformable_conv2d_input"17+ "e2e": {"torch_npu.npu_deformable_conv2d": "deformable_conv2d_input"},
18- }
19}18}
20 19 
21 20 
22-def deformable_conv2d_input(x, weight, offset, bias=None,21+def _safe_uniform(shape, dtype, low=-1, high=1):
23- *, kernel_size, stride, padding, dilation,22+ if low is None:
24- groups=1, deformable_groups=1, modulated=True,23+ low = -1
25- **kwargs):24+ if high is None:
26- '''25+ high = 1
26+ import torch
27+ 
28+ if isinstance(dtype, torch.dtype):
29+ dtype_map = {
30+ torch.float16: np.float16,
31+ torch.float32: np.float32,
32+ torch.bfloat16: np.float32,
33+ torch.int64: np.int64,
34+ torch.int32: np.int32,
35+ torch.int8: np.int8,
36+ }
37+ dtype = dtype_map.get(dtype, np.float32)
38+ return np.random.uniform(low, high, shape).astype(dtype)
39+ 
40+ 
41+def deformable_conv2d_input(
42+ x,
43+ weight,
44+ offset,
45+ bias=None,
46+ kernel_size=None,
47+ stride=None,
48+ padding=None,
49+ dilation=None,
50+ groups=1,
51+ deformable_groups=1,
52+ modulated=True,
53+ out=None,
54+ deform_out=None,
55+ **kwargs,
56+):
57+ """
27 Input function for deformable_conv2d operator.58 Input function for deformable_conv2d operator.
28 All parameters follow @deformable_conv2d_def.cpp without outputs.59 All parameters follow @deformable_conv2d_def.cpp without outputs.
29 All input Tensors are numpy.ndarray.60 All input Tensors are numpy.ndarray.
30 61 
62+ 
31 Args:63 Args:
32 x: Input feature map tensor, shape (N, C, H, W) in NCHW format64 x: Input feature map tensor, shape (N, C, H, W) in NCHW format
33 weight: Convolution weight tensor, shape (outC, inC/groups, kH, kW)65 weight: Convolution weight tensor, shape (outC, inC/groups, kH, kW)
@@ -47,30 +79,57 @@ def deformable_conv2d_input(x, weight, offset, bias=None,
47 - input_ori_formats: List[str] - original input formats79 - input_ori_formats: List[str] - original input formats
48 - input_ranges: List[tuple] - input data ranges80 - input_ranges: List[tuple] - input data ranges
49 81 
82+ 
50 Returns:83 Returns:
51 List of input tensors: [x, weight, offset, bias]84 List of input tensors: [x, weight, offset, bias]
52- '''85+ """
53 # Get input ranges from kwargs86 # Get input ranges from kwargs
54- input_ranges = kwargs.get('input_ranges', [])87+ input_ranges = kwargs.get("input_ranges", [])
55- 88+ 
56- # Apply input ranges if available89+ if x is not None:
57- if input_ranges and len(input_ranges) >= 3:90+ r = (
58- x_range = input_ranges[0] if input_ranges[0] else (-10, 10)91+ input_ranges[0]
59- weight_range = input_ranges[1] if input_ranges[1] else (-1, 1)92+ if input_ranges and len(input_ranges) > 0 and input_ranges[0]
60- offset_range = input_ranges[2] if input_ranges[2] else (-1, 1)93+ else (-10, 10)
61- 94+ )
62- low, high = x_range95+ x = _safe_uniform(x.shape, x.dtype, r[0], r[1])
63- x = np.random.uniform(low, high, x.shape).astype(x.dtype)96+ 
64- 97+ if weight is not None:
65- low, high = weight_range98+ r = (
66- weight = np.random.uniform(low, high, weight.shape).astype(weight.dtype)99+ input_ranges[1]
67- 100+ if input_ranges and len(input_ranges) > 1 and input_ranges[1]
68- low, high = offset_range101+ else (-1, 1)
69- offset = np.random.uniform(low, high, offset.shape).astype(offset.dtype)102+ )
70- 103+ weight = _safe_uniform(weight.shape, weight.dtype, r[0], r[1])
71- if bias is not None and len(input_ranges) > 3:104+ 
72- bias_range = input_ranges[3] if input_ranges[3] else (-1, 1)105+ if offset is not None:
73- low, high = bias_range106+ r = (
74- bias = np.random.uniform(low, high, bias.shape).astype(bias.dtype)107+ input_ranges[2]
75- 108+ if input_ranges and len(input_ranges) > 2 and input_ranges[2]
76- return [x, weight, offset, bias]109+ else (-1, 1)
110+ )
111+ offset = _safe_uniform(offset.shape, offset.dtype, r[0], r[1])
112+ 
113+ if bias is not None:
114+ r = (
115+ input_ranges[3]
116+ if input_ranges and len(input_ranges) > 3 and input_ranges[3]
117+ else (-1, 1)
118+ )
119+ bias = _safe_uniform(bias.shape, bias.dtype, r[0], r[1])
120+ 
121+ return [
122+ x,
123+ weight,
124+ offset,
125+ bias,
126+ kernel_size,
127+ stride,
128+ padding,
129+ dilation,
130+ groups,
131+ deformable_groups,
132+ modulated,
133+ out,
134+ deform_out,
135+ ]