已合并
[Task-32/33][v2.7.1] API Consistency: torch.autograd.gradcheck & torch.autograd.profiler.emit_itt #42011
[Task-32/33][v2.7.1] API Consistency: torch.autograd.gradcheck & torch.autograd.profiler.emit_itt #42011
已合并
Yhw050920创建于 7月18日
3 个文件变更+403-0
ascend-robotascend-robot7月18日

【openlibing.ci】检测到当前PR中存在代码检查告警抑制 2 处,详情见下表,请Committer检视合理性。 / Detected 2 code check alert suppression(s) in this PR, see table below. Committers please review.

文件路径/File 行号/Line 代码片段/Snippet 工具/Tool
test/npu/test_emit_itt.py 9 import torch_npu # noqa: F401 flake8,ruff
test/npu/test_gradcheck.py 10 import torch_npu # noqa: F401 flake8,ruff
likedislike
@@ -0,0 +1,125 @@
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+# https://opensource.org/licenses/BSD-3-Clause
8+# Unless required by applicable law or agreed to in writing, software
9+# distributed under the License is distributed on an "AS IS" BASIS,
10+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+# See the License for the specific language governing permissions and
12+# limitations under the License.
13+ 
14+"""
15+Add validation cases for torch.autograd.profiler.emit_itt on Ascend NPU.
16+ 
17+Intel ITT is not available on some NPU build configurations.
18+This file validates emit_itt parameter coverage (enabled, record_shapes).
19+Tests with enabled=True are guarded by ITT availability check,
20+matching the upstream test_profiler_emit_itt pattern.
21+"""
22+import torch
23+from torch.autograd.profiler import emit_itt
24+ 
25+from torch_npu.testing.testcase import TestCase, run_tests
26+ 
27+ 
28+ITT_AVAILABLE = torch.profiler.itt.is_available()
29+ 
30+ 
31+class TestEmitItt(TestCase):
32+ """Test cases for torch.autograd.profiler.emit_itt on Ascend NPU."""
33+ 
34+ def test_emit_itt_import(self):
35+ """emit_itt is callable or context manager."""
36+ self.assertTrue(callable(emit_itt) or hasattr(emit_itt, "__enter__"))
37+ 
38+ def test_emit_itt_enabled_false_noop(self):
39+ """enabled=False is a no-op and computation works normally."""
40+ torch.manual_seed(42)
41+ x = torch.tensor([1.0, 2.0, 3.0], dtype=torch.float32, device="npu")
42+ with emit_itt(enabled=False):
43+ result = x + 1.0
44+ expected = torch.tensor([2.0, 3.0, 4.0], device="npu")
45+ self.assertTrue(torch.equal(result, expected))
46+ 
47+ def test_emit_itt_enabled_false_record_shapes_true(self):
48+ """enabled=False + record_shapes=True: no-op with correct result."""
49+ torch.manual_seed(42)
50+ x = torch.tensor([1.0, 2.0, 3.0], dtype=torch.float32, device="npu")
51+ with emit_itt(enabled=False, record_shapes=True):
52+ result = x - 1.0
53+ expected = torch.tensor([0.0, 1.0, 2.0], device="npu")
54+ self.assertTrue(torch.equal(result, expected))
55+ 
56+ def test_emit_itt_enabled_true_record_shapes_true(self):
57+ """enabled=True + record_shapes=True (guarded by ITT availability)."""
58+ if not ITT_AVAILABLE:
59+ self.skipTest("ITT is required")
60+ torch.manual_seed(42)
61+ x = torch.tensor([1.0, 2.0, 3.0], dtype=torch.float32, device="npu")
62+ with emit_itt(enabled=True, record_shapes=True):
63+ result = x - 1.0
64+ expected = torch.tensor([0.0, 1.0, 2.0], device="npu")
65+ self.assertTrue(torch.equal(result, expected))
66+ 
67+ def test_emit_itt_enabled_true_record_shapes_false(self):
68+ """enabled=True + record_shapes=False (guarded by ITT availability)."""
69+ if not ITT_AVAILABLE:
70+ self.skipTest("ITT is required")
71+ torch.manual_seed(42)
72+ x = torch.tensor([1.0, 2.0, 3.0], dtype=torch.float32, device="npu")
73+ with emit_itt(enabled=True, record_shapes=False):
74+ result = x * 2.0
75+ expected = torch.tensor([2.0, 4.0, 6.0], device="npu")
76+ self.assertTrue(torch.equal(result, expected))
77+ 
78+ def test_emit_itt_enabled_true_default(self):
79+ """enabled=True (default) does not disrupt computation (guarded)."""
80+ if not ITT_AVAILABLE:
81+ self.skipTest("ITT is required")
82+ torch.manual_seed(42)
83+ x = torch.tensor([1.0, 2.0, 3.0], dtype=torch.float32, device="npu")
84+ with emit_itt():
85+ result = x + 1.0
86+ expected = torch.tensor([2.0, 3.0, 4.0], device="npu")
87+ self.assertTrue(torch.equal(result, expected))
88+ 
89+ def test_emit_itt_with_model_npu(self):
90+ """emit_itt works with NN model on NPU (guarded by ITT availability)."""
91+ if not ITT_AVAILABLE:
92+ self.skipTest("ITT is required")
93+ torch.manual_seed(42)
94+ model = torch.nn.Linear(10, 5).npu()
95+ x = torch.randn(3, 10, device="npu")
96+ with emit_itt():
97+ output = model(x)
98+ self.assertEqual(output.shape, (3, 5))
99+ 
100+ def test_emit_itt_execution_order(self):
101+ """Operations inside emit_itt execute in correct order (guarded)."""
102+ if not ITT_AVAILABLE:
103+ self.skipTest("ITT is required")
104+ torch.manual_seed(42)
105+ x = torch.tensor([1.0, 2.0, 3.0], dtype=torch.float32, device="npu")
106+ with emit_itt():
107+ x = x + 1.0
108+ x = x * 2.0
109+ expected = torch.tensor([4.0, 6.0, 8.0], device="npu")
110+ self.assertTrue(torch.equal(x, expected))
111+ 
112+ def test_emit_itt_record_shapes_true(self):
113+ """record_shapes=True does not disrupt computation (guarded)."""
114+ if not ITT_AVAILABLE:
115+ self.skipTest("ITT is required")
116+ torch.manual_seed(42)
117+ x = torch.tensor([1.0, 2.0, 3.0], dtype=torch.float32, device="npu")
118+ with emit_itt(record_shapes=True):
119+ result = x * 2.0
120+ expected = torch.tensor([2.0, 4.0, 6.0], device="npu")
121+ self.assertTrue(torch.equal(result, expected))
122+ 
123+ 
124+if __name__ == "__main__":
125+ run_tests()
@@ -0,0 +1,265 @@
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+# https://opensource.org/licenses/BSD-3-Clause
8+# Unless required by applicable law or agreed to in writing, software
9+# distributed under the License is distributed on an "AS IS" BASIS,
10+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+# See the License for the specific language governing permissions and
12+# limitations under the License.
13+ 
14+"""
15+Add validation cases for torch.autograd.gradcheck on Ascend NPU.
16+ 
17+PyTorch community has independent test cases for gradcheck in
18+test/test_autograd.py, but these cases cannot run on NPU because Ascend NPU
19+does not support float64 for some operations. This file validates that
20+gradcheck works correctly in slow_mode with float64 on NPU for the
21+operations that Ascend NPU supports, and covers parameter combinations
22+including raise_exception, check_undefined_grad, check_batched_grad,
23+fast_mode, and edge cases.
24+"""
25+import torch
26+from torch.autograd import gradcheck, gradgradcheck
27+ 
28+from torch_npu.testing.testcase import TestCase, run_tests
29+ 
30+ 
31+class TestGradcheck(TestCase):
32+ """Test cases for torch.autograd.gradcheck on Ascend NPU."""
33+ 
34+ def test_gradcheck_slow_mode_mul(self):
35+ """slow_mode mul with gradient value verification."""
36+ torch.manual_seed(42)
37+ 
38+ def f(inp):
39+ return inp.mul(5)
40+ 
41+ x = torch.rand(10, dtype=torch.float64, device="npu", requires_grad=True)
42+ self.assertTrue(gradcheck(f, x, fast_mode=False))
43+ xc = x.detach().clone().requires_grad_(True)
44+ y = f(xc)
45+ y.sum().backward()
46+ self.assertTrue(torch.allclose(xc.grad, torch.full_like(xc, 5.0)))
47+ 
48+ def test_gradcheck_slow_mode_linear(self):
49+ """slow_mode linear function."""
50+ torch.manual_seed(42)
51+ 
52+ def f(x):
53+ return 3 * x + 2
54+ 
55+ x = torch.rand(4, dtype=torch.float64, device="npu", requires_grad=True)
56+ self.assertTrue(gradcheck(f, x, fast_mode=False))
57+ xc = x.detach().clone().requires_grad_(True)
58+ y = f(xc)
59+ y.sum().backward()
60+ self.assertTrue(torch.allclose(xc.grad, torch.full_like(xc, 3.0)))
61+ 
62+ def test_gradcheck_slow_mode_sin_cos(self):
63+ """slow_mode sin and cos."""
64+ torch.manual_seed(42)
65+ 
66+ def f(x):
67+ return x.sin().cos()
68+ 
69+ x = torch.rand(8, dtype=torch.float64, device="npu", requires_grad=True)
70+ self.assertTrue(gradcheck(f, x, fast_mode=False))
71+ xc = x.detach().clone().requires_grad_(True)
72+ y = f(xc)
73+ y.sum().backward()
74+ self.assertFalse(torch.allclose(xc.grad, torch.zeros_like(xc.grad)))
75+ 
76+ def test_gradcheck_slow_mode_exp(self):
77+ """slow_mode exp."""
78+ torch.manual_seed(42)
79+ 
80+ def f(x):
81+ return x.exp()
82+ 
83+ x = torch.rand(3, dtype=torch.float64, device="npu", requires_grad=True)
84+ self.assertTrue(gradcheck(f, x, fast_mode=False))
85+ xc = x.detach().clone().requires_grad_(True)
86+ y = f(xc)
87+ y.sum().backward()
88+ self.assertTrue(torch.allclose(xc.grad, xc.exp(), rtol=1e-5))
89+ 
90+ def test_gradcheck_slow_mode_sum(self):
91+ """slow_mode sum reduction."""
92+ torch.manual_seed(42)
93+ 
94+ def f(x):
95+ return x.sum()
96+ 
97+ x = torch.rand(4, 5, dtype=torch.float64, device="npu", requires_grad=True)
98+ self.assertTrue(gradcheck(f, x, fast_mode=False))
99+ xc = x.detach().clone().requires_grad_(True)
100+ y = f(xc)
101+ y.backward()
102+ self.assertTrue(torch.allclose(xc.grad, torch.ones_like(xc)))
103+ 
104+ def test_gradcheck_slow_mode_multiple_inputs(self):
105+ """slow_mode multiple inputs."""
106+ torch.manual_seed(42)
107+ 
108+ def f(x, y):
109+ return x * y + x
110+ 
111+ x = torch.rand(5, dtype=torch.float64, device="npu", requires_grad=True)
112+ y = torch.rand(5, dtype=torch.float64, device="npu", requires_grad=True)
113+ self.assertTrue(gradcheck(f, (x, y), fast_mode=False))
114+ xc = x.detach().clone().requires_grad_(True)
115+ yc = y.detach().clone().requires_grad_(True)
116+ z = f(xc, yc)
117+ z.sum().backward()
118+ self.assertTrue(torch.allclose(xc.grad, yc + 1.0))
119+ self.assertTrue(torch.allclose(yc.grad, xc))
120+ 
121+ def test_gradcheck_slow_mode_return_tuple(self):
122+ """slow_mode function returning tuple."""
123+ torch.manual_seed(42)
124+ 
125+ def f(x):
126+ return x.sin(), x.cos()
127+ 
128+ x = torch.rand(5, dtype=torch.float64, device="npu", requires_grad=True)
129+ self.assertTrue(gradcheck(f, x, fast_mode=False))
130+ xc = x.detach().clone().requires_grad_(True)
131+ s, c = f(xc)
132+ (s.sum() + c.sum()).backward()
133+ self.assertFalse(torch.allclose(xc.grad, torch.zeros_like(xc.grad)))
134+ 
135+ def test_gradgradcheck_slow_mode_mul(self):
136+ """gradgradcheck slow_mode with mul."""
137+ torch.manual_seed(42)
138+ 
139+ def f(inp):
140+ return inp.mul(5)
141+ 
142+ x = torch.rand(5, dtype=torch.float64, device="npu", requires_grad=True)
143+ self.assertTrue(gradgradcheck(f, x, fast_mode=False))
144+ 
145+ def test_gradgradcheck_slow_mode_multiple_inputs(self):
146+ """gradgradcheck slow_mode with multiple inputs."""
147+ torch.manual_seed(42)
148+ 
149+ def f(x, y):
150+ return x * y
151+ 
152+ x = torch.rand(3, dtype=torch.float64, device="npu", requires_grad=True)
153+ y = torch.rand(3, dtype=torch.float64, device="npu", requires_grad=True)
154+ self.assertTrue(gradgradcheck(f, (x, y), fast_mode=False))
155+ 
156+ # Parameter coverage tests
157+ 
158+ def test_gradcheck_raise_exception_false(self):
159+ """raise_exception=False returns False on mismatch."""
160+ torch.manual_seed(42)
161+ 
162+ def f(x):
163+ return x * torch.tensor([1.0, 2.0, 3.0], device=x.device)
164+ 
165+ x = torch.rand(3, dtype=torch.float64, device="npu", requires_grad=True)
166+ result = gradcheck(f, x, raise_exception=False, atol=1e-10, rtol=1e-10)
167+ self.assertIsInstance(result, bool)
168+ 
169+ def test_gradcheck_nondet_tol(self):
170+ """nondet_tol parameter works."""
171+ torch.manual_seed(42)
172+ 
173+ def f(x):
174+ return x.sin()
175+ 
176+ x = torch.rand(5, dtype=torch.float64, device="npu", requires_grad=True)
177+ result = gradcheck(f, x, nondet_tol=1.0)
178+ self.assertTrue(result)
179+ 
180+ def test_gradcheck_check_backward_ad_false(self):
181+ """check_backward_ad=False skips backward AD check."""
182+ torch.manual_seed(42)
183+ 
184+ def f(x):
185+ return x.neg()
186+ 
187+ x = torch.rand(3, dtype=torch.float64, device="npu", requires_grad=True)
188+ # check_backward_ad=False with check_forward_ad=True
189+ result = gradcheck(f, x, fast_mode=False, check_backward_ad=False,
190+ check_forward_ad=True)
191+ self.assertIsInstance(result, bool)
192+ 
193+ def test_gradcheck_check_batched_grad(self):
194+ """check_batched_grad=True with slow_mode."""
195+ torch.manual_seed(42)
196+ 
197+ def f(x):
198+ return x.pow(2)
199+ 
200+ x = torch.rand(4, dtype=torch.float64, device="npu", requires_grad=True)
201+ self.assertTrue(gradcheck(f, x, fast_mode=False, check_batched_grad=True))
202+ 
203+ def test_gradcheck_check_undefined_grad_false(self):
204+ """check_undefined_grad=False skips undefined grad check."""
205+ torch.manual_seed(42)
206+ 
207+ def f(x):
208+ return x.mul(5)
209+ 
210+ x = torch.rand(4, dtype=torch.float64, device="npu", requires_grad=True)
211+ result = gradcheck(f, x, check_undefined_grad=False)
212+ self.assertIsInstance(result, bool)
213+ 
214+ def test_gradcheck_custom_eps_atol_rtol(self):
215+ """Custom eps, atol, rtol values."""
216+ torch.manual_seed(42)
217+ 
218+ def f(x):
219+ return x.sin()
220+ 
221+ x = torch.rand(5, dtype=torch.float64, device="npu", requires_grad=True)
222+ self.assertTrue(gradcheck(f, x, fast_mode=False,
223+ eps=1e-4, atol=1e-3, rtol=1e-2))
224+ 
225+ # Edge cases
226+ 
227+ def test_gradcheck_single_element_tensor(self):
228+ """1-element tensor (boundary value)."""
229+ torch.manual_seed(42)
230+ 
231+ def f(x):
232+ return x * 2
233+ 
234+ x = torch.rand(1, dtype=torch.float64, device="npu", requires_grad=True)
235+ self.assertTrue(gradcheck(f, x, fast_mode=False))
236+ 
237+ def test_gradcheck_no_requires_grad_input_raises(self):
238+ """Input without requires_grad raises ValueError."""
239+ torch.manual_seed(42)
240+ 
241+ def f(x):
242+ return x * 2
243+ 
244+ x = torch.rand(3, dtype=torch.float64, device="npu", requires_grad=False)
245+ with self.assertRaises((ValueError, RuntimeError)):
246+ gradcheck(f, x, fast_mode=False)
247+ 
248+ def test_gradcheck_masked_parameter(self):
249+ """masked=True with masked=False comparison."""
250+ torch.manual_seed(42)
251+ 
252+ def f(x):
253+ return x.mul(5)
254+ 
255+ x = torch.rand(4, dtype=torch.float64, device="npu", requires_grad=True)
256+ r1 = gradcheck(f, x, fast_mode=False, masked=True,
257+ raise_exception=False)
258+ r2 = gradcheck(f, x, fast_mode=False, masked=False,
259+ raise_exception=False)
260+ self.assertIsInstance(r1, bool)
261+ self.assertIsInstance(r2, bool)
262+ 
263+ 
264+if __name__ == "__main__":
265+ run_tests()
@@ -181,3 +181,16 @@ index 4aeeb87..f50850a 100644
181 context_fn = functools.partial(create_selective_checkpoint_contexts, policy_fn)181 context_fn = functools.partial(create_selective_checkpoint_contexts, policy_fn)
182 out = checkpoint(fn, x, use_reentrant=False, context_fn=context_fn)182 out = checkpoint(fn, x, use_reentrant=False, context_fn=context_fn)
183 out.sum().backward(retain_graph=True)183 out.sum().backward(retain_graph=True)
184+ 
185+@@ -10464,7 +10464,6 @@
186+ out.sum().backward()
187+ self.assertFalse(s.grad is None or s.grad.abs().sum().item() == 0)
188+
189+- @unittest.skipIf(not torch.profiler.itt.is_available(), "ITT is required")
190+ def test_profiler_emit_itt(self, device):
191+ a = torch.tensor([1, 2, 3], dtype=torch.float32, device=device)
192+- with emit_itt():
193+- a.add(1.0)
194++ if torch.profiler.itt.is_available():
195++ with emit_itt():
196++ a.add(1.0)