已合并
test: add unflatten_dense_tensors NPU coverage #41632
test: add unflatten_dense_tensors NPU coverage #41632
已合并
yulin520创建于 7月14日
1 个文件变更+211-0
Atest/nn/test_unflatten_dense_tensors.py+211-0
@@ -0,0 +1,211 @@
1+# Copyright (c) 2026 Huawei Technologies Co., Ltd
2+# All rights reserved.
3+#
4+# Licensed under the BSD 3-Clause License (the "License");
5+# you may not use this file except in compliance with the License.
6+# You may obtain a copy of the License at
7+#
8+# https://opensource.org/licenses/BSD-3-Clause
9+#
10+# Unless required by applicable law or agreed to in writing, software
11+# distributed under the License is distributed on an "AS IS" BASIS,
12+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13+# implied.
14+# See the License for the specific language governing permissions and
15+# limitations under the License.
16+ 
17+"""
18+Add validation cases for torch.nn APIs on NPU:
19+1. PyTorch community lacks sufficient and direct API validations for some APIs, so this file is added.
20+2. This file validates torch._C._nn.unflatten_dense_tensors (extendable).
21+"""
22+ 
23+import torch
24+from torch_npu.testing.testcase import TestCase, run_tests
25+ 
26+device_type = (
27+ acc.type if (acc := torch.accelerator.current_accelerator()) else "cpu"
28+)
29+ 
30+ 
31+class TestUnflattenDenseTensors(TestCase):

用例只覆盖 float32,缺少对 dtype 保持契约的基本验证

