已合并
test(utils): add checkpoint API coverage tests on NPU #37236
test(utils): add checkpoint API coverage tests on NPU #37236
已合并
Jinfan Liu创建于 5月30日
1 个文件变更+110-0
Atest/test_utils_checkpoint_api.py+110-0
@@ -0,0 +1,110 @@
1+"""
2+Add validation cases for torch.utils.checkpoint APIs on NPU:
3+1. PyTorch community tests cover these APIs mainly through checkpoint call chains, so this file adds direct API validations.
4+2. This file validates torch.utils.checkpoint.SelectiveCheckpointContext and torch.utils.checkpoint.detach_variable (extendable).
5+"""
6+ 
7+import functools
8+import inspect
9+ 
10+import torch
11+from torch.testing._internal.common_utils import TestCase, run_tests
12+from torch.utils import checkpoint as checkpoint_utils
13+ 
14+ 
15+device_type = acc.type if (acc := torch.accelerator.current_accelerator()) else "cpu"
16+ 
17+ 
18+class TestUtilsCheckpointAPIs(TestCase):
19+ 
20+ def test_detach_variable_keeps_device_and_requires_grad(self):
21+ x = torch.randn(2, 3, device=device_type, requires_grad=True)
22+ y = (x * 2).relu()
23+ marker = object()
24+ 
25+ detached_x, detached_y, detached_marker = checkpoint_utils.detach_variable(
26+ (x, y, marker)
27+ )
28+ 
29+ self.assertEqual(detached_x.device.type, device_type)
30+ self.assertEqual(detached_y.device.type, device_type)
31+ self.assertTrue(detached_x.requires_grad)
32+ self.assertTrue(detached_y.requires_grad)
33+ self.assertTrue(detached_x.is_leaf)
34+ self.assertTrue(detached_y.is_leaf)
35+ self.assertIsNone(detached_x.grad_fn)
36+ self.assertIsNone(detached_y.grad_fn)
37+ self.assertEqual(detached_x, x)
38+ self.assertEqual(detached_y, y)
39+ self.assertIs(detached_marker, marker)
40+ 
41+ def test_detach_variable_rejects_non_tuple_input(self):
42+ x = torch.randn(2, device=device_type)
43+ 
44+ with self.assertRaisesRegex(RuntimeError, "Only tuple"):
45+ checkpoint_utils.detach_variable([x])
46+ 
47+ def test_selective_checkpoint_context_direct_attributes(self):
48+ ctx = checkpoint_utils.SelectiveCheckpointContext(is_recompute=False)
49+ 
50+ self.assertIsInstance(ctx, checkpoint_utils.SelectiveCheckpointContext)
51+ self.assertFalse(ctx.is_recompute)
52+ 
53+ signature = inspect.signature(checkpoint_utils.SelectiveCheckpointContext)
54+ if "op_output" in signature.parameters:
55+ output = torch.ones(2, device=device_type)
56+ ctx = checkpoint_utils.SelectiveCheckpointContext(
57+ is_recompute=False,
58+ op_output=output,
59+ )
60+ self.assertIs(ctx.op_output, output)
61+ self.assertEqual(ctx.op_output.device.type, device_type)
62+ 
63+ def test_selective_checkpoint_context_passed_to_policy_fn(self):
64+ contexts = []
65+ 
66+ def policy_fn(ctx, op, *args, **kwargs):
67+ contexts.append(ctx)
68+ return checkpoint_utils.CheckpointPolicy.PREFER_RECOMPUTE
69+ 
70+ def fn(x):
71+ return x.sin().cos().sum()
72+ 
73+ x = torch.randn(4, device=device_type, requires_grad=True)
74+ context_fn = functools.partial(
75+ checkpoint_utils.create_selective_checkpoint_contexts,
76+ policy_fn,
77+ )
78+ out = checkpoint_utils.checkpoint(
79+ fn,
80+ x,
81+ use_reentrant=False,
82+ context_fn=context_fn,
83+ )
84+ out.backward()
85+ 
86+ self.assertTrue(contexts)
87+ self.assertTrue(
88+ all(
89+ isinstance(ctx, checkpoint_utils.SelectiveCheckpointContext)
90+ for ctx in contexts
91+ )
92+ )
93+ self.assertTrue(any(ctx.is_recompute for ctx in contexts))
94+ self.assertTrue(any(not ctx.is_recompute for ctx in contexts))
95+ 
96+ forward_contexts = [ctx for ctx in contexts if not ctx.is_recompute]
97+ if forward_contexts and hasattr(forward_contexts[0], "op_output"):
98+ tensor_outputs = [
99+ ctx.op_output
100+ for ctx in forward_contexts
101+ if isinstance(ctx.op_output, torch.Tensor)
102+ ]
103+ self.assertTrue(tensor_outputs)
104+ self.assertTrue(
105+ all(output.device.type == device_type for output in tensor_outputs)
106+ )
107+ 
108+ 
109+if __name__ == "__main__":
110+ run_tests()