已合并
fix(st): 修正 pad、pad_v3 测试用例 paddings dtype 为 int32 #3690
yefeicoding创建于 7月1日
fix(st): 修正 pad、pad_v3 测试用例 paddings dtype 为 int32 #3690
已合并
yefeicoding创建于 7月1日
8 个文件变更+659-0
@@ -0,0 +1,146 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# ----------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# 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").
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,
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.
11+# ----------------------------------------------------------------------------
12+ 
13+import numpy as np
14+ 
15+ 
16+__golden__ = {
17+ "kernel": {
18+ "circular_pad_grad": "circular_pad_grad_golden"
19+ }
20+}
21+ 
22+ 
23+def _numpy_bfloat16():
24+ try:
25+ from ml_dtypes import bfloat16
26+ except ModuleNotFoundError:
27+ try:
28+ import tensorflow
29+ bfloat16 = tensorflow.bfloat16.as_numpy_dtype
30+ except ModuleNotFoundError:
31+ raise RuntimeError("ml-dtypes or tensorflow is needed to support bfloat16 dtype!!! "
32+ "Please install with `pip3 install ml-dtypes` or `pip3 install tensorflow`")
33+ return bfloat16
34+ 
35+ 
36+def _numpy_to_torch_tensor(np_array):
37+ import torch
38+ if np_array is None:
39+ return None
40+ np_dtype = np_array.dtype.name
41+ if "bfloat16" in np_dtype:
42+ np_int16 = np_array.view(dtype=np.int16)
43+ t_int16 = torch.from_numpy(np_int16)
44+ return t_int16.view(torch.bfloat16)
45+ else:
46+ return torch.from_numpy(np_array)
47+ 
48+ 
49+def _torch_to_numpy_tensor(torch_tensor):
50+ import torch
51+ if torch_tensor is None:
52+ return None
53+ if not isinstance(torch_tensor, torch.Tensor):
54+ raise RuntimeError(f"Only support torch.Tensor. But got {type(torch_tensor)}")
55+ torch_dtype = torch_tensor.dtype
56+ if torch_dtype == torch.bfloat16:
57+ t_int16 = torch_tensor.view(torch.int16)
58+ np_int16 = t_int16.numpy()
59+ return np_int16.view(dtype=_numpy_bfloat16())
60+ else:
61+ return torch_tensor.numpy()
62+ 
63+ 
64+def _cal_out_shape(in_shape, paddings):
65+ out_shape = []
66+ offset = len(in_shape) - len(paddings)
67+ for i in range(len(in_shape)):
68+ if i < len(in_shape) - len(paddings):
69+ out_shape.append(in_shape[i])
70+ else:
71+ out_shape.append(in_shape[i] - paddings[i - offset][0] - paddings[i - offset][1])
72+ return out_shape
73+ 
74+ 
75+def _torch_direct_invoke_circular(grad_output_np, y_shape_list, pad_temp):
76+ import torch
77+ 
78+ pad = []
79+ for dim in reversed(pad_temp):
80+ pad.append(dim[0])
81+ pad.append(dim[1])
82+ 
83+ origin_dtype = grad_output_np.dtype.name
84+ if grad_output_np.dtype.name == "bfloat16":
85+ grad_output = _numpy_to_torch_tensor(grad_output_np).to(torch.float32)
86+ else:
87+ grad_output = _numpy_to_torch_tensor(grad_output_np)
88+ x = torch.zeros(y_shape_list, dtype=grad_output.dtype)
89+ 
90+ grad_output = grad_output.unsqueeze(0)
91+ x = x.unsqueeze(0)
92+ 
93+ x.requires_grad_(True)
94+ out = torch.nn.functional.pad(x, pad, "circular")
95+ 
96+ loss = (grad_output * out).sum()
97+ loss.backward()
98+ 
99+ golden = x.grad
100+ 
101+ golden.squeeze(0)
atomgit-bot
atomgit-botatomgit-bot7月1日

🟠 High Priority

变更位置:conversion/circular_pad_grad/tests/assets/golden.py 第 101 行,函数 _torch_direct_invoke_circular 内部。

问题链:

  1. 第 90-91 行:grad_output.unsqueeze(0)x.unsqueeze(0) 为 tensor 增加了 batch 维度(size=1)。
  2. 第 101 行:golden.squeeze(0) 意图移除该 batch 维度,使输出 shape 恢复为 y_shape_list
  3. PyTorch 的 Tensor.squeeze(dim) 返回的是新的 tensor,不会原地修改 —— 原地版本是 squeeze_(dim)
  4. 返回值被丢弃,golden 仍然保持 (1, *y_shape_list) 的 shape。