likedislike
yulin520
yulin520
8 天前 评论:
32+ def test_unflatten_dense_tensors_basic(self):
33+ flat = torch.arange(
34+ 12,
35+ dtype=torch.float32,
36+ device=device_type,
37+ )
38+ 
39+ tensors = [
40+ torch.empty(
41+ (2, 3),
42+ dtype=torch.float32,
43+ device=device_type,
44+ ),
45+ torch.empty(
46+ (3, 2),
47+ dtype=torch.float32,
48+ device=device_type,
49+ ),
50+ ]
51+ 
52+ outputs = torch._C._nn.unflatten_dense_tensors(
53+ flat,
54+ tensors,
55+ )
56+ 
57+ self.assertIsInstance(outputs, tuple)
58+ self.assertEqual(len(outputs), 2)
59+ 
60+ self.assertEqual(tuple(outputs[0].shape), (2, 3))
61+ self.assertEqual(tuple(outputs[1].shape), (3, 2))
62+ 
63+ self.assertEqual(outputs[0].device.type, device_type)
64+ self.assertEqual(outputs[1].device.type, device_type)
65+ 
66+ self.assertEqual(outputs[0].dtype, torch.float32)
67+ self.assertEqual(outputs[1].dtype, torch.float32)
68+ 
69+ expected_first = torch.arange(
70+ 0,
71+ 6,
72+ dtype=torch.float32,
73+ device=device_type,
74+ ).reshape(2, 3)
75+ 
76+ expected_second = torch.arange(
77+ 6,
78+ 12,
79+ dtype=torch.float32,
80+ device=device_type,
81+ ).reshape(3, 2)
82+ 
83+ self.assertTrue(
84+ torch.equal(outputs[0], expected_first)
85+ )
86+ self.assertTrue(
87+ torch.equal(outputs[1], expected_second)
88+ )
89+ 
90+ flat_storage_ptr = (
91+ flat.untyped_storage().data_ptr()
92+ )
93+ 
94+ self.assertEqual(
95+ outputs[0].untyped_storage().data_ptr(),
96+ flat_storage_ptr,
97+ )
98+ self.assertEqual(
99+ outputs[1].untyped_storage().data_ptr(),
100+ flat_storage_ptr,
101+ )
102+ 
103+ self.assertEqual(outputs[0].storage_offset(), 0)
104+ self.assertEqual(outputs[1].storage_offset(), 6)
105+ 
106+ def test_unflatten_dense_tensors_with_empty_tensor(self):
107+ flat = torch.arange(
108+ 6,
109+ dtype=torch.float32,
110+ device=device_type,
111+ )
112+ 
113+ tensors = [
114+ torch.empty(
115+ (0,),
116+ dtype=torch.float32,
117+ device=device_type,
118+ ),
119+ torch.empty(
120+ (2, 3),
121+ dtype=torch.float32,
122+ device=device_type,
123+ ),
124+ ]
125+ 
126+ outputs = torch._C._nn.unflatten_dense_tensors(
127+ flat,
128+ tensors,
129+ )
130+ 
131+ self.assertIsInstance(outputs, tuple)
132+ self.assertEqual(len(outputs), 2)
133+ 
134+ self.assertEqual(tuple(outputs[0].shape), (0,))
135+ self.assertEqual(outputs[0].numel(), 0)
136+ self.assertEqual(outputs[0].device.type, device_type)
137+ 
138+ self.assertEqual(tuple(outputs[1].shape), (2, 3))
139+ self.assertEqual(outputs[1].device.type, device_type)
140+ 
141+ expected = torch.arange(
142+ 6,
143+ dtype=torch.float32,
144+ device=device_type,
145+ ).reshape(2, 3)
146+ 
147+ self.assertTrue(
148+ torch.equal(outputs[1], expected)
149+ )
150+ 
151+ self.assertEqual(
152+ outputs[1].untyped_storage().data_ptr(),
153+ flat.untyped_storage().data_ptr(),
154+ )
155+ self.assertEqual(outputs[1].storage_offset(), 0)
156+ 
157+ def test_unflatten_dense_tensors_autograd(self):
158+ flat = torch.arange(
159+ 10,
160+ dtype=torch.float32,
161+ device=device_type,
162+ requires_grad=True,
163+ )
164+ 
165+ tensors = [
166+ torch.empty((2, 3), dtype=torch.float32, device=device_type),
167+ torch.empty((4,), dtype=torch.float32, device=device_type),
168+ ]
169+ 
170+ outputs = torch._C._nn.unflatten_dense_tensors(flat, tensors)
171+ 
172+ self.assertEqual(len(outputs), 2)
173+ self.assertTrue(outputs[0].requires_grad)
174+ self.assertTrue(outputs[1].requires_grad)
175+ self.assertEqual(outputs[0].device.type, device_type)
176+ self.assertEqual(outputs[1].device.type, device_type)
177+ 
178+ loss = outputs[0].sum() + (outputs[1] * 2).sum()
179+ loss.backward()
180+ 
181+ expected_grad = torch.tensor(
182+ [1.0] * 6 + [2.0] * 4,
183+ dtype=torch.float32,
184+ device=device_type,
185+ )
186+ 
187+ self.assertIsNotNone(flat.grad)
188+ self.assertEqual(flat.grad.device.type, device_type)
189+ self.assertEqual(flat.grad, expected_grad)
190+ 
191+ def test_unflatten_dense_tensors_dtype_preservation(self):
192+ for dtype in (torch.float16, torch.float32):
193+ with self.subTest(dtype=dtype):
194+ flat = torch.tensor(
195+ [0, 1, 2, 3, 4, 5],
196+ dtype=dtype,
197+ device=device_type,
198+ )
199+ tensors = [
200+ torch.empty((2,), dtype=dtype, device=device_type),
201+ torch.empty((2, 2), dtype=dtype, device=device_type),
202+ ]
203+ 
204+ outputs = torch._C._nn.unflatten_dense_tensors(flat, tensors)
205+ 
206+ self.assertEqual(outputs[0].dtype, dtype)
207+ self.assertEqual(outputs[1].dtype, dtype)
208+ 
209+ 
210+if __name__ == "__main__":
211+ run_tests()