1010from torch .testing import assert_close
1111
1212from lightning_utilities import compare_version
13+ import inspect
1314
1415import thunder
1516
16- from thunder .tests .framework import requiresCUDA , IS_WINDOWS , xfail_if_args_tensor_mask_removed
17+ from thunder .tests .framework import requiresCUDA , IS_WINDOWS
1718from thunder .core .options import CACHE_OPTIONS
1819import thunder .core .prims as prims
1920from thunder import pytorch_executor , nvfuser_executor
2021from thunder .executors .sdpaex import sdpa_ex
2122from thunder .core .transforms import Transform
2223
2324
25+ # Detect once at module load time whether PyTorch uses args_tensor_mask.
26+ # This must be done outside the JIT-traced function to avoid interpreter issues.
27+ def _detect_has_args_tensor_mask ():
28+ """Check if autograd_function_apply uses args_tensor_mask.
29+
30+ Stable PyTorch requires args_tensor_mask, nightly PyTorch has removed it.
31+ """
32+ try :
33+ from torch ._functorch .autograd_function import AutogradFunctionApply
34+
35+ source = inspect .getsource (AutogradFunctionApply .__call__ )
36+ return "args_tensor_mask" in source
37+ except (ImportError , AttributeError , OSError ):
38+ # Fallback: assume stable PyTorch with args_tensor_mask
39+ return True
40+
41+
42+ _HAS_ARGS_TENSOR_MASK = _detect_has_args_tensor_mask ()
43+
44+
45+ def _autograd_function_apply_kwargs (args_tensor_mask , non_differentiable_idx = None ):
46+ """Create kwargs for autograd_function_apply that work with both stable and nightly PyTorch."""
47+ kwargs = {}
48+ if _HAS_ARGS_TENSOR_MASK :
49+ kwargs ["args_tensor_mask" ] = args_tensor_mask
50+ if non_differentiable_idx is not None :
51+ kwargs ["non_differentiable_idx" ] = non_differentiable_idx
52+ return kwargs
53+
54+
2455thunder_jit = partial (thunder .jit , debug_options = thunder .DebugOptions (check_traces = 2 ))
2556
2657#
@@ -1252,35 +1283,48 @@ def f(x):
12521283
12531284
12541285@pytest .mark .filterwarnings ("ignore:Please use torch.vmap" )
1255- @xfail_if_args_tensor_mask_removed
12561286def test_autograd_function_apply ():
12571287 # see https://github.com/Lightning-AI/lightning-thunder/issues/1248#issuecomment-2388655917
12581288 # for why `torch.foo` instead of `torch.Tensor.foo`
12591289
12601290 # since https://github.com/pytorch/pytorch/pull/169528 `torch.ops.higher_order.autograd_function_apply`
12611291 # no longer accepts simple callables, but rather `torch.fx.GraphModule`s.
12621292
1263- class FwdModule ( torch . nn . Module ):
1264- def forward ( self , ctx , x ):
1265- saved_for_backward = ( x ,)
1266- return torch . sin ( x ), saved_for_backward
1293+ # TODO: Remove this once this autograd API becomes stable.
1294+ # On stable PyTorch (with args_tensor_mask), forward/backward expect ctx as first arg.
1295+ # On nightly PyTorch (without args_tensor_mask), ctx is not an argument.
1296+ if _HAS_ARGS_TENSOR_MASK :
12671297
1268- fwd = torch .fx .symbolic_trace (FwdModule ())
1298+ class FwdModule (torch .nn .Module ):
1299+ def forward (self , ctx , x ):
1300+ saved_for_backward = (x ,)
1301+ return torch .sin (x ), saved_for_backward
12691302
1270- class BwdModule (torch .nn .Module ):
1271- def forward (self , ctx , grad_output , * saved_tensors ):
1272- (x ,) = saved_tensors
1273- return grad_output * torch .cos (x )
1303+ class BwdModule (torch .nn .Module ):
1304+ def forward (self , ctx , grad_output , * saved_tensors ):
1305+ (x ,) = saved_tensors
1306+ return grad_output * torch .cos (x )
1307+ else :
12741308
1309+ class FwdModule (torch .nn .Module ):
1310+ def forward (self , x ):
1311+ saved_for_backward = (x ,)
1312+ return torch .sin (x ), saved_for_backward
1313+
1314+ class BwdModule (torch .nn .Module ):
1315+ def forward (self , grad_output , * saved_tensors ):
1316+ (x ,) = saved_tensors
1317+ return grad_output * torch .cos (x )
1318+
1319+ fwd = torch .fx .symbolic_trace (FwdModule ())
12751320 bwd = torch .fx .symbolic_trace (BwdModule ())
12761321
12771322 def my_sin (x ):
12781323 return torch .ops .higher_order .autograd_function_apply (
12791324 fwd ,
12801325 bwd ,
12811326 x ,
1282- args_tensor_mask = [True ],
1283- non_differentiable_idx = [],
1327+ ** _autograd_function_apply_kwargs ([True ], non_differentiable_idx = []),
12841328 )
12851329
12861330 jitted = thunder_jit (my_sin )
@@ -1296,10 +1340,21 @@ def my_sin(x):
12961340 expect_grad = torch .autograd .grad (y_ref , x_ref , grad )
12971341 torch .testing .assert_close (actual_grad , expect_grad )
12981342
1299- class WrongBwdModule (torch .nn .Module ):
1300- def forward (self , ctx , grad_output , * saved_tensors ):
1301- (x ,) = saved_tensors
1302- return grad_output * torch .cos (x )
1343+ # TODO: Remove this once this autograd API becomes stable.
1344+ # On stable PyTorch (with args_tensor_mask), forward/backward expect ctx as first arg.
1345+ # On nightly PyTorch (without args_tensor_mask), ctx is not an argument.
1346+ if _HAS_ARGS_TENSOR_MASK :
1347+
1348+ class WrongBwdModule (torch .nn .Module ):
1349+ def forward (self , ctx , grad_output , * saved_tensors ):
1350+ (x ,) = saved_tensors
1351+ return grad_output * torch .cos (x )
1352+ else :
1353+
1354+ class WrongBwdModule (torch .nn .Module ):
1355+ def forward (self , grad_output , * saved_tensors ):
1356+ (x ,) = saved_tensors
1357+ return grad_output * torch .cos (x )
13031358
13041359 wrong_bwd = torch .fx .symbolic_trace (WrongBwdModule ())
13051360
@@ -1308,8 +1363,7 @@ def my_sin_with_wrong_backward(x):
13081363 fwd ,
13091364 wrong_bwd ,
13101365 x ,
1311- args_tensor_mask = [True ],
1312- non_differentiable_idx = [],
1366+ ** _autograd_function_apply_kwargs ([True ], non_differentiable_idx = []),
13131367 )
13141368
13151369 jitted = thunder_jit (my_sin_with_wrong_backward )
@@ -1329,26 +1383,40 @@ def my_sin_with_wrong_backward(x):
13291383 gradcheck (jitted , (x ,))
13301384
13311385
1332- @xfail_if_args_tensor_mask_removed
13331386def test_autograd_function_apply_with_no_grad ():
13341387 # This case is using `torch` operations
1335- def forward (_ , x ):
1336- saved_for_backward = (x ,)
1388+ # TODO: Remove this once this autograd API becomes stable.
1389+ # On stable PyTorch (with args_tensor_mask), forward/backward expect ctx as first arg.
1390+ # On nightly PyTorch (without args_tensor_mask), ctx is not an argument.
1391+ if _HAS_ARGS_TENSOR_MASK :
1392+
1393+ def forward (_ , x ):
1394+ saved_for_backward = (x ,)
13371395
1338- with torch .no_grad ():
1339- sin = torch .sin (x )
1340- return sin , saved_for_backward
1396+ with torch .no_grad ():
1397+ sin = torch .sin (x )
1398+ return sin , saved_for_backward
1399+
1400+ def backward (_ , grad_output , * saved_tensors ):
1401+ return grad_output * 2
1402+ else :
1403+
1404+ def forward (x ):
1405+ saved_for_backward = (x ,)
13411406
1342- def backward (_ , grad_output , * saved_tensors ):
1343- return grad_output * 2
1407+ with torch .no_grad ():
1408+ sin = torch .sin (x )
1409+ return sin , saved_for_backward
1410+
1411+ def backward (grad_output , * saved_tensors ):
1412+ return grad_output * 2
13441413
13451414 def my_sin (x ):
13461415 res = torch .ops .higher_order .autograd_function_apply (
13471416 forward ,
13481417 backward ,
13491418 x ,
1350- args_tensor_mask = [True ],
1351- non_differentiable_idx = [],
1419+ ** _autograd_function_apply_kwargs ([True ], non_differentiable_idx = []),
13521420 )
13531421 return res
13541422
@@ -1364,24 +1432,40 @@ def my_sin(x):
13641432
13651433 # This is using `thunder` operations
13661434 # NOTE - This takes a different codepath compared to above.
1367- def forward (_ , x ): # noqa: F811
1368- saved_for_backward = (x ,)
1369- thunder .torch ._set_grad_enabled_with_warning (False )
1370- sin = thunder .torch .sin (x )
1371- thunder .torch ._set_grad_enabled_with_warning (True )
1372- return sin , saved_for_backward
1435+ # TODO: Remove this once this autograd API becomes stable.
1436+ # On stable PyTorch (with args_tensor_mask), forward/backward expect ctx as first arg.
1437+ # On nightly PyTorch (without args_tensor_mask), ctx is not an argument.
1438+ if _HAS_ARGS_TENSOR_MASK :
1439+
1440+ def forward (_ , x ):
1441+ saved_for_backward = (x ,)
1442+ thunder .torch ._set_grad_enabled_with_warning (False )
1443+ sin = thunder .torch .sin (x )
1444+ thunder .torch ._set_grad_enabled_with_warning (True )
1445+ return sin , saved_for_backward
1446+
1447+ def backward (_ , grad_output , * saved_tensors ):
1448+ # NOTE - This is incorrect on purpose
1449+ return grad_output * 2
1450+ else :
1451+
1452+ def forward (x ):
1453+ saved_for_backward = (x ,)
1454+ thunder .torch ._set_grad_enabled_with_warning (False )
1455+ sin = thunder .torch .sin (x )
1456+ thunder .torch ._set_grad_enabled_with_warning (True )
1457+ return sin , saved_for_backward
13731458
1374- def backward (_ , grad_output , * saved_tensors ): # noqa: F811
1375- # NOTE - This is incorrect on purpose
1376- return grad_output * 2
1459+ def backward (grad_output , * saved_tensors ):
1460+ # NOTE - This is incorrect on purpose
1461+ return grad_output * 2
13771462
13781463 def fn (x ):
13791464 res = thunder .torch .autograd_function_apply (
13801465 forward ,
13811466 backward ,
13821467 x ,
1383- args_tensor_mask = [True ],
1384- non_differentiable_idx = [],
1468+ ** _autograd_function_apply_kwargs ([True ], non_differentiable_idx = []),
13851469 )
13861470 return res
13871471
0 commit comments