触发条件:circular_pad_grad_golden 第 142 行 if grad_output.ndim <= 3 分支(低维输入)走 _torch_direct_invoke_circular 路径时。

失败模式:返回的 golden 值多一个 size=1 的维度,与框架预期的 output shape 不匹配,导致测试比较失败。

注:该 bug 模式同样存在于已有文件 conversion/pad_v3_grad/tests/assets/golden.py 第 164 行,该文件不在本次 diff 范围内,但新文件复制了同样的错误。

建议:将 golden.squeeze(0) 改为 golden = golden.squeeze(0),使 squeeze 返回的新 tensor 被正确赋值回 golden 变量。

改动建议
101
- golden.squeeze(0)
101
+ golden = golden.squeeze(0)
应用建议
likedislike
102+ if origin_dtype == "bfloat16":
103+ golden = _torch_to_numpy_tensor(golden.to(torch.bfloat16))
104+ else:
105+ golden = golden.numpy()
106+ return golden
107+ 
108+ 
109+def _numpy_pad_v3_grad_circular(grad_output, input_shape, pad):
110+ dim = len(input_shape)
111+ 
112+ true_dtype = grad_output.dtype
113+ grad_output = np.array(grad_output, dtype=np.float32)
114+ grad_input = np.zeros(input_shape, dtype=np.float32)
115+ for idx_out in np.ndindex(grad_output.shape):
116+ idx_in = tuple(
117+ (idx_out[d] - pad[d][0]) % input_shape[d] for d in range(dim)
118+ )
119+ grad_input[idx_in] += grad_output[idx_out]
120+ grad_input = np.array(grad_input, dtype=true_dtype)
121+ return grad_input
122+ 
123+ 
124+def circular_pad_grad_golden(x, paddings, **kwargs):
125+ '''
126+ Kernel golden for circular_pad_grad.
127+ All the parameters follow @circular_pad_grad_def.cpp without outputs.
128+ All the input Tensors are numpy.ndarray.
129+ kwargs may contain: short_soc_version, input_ori_shapes, output_ori_shapes,
130+ input_formats, output_formats, input_ori_formats, output_ori_formats,
131+ input_dtypes, output_dtypes.
132+ '''
133+ grad_output = x
134+ 
135+ paddings_arr = np.array(paddings).astype(np.int64)
136+ if paddings_arr.ndim == 1:
137+ paddings_arr = paddings_arr.reshape(-1, 2)
138+ pad_shape = paddings_arr.tolist()
139+ 
140+ y_shape = _cal_out_shape(grad_output.shape, pad_shape)
141+ 
142+ if grad_output.ndim <= 3:
143+ grad = _torch_direct_invoke_circular(grad_output, y_shape, pad_shape)
144+ else:
145+ grad = _numpy_pad_v3_grad_circular(grad_output, y_shape, pad_shape)
146+ return grad
@@ -0,0 +1,130 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# ----------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# 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").
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,
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.
11+# ----------------------------------------------------------------------------
12+ 
13+import numpy as np
14+ 
15+ 
16+__golden__ = {
17+ "kernel": {
18+ "mirror_pad": "mirror_pad_golden"
19+ }
20+}
21+ 
22+ 
23+def _numpy_bfloat16():
24+ try:
25+ from ml_dtypes import bfloat16
26+ except ModuleNotFoundError:
27+ try:
28+ import tensorflow
29+ bfloat16 = tensorflow.bfloat16.as_numpy_dtype
30+ except ModuleNotFoundError:
31+ raise RuntimeError("ml-dtypes or tensorflow is needed to support bfloat16 dtype!!! "
32+ "Please install with `pip3 install ml-dtypes` or `pip3 install tensorflow`")
33+ return bfloat16
34+ 
35+ 
36+def _numpy_to_torch_tensor(np_array):
37+ import torch
38+ if np_array is None:
39+ return None
40+ np_dtype = np_array.dtype.name
41+ if "bfloat16" in np_dtype:
42+ np_int16 = np_array.view(dtype=np.int16)
43+ t_int16 = torch.from_numpy(np_int16)
44+ return t_int16.view(torch.bfloat16)
45+ else:
46+ return torch.from_numpy(np_array)
47+ 
48+ 
49+def _torch_to_numpy_tensor(torch_tensor):
50+ import torch
51+ if torch_tensor is None:
52+ return None
53+ if not isinstance(torch_tensor, torch.Tensor):
54+ raise RuntimeError(f"Only support torch.Tensor. But got {type(torch_tensor)}")
55+ torch_dtype = torch_tensor.dtype
56+ if torch_dtype == torch.bfloat16:
57+ t_int16 = torch_tensor.view(torch.int16)
58+ np_int16 = t_int16.numpy()
59+ return np_int16.view(dtype=_numpy_bfloat16())
60+ else:
61+ return torch_tensor.numpy()
62+ 
63+ 
64+def _pad_and_slice(arr, pad_width, mode='reflect'):
65+ pad_width = tuple(pad_width)
66+ if len(pad_width) != arr.ndim:
67+ raise ValueError(f"pad_width length ({len(pad_width)}) must match array ndim ({arr.ndim})")
68+ 
69+ result = arr
70+ 
71+ for i, (left, right) in enumerate(pad_width):
72+ if left == 0 and right == 0:
73+ continue
74+ 
75+ if left > 0 or right > 0:
76+ pad_tuple = [(0, 0)] * arr.ndim
77+ lv = left if left > 0 else 0
78+ rv = right if right > 0 else 0
79+ pad_tuple[i] = (lv, rv)
80+ result = np.pad(result, pad_tuple, mode=mode)
81+ 
82+ if left < 0 or right < 0:
83+ crop_left = abs(left) if left < 0 else 0
84+ crop_right = abs(right) if right < 0 else 0
85+ slice_start = crop_left
86+ slice_end = -crop_right if crop_right > 0 else None
87+ slices = [slice(None)] * arr.ndim
88+ slices[i] = slice(slice_start, slice_end)
89+ result = result[tuple(slices)]
90+ 
91+ return result
92+ 
93+ 
94+def mirror_pad_golden(x, paddings, mode="REFLECT", **kwargs):
95+ '''
96+ Kernel golden for mirror_pad.
97+ All the parameters follow @mirror_pad_def.cpp without outputs.
98+ All the input Tensors are numpy.ndarray.
99+ kwargs may contain: short_soc_version, input_ori_shapes, output_ori_shapes,
100+ input_formats, output_formats, input_ori_formats, output_ori_formats,
101+ input_dtypes, output_dtypes.
102+ '''
103+ import torch
104+ 
105+ pad_shape = np.reshape(paddings, (len(x.shape), 2))
106+ if mode == 'REFLECT' or mode == 'SYMMETRIC':
107+ if mode == 'SYMMETRIC':
108+ np_mode = 'symmetric'
109+ else:
110+ np_mode = 'reflect'
111+ 
112+ orig_dtype = x.dtype
113+ if orig_dtype.name == "bfloat16":
114+ x = _numpy_to_torch_tensor(x).to(torch.float32).numpy()
115+ 
116+ neg_pad = _numpy_to_torch_tensor(np.array(pad_shape))
117+ neg_mask = neg_pad < 0
118+ indices = torch.nonzero(neg_mask, as_tuple=False)
119+ 
120+ if indices.size(0) == 0:
121+ golden = np.pad(x, pad_shape, mode=np_mode)
122+ else:
123+ golden = _pad_and_slice(x, pad_shape, mode=np_mode)
124+ 
125+ if orig_dtype.name == "bfloat16":
126+ golden = _torch_to_numpy_tensor(_numpy_to_torch_tensor(golden).to(torch.bfloat16))
127+ else:
128+ raise ValueError(f"Unsupported pad mode: {mode}")
129+ 
130+ return golden
@@ -0,0 +1,6 @@
1+testcase_name,network_name,op_name,input_dtypes,input_ori_shapes,output_ori_shapes,input_ori_formats,output_ori_formats,attributes,input_shapes,output_dtypes,output_shapes,input_formats,output_formats,input_data_ranges,precision_tolerances,absolute_precision,output_inplace_indexes,output_shape_unknown_indexes,is_enabled,remark,soc_series,priority,dump_file_prefix,manual_input_binaries,manual_golden_binaries
2+mirror_pad_daily_ID0000_0174,UNKNOWN,mirror_pad,"('float16', 'int32')","((2, 19, 1, 39), (4, 2))","((3, 39, 1, 109),)","('ND', 'ND')","('ND',)","{'paddings': [[0, 1], [10, 10], [0, 0], [35, 35]], 'mode': 'REFLECT'}","((2, 19, 1, 39), (4, 2))","('float16',)","((3, 39, 1, 109),)","('ND', 'ND')","('ND',)","((None, None),)",,1e-08,(),(),True,,,10,,,
3+mirror_pad_daily_ID0000_0244,UNKNOWN,mirror_pad,"('float32', 'int32')","((64, 40, 8), (3, 2))","((110, 50, 14),)","('ND', 'ND')","('ND',)","{'paddings': [22, 24, 0, 10, 6, 0], 'mode': 'REFLECT'}","((64, 40, 8), (3, 2))","('float32',)","((110, 50, 14),)","('ND', 'ND')","('ND',)","((None, None),)",,1e-08,(),(),True,,,10,,,
4+mirror_pad_daily_ID0000_0208,UNKNOWN,mirror_pad,"('int16', 'int64')","((192,), (1, 2))","((465,),)","('ND', 'ND')","('ND',)","{'paddings': [179, 94], 'mode': 'REFLECT'}","((192,), (1, 2))","('int16',)","((465,),)","('ND', 'ND')","('ND',)","((None, None),)",,1e-08,(),(),True,,,10,,,
5+mirror_pad_daily_ID0000_0310,UNKNOWN,mirror_pad,"('float32', 'int64')","((181, 17, 6, 13, 4), (5, 2))","((289, 21, 10, 25, 8),)","('ND', 'ND')","('ND',)","{'paddings': [8, 100, 3, 1, 2, 2, 2, 10, 2, 2], 'mode': 'REFLECT'}","((181, 17, 6, 13, 4), (5, 2))","('float32',)","((289, 21, 10, 25, 8),)","('ND', 'ND')","('ND',)","((None, None),)",,1e-08,(),(),True,,,10,,,
6+mirror_pad_daily_ID0000_0277,UNKNOWN,mirror_pad,"('int64', 'int32')","((128, 128, 12, 48), (4, 2))","((268, 176, 21, 70),)","('ND', 'ND')","('ND',)","{'paddings': [93, 47, 1, 13, 6, 3, 0, 22], 'mode': 'SYMMETRIC'}","((128, 128, 12, 48), (4, 2))","('int64',)","((268, 176, 21, 70),)","('ND', 'ND')","('ND',)","((None, None),)",,1e-08,(),(),True,,,0,,,
@@ -0,0 +1,100 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# ----------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# 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").
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,
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.
11+# ----------------------------------------------------------------------------
12+ 
13+import numpy as np
14+ 
15+ 
16+__golden__ = {
17+ "kernel": {
18+ "pad": "pad_golden"
19+ }
20+}
21+ 
22+ 
23+def _numpy_bfloat16():
24+ try:
25+ from ml_dtypes import bfloat16
26+ except ModuleNotFoundError:
27+ try:
28+ import tensorflow
29+ bfloat16 = tensorflow.bfloat16.as_numpy_dtype
30+ except ModuleNotFoundError:
31+ raise RuntimeError("ml-dtypes or tensorflow is needed to support bfloat16 dtype!!! "
32+ "Please install with `pip3 install ml-dtypes` or `pip3 install tensorflow`")
33+ return bfloat16
34+ 
35+ 
36+def _numpy_to_torch_tensor(np_array):
37+ import torch
38+ if np_array is None:
39+ return None
40+ np_dtype = np_array.dtype.name
41+ if "bfloat16" in np_dtype:
42+ np_int16 = np_array.view(dtype=np.int16)
43+ t_int16 = torch.from_numpy(np_int16)
44+ return t_int16.view(torch.bfloat16)
45+ else:
46+ return torch.from_numpy(np_array)
47+ 
48+ 
49+def _torch_to_numpy_tensor(torch_tensor):
50+ import torch
51+ if torch_tensor is None:
52+ return None
53+ if not isinstance(torch_tensor, torch.Tensor):
54+ raise RuntimeError(f"Only support torch.Tensor. But got {type(torch_tensor)}")
55+ torch_dtype = torch_tensor.dtype
56+ if torch_dtype == torch.bfloat16:
57+ t_int16 = torch_tensor.view(torch.int16)
58+ np_int16 = t_int16.numpy()
59+ return np_int16.view(dtype=_numpy_bfloat16())
60+ else:
61+ return torch_tensor.numpy()
62+ 
63+ 
64+def pad_golden(x, paddings, **kwargs):
65+ '''
66+ Kernel golden for pad.
67+ All the parameters follow @pad_def.cpp without outputs.
68+ All the input Tensors are numpy.ndarray.
69+ kwargs may contain: short_soc_version, input_ori_shapes, output_ori_shapes,
70+ input_formats, output_formats, input_ori_formats, output_ori_formats,
71+ input_dtypes, output_dtypes.
72+ '''
73+ import torch
74+ 
75+ dtypes = {'uint8': 'int8', 'uint16': 'int16', 'uint32': 'int32', 'uint64': 'int64'}
76+ 
77+ if x.dtype.name in dtypes.keys():
78+ x_tensor = _numpy_to_torch_tensor(x.view(dtypes[x.dtype.name]))
79+ elif x.dtype.name == "bfloat16":
80+ x_tensor = _numpy_to_torch_tensor(x).to(torch.float32)
81+ else:
82+ x_tensor = _numpy_to_torch_tensor(x)
83+ 
84+ paddings_arr = np.array(paddings).astype(np.int64)
85+ if paddings_arr.ndim == 1:
86+ paddings_arr = paddings_arr.reshape(-1, 2)
87+ torch_paddings = []
88+ for dim in reversed(paddings_arr):
89+ torch_paddings.extend([dim[0], dim[1]])
90+ 
91+ golden = torch.nn.functional.pad(x_tensor, torch_paddings)
92+ 
93+ if x.dtype.name in dtypes.keys():
94+ golden = golden.numpy().view(x.dtype)
95+ elif x.dtype.name == "bfloat16":
96+ golden = _torch_to_numpy_tensor(golden.to(torch.bfloat16))
97+ else:
98+ golden = golden.numpy()
99+ 
100+ return golden
@@ -0,0 +1,6 @@
1+testcase_name,network_name,op_name,input_dtypes,input_ori_shapes,output_ori_shapes,input_ori_formats,output_ori_formats,attributes,input_shapes,output_dtypes,output_shapes,input_formats,output_formats,input_data_ranges,precision_tolerances,absolute_precision,output_inplace_indexes,output_shape_unknown_indexes,is_enabled,remark,soc_series,priority,dump_file_prefix,manual_input_binaries,manual_golden_binaries
2+Pad_WhiteBox_0_key_int8_ND_000009,UNKNOWN,pad,"('int8', 'int32')","((890, 6771),)","((1136, 15501),)","('ND', 'ND')","('ND',)","{'paddings': [[128, 118], [4236, 4494]]}","((890, 6771), (2, 2))","('int8',)","((1136, 15501),)","('ND', 'ND')","('ND',)","((1, 2), (1, 2))","((0.001, 0.001),)",1e-08,(),(),True,,,10,,(),()
3+Pad_RandomFuzz_int64_ND_000160,UNKNOWN,pad,"('int64', 'int64')","((1, 1, 1, 1),)","((1, 2, 1, 1),)","('ND', 'ND')","('ND',)","{'paddings': [[0, 0], [1, 0], [0, 0], [0, 0]]}","((1, 1, 1, 1), (4, 2))","('int64',)","((1, 2, 1, 1),)","('ND', 'ND')","('ND',)","((0, 1), (0, 1))","((0.001, 0.001),)",1e-08,(),(),True,,,0,,(),()
4+Pad_RandomFuzz_float32_ND_000147,UNKNOWN,pad,"('float32', 'int32')","((43, 1),)","((103, 3),)","('ND', 'ND')","('ND',)","{'paddings': [[31, 29], [1, 1]]}","((43, 1), (2, 2))","('float32',)","((103, 3),)","('ND', 'ND')","('ND',)","((-1, -0.01), (-1, -0.01))","((0.0001, 0.0001),)",1e-08,(),(),True,,,10,,(),()
5+Pad_RandomFuzz_float16_ND_000095,UNKNOWN,pad,"('float16', 'int32')","((3576,),)","((7382,),)","('ND', 'ND')","('ND',)","{'paddings': [[3483, 323]]}","((3576,), (2,))","('float16',)","((7382,),)","('ND', 'ND')","('ND',)","((2, 10), (2, 10))","((0.001, 0.001),)",1e-08,(),(),True,,,0,,(),()
6+Pad_RandomFuzz_int8_ND_000095,UNKNOWN,pad,"('int8', 'int32')","((3576,),)","((7382,),)","('ND', 'ND')","('ND',)","{'paddings': [[3483, 323]]}","((3576,), (2,))","('int8',)","((7382,),)","('ND', 'ND')","('ND',)","((2, 10), (2, 10))","((0.001, 0.001),)",1e-08,(),(),True,,,10,,(),()
@@ -0,0 +1,178 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# ----------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# 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").
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,
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.
11+# ----------------------------------------------------------------------------
12+ 
13+import numpy as np
14+from collections import deque
15+ 
16+ 
17+__golden__ = {
18+ "kernel": {
19+ "pad_v3": "pad_v3_golden"
20+ }
21+}
22+ 
23+ 
24+def _numpy_bfloat16():
25+ try:
26+ from ml_dtypes import bfloat16
27+ except ModuleNotFoundError:
28+ try:
29+ import tensorflow
30+ bfloat16 = tensorflow.bfloat16.as_numpy_dtype
31+ except ModuleNotFoundError:
32+ raise RuntimeError("ml-dtypes or tensorflow is needed to support bfloat16 dtype!!! "
33+ "Please install with `pip3 install ml-dtypes` or `pip3 install tensorflow`")
34+ return bfloat16
35+ 
36+ 
37+def _numpy_to_torch_tensor(np_array):
38+ import torch
39+ if np_array is None:
40+ return None
41+ np_dtype = np_array.dtype.name
42+ if "bfloat16" in np_dtype:
43+ np_int16 = np_array.view(dtype=np.int16)
44+ t_int16 = torch.from_numpy(np_int16)
45+ return t_int16.view(torch.bfloat16)
46+ else:
47+ return torch.from_numpy(np_array)
48+ 
49+ 
50+def _torch_to_numpy_tensor(torch_tensor):
51+ import torch
52+ if torch_tensor is None:
53+ return None
54+ if not isinstance(torch_tensor, torch.Tensor):
55+ raise RuntimeError(f"Only support torch.Tensor. But got {type(torch_tensor)}")
56+ torch_dtype = torch_tensor.dtype
57+ if torch_dtype == torch.bfloat16:
58+ t_int16 = torch_tensor.view(torch.int16)
59+ np_int16 = t_int16.numpy()
60+ return np_int16.view(dtype=_numpy_bfloat16())
61+ else:
62+ return torch_tensor.numpy()
63+ 
64+ 
65+def _pad_and_slice(arr, pad_width, mode='edge'):
66+ pad_width = tuple(pad_width)
67+ if len(pad_width) != arr.ndim:
68+ raise ValueError(f"pad_width length ({len(pad_width)}) must match array ndim ({arr.ndim})")
69+ 
70+ result = arr
71+ 
72+ for i, (left, right) in enumerate(pad_width):
73+ if left == 0 and right == 0:
74+ continue
75+ 
76+ if left > 0 or right > 0:
77+ pad_tuple = [(0, 0)] * arr.ndim
78+ lv = left if left > 0 else 0
79+ rv = right if right > 0 else 0
80+ pad_tuple[i] = (lv, rv)
81+ result = np.pad(result, pad_tuple, mode=mode)
82+ 
83+ if left < 0 or right < 0:
84+ crop_left = abs(left) if left < 0 else 0
85+ crop_right = abs(right) if right < 0 else 0
86+ slice_start = crop_left
87+ slice_end = -crop_right if crop_right > 0 else None
88+ slices = [slice(None)] * arr.ndim
89+ slices[i] = slice(slice_start, slice_end)
90+ result = result[tuple(slices)]
91+ 
92+ return result
93+ 
94+ 
95+def pad_v3_golden(x, paddings, constant_values=None, mode="constant", paddings_contiguous=True, **kwargs):
96+ '''
97+ Kernel golden for pad_v3.
98+ All the parameters follow @pad_v3_def.cpp without outputs.
99+ All the input Tensors are numpy.ndarray.
100+ kwargs may contain: short_soc_version, input_ori_shapes, output_ori_shapes,
101+ input_formats, output_formats, input_ori_formats, output_ori_formats,
102+ input_dtypes, output_dtypes.
103+ '''
104+ import torch
105+ 
106+ input_formats = kwargs.get('input_formats', ())
107+ x_format = input_formats[0] if input_formats and len(input_formats) > 0 else 'ND'
108+ 
109+ paddings = np.array(paddings).astype(np.int64)
110+ if paddings.ndim == 1:
111+ if paddings_contiguous:
112+ paddings = paddings.reshape(-1, 2)
113+ else:
114+ paddings = paddings.reshape(2, -1)
115+ 
116+ if mode == 'constant':
117+ pad_shape = deque()
118+ if paddings_contiguous == True:
119+ for i in range(len(paddings)):
120+ pad_shape.append(paddings[len(paddings) - 1 - i][0])
121+ pad_shape.append(paddings[len(paddings) - 1 - i][1])
122+ else:
123+ for i in range(len(paddings[0])):
124+ pad_shape.append(paddings[0][len(paddings[0]) - 1 - i])
125+ pad_shape.append(paddings[1][len(paddings[1]) - 1 - i])
126+ 
127+ if x_format == "NC1HWC0":
128+ pad_shape.appendleft(0)
129+ pad_shape.appendleft(0)
130+ 
131+ dtypes = {'uint8': 'int8', 'uint16': 'int16', 'uint32': 'int32', 'uint64': 'int64'}
132+ 
133+ if x.dtype.name in dtypes.keys():
134+ x_tensor = _numpy_to_torch_tensor(x.view(dtypes[x.dtype.name]))
135+ elif x.dtype.name == "bfloat16":
136+ x_tensor = _numpy_to_torch_tensor(x).to(torch.float32)
137+ else:
138+ x_tensor = _numpy_to_torch_tensor(x)
139+ 
140+ if constant_values is not None:
141+ const_val = constant_values.item() if isinstance(constant_values, np.ndarray) else constant_values
142+ else:
143+ const_val = 0
144+ golden = torch.constant_pad_nd(x_tensor, tuple(pad_shape), const_val)
145+ 
146+ if x.dtype.name in dtypes.keys():
147+ golden = golden.numpy().view(x.dtype)
148+ elif x.dtype.name == "bfloat16":
149+ golden = _torch_to_numpy_tensor(golden.to(torch.bfloat16))
150+ else:
151+ golden = golden.numpy()
152+ elif mode == 'reflect' or mode == 'symmetric' or mode == 'edge':
153+ pad_shape = list()
154+ if paddings_contiguous:
155+ pad_shape = paddings
156+ else:
157+ pad_shape = np.stack(paddings, axis=1).ravel().tolist()
158+ pad_shape = np.array(pad_shape).reshape(-1, 2).tolist()
159+ 
160+ orig_dtype = x.dtype
161+ if orig_dtype.name == "bfloat16":
162+ x = _numpy_to_torch_tensor(x).to(torch.float32).numpy()
163+ 
164+ neg_pad = _numpy_to_torch_tensor(np.array(pad_shape))
165+ neg_mask = neg_pad < 0
166+ indices = torch.nonzero(neg_mask, as_tuple=False)
167+ 
168+ if indices.size(0) == 0:
169+ golden = np.pad(x, pad_shape, mode=mode)
170+ else:
171+ golden = _pad_and_slice(x, pad_shape, mode=mode)
172+ 
173+ if orig_dtype.name == "bfloat16":
174+ golden = _torch_to_numpy_tensor(_numpy_to_torch_tensor(golden).to(torch.bfloat16))
175+ else:
176+ raise ValueError(f"Unsupported pad mode: {mode}")
177+ 
178+ return golden
@@ -0,0 +1,6 @@
1+testcase_name,network_name,op_name,input_dtypes,input_ori_shapes,output_ori_shapes,input_ori_formats,output_ori_formats,attributes,input_shapes,output_dtypes,output_shapes,input_formats,output_formats,input_data_ranges,precision_tolerances,absolute_precision,output_inplace_indexes,output_shape_unknown_indexes,is_enabled,remark,soc_series,priority,dump_file_prefix,manual_input_binaries,manual_golden_binaries
2+pad_v3_random_float32_5_6_5_6_5_3_22,UNKNOWN,pad_v3,"('float32', 'int32', 'float32')","((5, 6, 5, 6, 5, 3, 22),)","((5, 6, 5, 6, 5, 51, 22),)","('ND', 'ND', 'ND')","('ND',)","{'paddings': [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 48], [0, 0]], 'constant_values': -4997, 'mode': 'constant', 'paddings_contiguous': True}","((5, 6, 5, 6, 5, 3, 22), (14,), (1,))","('float32',)","((5, 6, 5, 6, 5, 51, 22),)","('ND', 'ND', 'ND')","('ND',)","((-100, 100), (-1, 1), (-1, 61))","((0, 0),)",1e-08,(),(),True,,,0,,(),()
3+pad_v3_random_float32_57_8_400,UNKNOWN,pad_v3,"('float32', 'int32', 'float32')","((57, 8, 400),)","((57, 8, 1758),)","('ND', 'ND', 'ND')","('ND',)","{'paddings': [[0, 0, 934], [0, 0, 424]], 'constant_values': -1582, 'mode': 'constant', 'paddings_contiguous': False}","((57, 8, 400), (6,), (1,))","('float32',)","((57, 8, 1758),)","('ND', 'ND', 'ND')","('ND',)","((-100, 100), (-1, 1), (-1, 69))","((0, 0),)",1e-08,(),(),True,,,0,,(),()
4+pad_v3_random_uint32_3_1321_1_2_2_6_3,UNKNOWN,pad_v3,"('uint32', 'int32', 'uint32')","((3, 1321, 1, 2, 2, 6, 3),)","((3, 2502, 1, 2, 2, 6, 3),)","('ND', 'ND', 'ND')","('ND',)","{'paddings': [[0, 736, 0, 0, 0, 0, 0], [0, 445, 0, 0, 0, 0, 0]], 'constant_values': 5778, 'mode': 'constant', 'paddings_contiguous': False}","((3, 1321, 1, 2, 2, 6, 3), (14,), (1,))","('uint32',)","((3, 2502, 1, 2, 2, 6, 3),)","('ND', 'ND', 'ND')","('ND',)","((-100, 100), (-1, 1), (-1, 113))","((0, 0),)",1e-08,(),(),True,,,0,,(),()
5+pad_v3_random_bfloat16_93_2896,UNKNOWN,pad_v3,"('bfloat16', 'int32', 'bfloat16')","((93, 2896),)","((1025, 4086),)","('ND', 'ND', 'ND')","('ND',)","{'paddings': [[168, 974], [764, 216]], 'constant_values': 285, 'mode': 'constant', 'paddings_contiguous': False}","((93, 2896), (4,), (1,))","('bfloat16',)","((1025, 4086),)","('ND', 'ND', 'ND')","('ND',)","((-100, 100), (-1, 1), (-1, 26))","((0, 0),)",1e-08,(),(),True,,,0,,(),()
6+pad_v3_random_int32_1026_5_30,UNKNOWN,pad_v3,"('int32', 'int32', 'int32')","((1026, 5, 30),)","((1932, 99, 30),)","('ND', 'ND', 'ND')","('ND',)","{'paddings': [[906, 0], [94, 0], [0, 0]], 'constant_values': 3677, 'mode': 'constant', 'paddings_contiguous': True}","((1026, 5, 30), (6,), (1,))","('int32',)","((1932, 99, 30),)","('ND', 'ND', 'ND')","('ND',)","((-100, 100), (-1, 1), (-1, 9))","((0, 0),)",1e-08,(),(),True,,,0,,(),()
@@ -0,0 +1,87 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# ----------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# 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").
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,
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.
11+# ----------------------------------------------------------------------------
12+ 
13+import numpy as np
14+ 
15+ 
16+__golden__ = {
17+ "kernel": {
18+ "pad_v3_grad_replication": "pad_v3_grad_replication_golden"
19+ }
20+}
21+ 
22+ 
23+def _cal_out_shape(in_shape, paddings):
24+ out_shape = []
25+ offset = len(in_shape) - len(paddings)
26+ for i in range(len(in_shape)):
27+ if i < len(in_shape) - len(paddings):
28+ out_shape.append(in_shape[i])
29+ else:
30+ out_shape.append(in_shape[i] - paddings[i - offset][0] - paddings[i - offset][1])
31+ return out_shape
32+ 
33+ 
34+def _numpy_pad_v3_grad_edge(grad_output, in_shape, pad_per_dim):
35+ dim = grad_output.ndim
36+ 
37+ true_dtype = grad_output.dtype
38+ grad_input = np.zeros(in_shape, dtype=np.float32)
39+ grad_output = np.array(grad_output, dtype=np.float32)
40+ 
41+ out_shape = grad_output.shape
42+ for out_idx in np.ndindex(out_shape):
43+ in_idx = []
44+ for i in range(dim):
45+ left, right = pad_per_dim[i]
46+ out_len = out_shape[i]
47+ in_len = in_shape[i]
48+ o = out_idx[i]
49+ 
50+ left_pad = max(0, left)
51+ right_pad = max(0, right)
52+ 
53+ if left_pad > 0 and o < left_pad:
54+ i_in = 0
55+ elif right_pad > 0 and o >= out_len - right_pad:
56+ i_in = in_len - 1
57+ else:
58+ i_in = o - left
59+ 
60+ in_idx.append(i_in)
61+ 
62+ in_idx = tuple(in_idx)
63+ grad_input[in_idx] += grad_output[out_idx]
64+ grad_input = np.array(grad_input, dtype=true_dtype)
65+ return grad_input
66+ 
67+ 
68+def pad_v3_grad_replication_golden(x, paddings, **kwargs):
69+ '''
70+ Kernel golden for pad_v3_grad_replication.
71+ All the parameters follow @pad_v3_grad_replication_def.cpp without outputs.
72+ All the input Tensors are numpy.ndarray.
73+ kwargs may contain: short_soc_version, input_ori_shapes, output_ori_shapes,
74+ input_formats, output_formats, input_ori_formats, output_ori_formats,
75+ input_dtypes, output_dtypes.
76+ '''
77+ grad_output = x
78+ 
79+ paddings_arr = np.array(paddings).astype(np.int64)
80+ if paddings_arr.ndim == 1:
81+ paddings_arr = paddings_arr.reshape(-1, 2)
82+ pad_shape = paddings_arr.tolist()
83+ 
84+ y_shape = _cal_out_shape(grad_output.shape, pad_shape)
85+ 
86+ grad = _numpy_pad_v3_grad_edge(grad_output, y_shape, pad_shape)
87+ return grad