_utils.py 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111
  1. # mypy: allow-untyped-defs
  2. import copyreg
  3. import functools
  4. import importlib
  5. import logging
  6. import sys
  7. import traceback
  8. import warnings
  9. from collections import defaultdict
  10. from types import ModuleType
  11. from typing import Any, Callable, Generic, Optional, TYPE_CHECKING
  12. from typing_extensions import deprecated, ParamSpec
  13. import torch
  14. def _type(self, dtype=None, non_blocking=False, **kwargs):
  15. """Returns the type if `dtype` is not provided, else casts this object to
  16. the specified type.
  17. If this is already of the correct type, no copy is performed and the
  18. original object is returned.
  19. Args:
  20. dtype (type or string): The desired type
  21. non_blocking (bool): If ``True``, and the source is in pinned memory
  22. and destination is on the GPU or vice versa, the copy is performed
  23. asynchronously with respect to the host. Otherwise, the argument
  24. has no effect.
  25. **kwargs: For compatibility, may contain the key ``async`` in place of
  26. the ``non_blocking`` argument. The ``async`` arg is deprecated.
  27. """
  28. non_blocking = _get_async_or_non_blocking("type", non_blocking, kwargs)
  29. if dtype is None:
  30. return self.__module__ + "." + self.__class__.__name__
  31. if isinstance(dtype, str):
  32. dtype = _import_dotted_name(dtype)
  33. if dtype == type(self):
  34. return self
  35. if self.is_sparse:
  36. if not dtype.is_sparse:
  37. raise RuntimeError("Cannot cast sparse tensor to dense tensor")
  38. new_module_name = dtype.__module__.replace(".sparse", "")
  39. new_values_type_name = new_module_name + "." + dtype.__name__
  40. new_values = torch.Tensor._values(self).type(new_values_type_name, non_blocking)
  41. new_indices_type_name = new_module_name + ".LongTensor"
  42. new_indices = torch.Tensor._indices(self).type(
  43. new_indices_type_name, non_blocking
  44. )
  45. return dtype(new_indices, new_values, self.size())
  46. if dtype.is_sparse:
  47. raise RuntimeError("Cannot cast dense tensor to sparse tensor")
  48. return dtype(self.size()).copy_(self, non_blocking)
  49. def _to(self, device, non_blocking=False):
  50. """Returns a copy of this object in device memory.
  51. If this object is already on the correct device, then no copy is performed
  52. and the original object is returned.
  53. Args:
  54. device (int): The destination device.
  55. non_blocking (bool): If ``True`` and the source is in pinned memory,
  56. the copy will be asynchronous with respect to the host. Otherwise,
  57. the argument has no effect.
  58. """
  59. if self.device == device:
  60. return self
  61. if device.type == "cpu":
  62. pin_memory = non_blocking and self.device.type in (
  63. "cuda",
  64. torch._C._get_privateuse1_backend_name(),
  65. )
  66. untyped_storage = torch.empty(
  67. self.nbytes(), dtype=torch.uint8, device=device, pin_memory=pin_memory
  68. ).untyped_storage()
  69. untyped_storage.copy_(self, non_blocking)
  70. return untyped_storage
  71. device_module = getattr(torch, device.type, None)
  72. assert device_module is not None, (
  73. f"{device.type.upper()} device module is not loaded"
  74. )
  75. with device_module.device(device):
  76. if self.is_sparse and hasattr(device_module, "sparse"):
  77. new_type = getattr(device_module.sparse, self.__class__.__name__)
  78. indices = getattr(torch.Tensor._indices(self), device.type)(
  79. device, non_blocking
  80. )
  81. values = getattr(torch.Tensor._values(self), device.type)(
  82. device, non_blocking
  83. )
  84. return new_type(indices, values, self.size())
  85. else:
  86. assert not self.is_sparse, (
  87. f"sparse storage is not supported for {device.type.upper()} tensors"
  88. )
  89. untyped_storage = torch.UntypedStorage(self.size(), device=device)
  90. untyped_storage.copy_(self, non_blocking)
  91. return untyped_storage
  92. def _get_async_or_non_blocking(function_name, non_blocking, kwargs):
  93. """Return the non-blocking flag given the function name and kwargs.
  94. Args:
  95. function_name (str): the name of the function being used.
  96. non_blocking (bool): the default value.
  97. **kwargs (dict): the kwargs passed to the function.
  98. """
  99. if not kwargs:
  100. return non_blocking
  101. if len(kwargs) != 1 or "async" not in kwargs:
  102. message = "{}() got an unexpected keyword argument '{}'"
  103. argument = list(kwargs.keys()).pop()
  104. raise TypeError(message.format(function_name, argument))
  105. warnings.warn("'async' is deprecated; use 'non_blocking'")
  106. return kwargs["async"]
  107. def _get_restore_location(device):
  108. """Return the map_location location.
  109. Used for rebuild functions where the tensor device is distinct from the storage
  110. """
  111. map_location = torch.serialization._serialization_tls.map_location
  112. if map_location is None:
  113. return device
  114. else:
  115. if isinstance(map_location, dict):
  116. return map_location.get(device, device)
  117. elif isinstance(map_location, (str, torch.device)):
  118. return map_location
  119. else:
  120. assert callable(map_location)
  121. raise RuntimeError(
  122. "Callable map_location not supported with _rebuild_wrapper_subclass "
  123. "or _rebuild_device_tensor_from_numpy"
  124. )
  125. # Note [Don't serialize hooks]
  126. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  127. # Since time immemorial, we have serialized the backward hooks associated with
  128. # variables. This kind of half-worked--Python can pickle global functions
  129. # (but not closures!)--but there were problems.
  130. #
  131. # - It's fragile. If you serialize a backward hook into a saved
  132. # model, and then you rename the function associated with the hook,
  133. # now your saved model is broken and you can't load it anymore.
  134. #
  135. # - It's not actually used. The standard recommendation is to
  136. # serialize the *state_dict* of a model, not the model itself
  137. # (since this is more stable to code changes affecting the model
  138. # serialization), and the state dict saves "data" only, thus
  139. # stripping the backward hooks. In some cases, hooks are
  140. # essential to the well-functioning of a model (e.g., DDP),
  141. # but DDP already manages re-adding the hooks!
  142. #
  143. # - We didn't serialize them in many cases. Prior to #10220, we
  144. # were dropping backward hooks in ForkingPickler. We "fixed" this
  145. # to be convenient with other serialization sites, but lack of
  146. # serializing backward hooks wasn't actually the root cause of
  147. # the bug.
  148. #
  149. # With these cases in mind, we have decided that a better strategy
  150. # is to just NOT serialize hooks at all.
  151. #
  152. # Since this is a BC-breaking change, we should warn when we previously
  153. # serialized a hook, but no longer do so. This will be done by adding a special
  154. # sentinel property to hooks will be used to suppress this warning. If a hook
  155. # has the property _torch_serialize_ignore, we will not emit a warning if we
  156. # attempt to serialize a Tensor with this hook attached to it.
  157. #
  158. # By the way, when _backward_hooks is skipped, we must give an EMPTY
  159. # OrderedDict(), if you pass a None you'll run afoul #12219.
  160. # TODO: Once we decide to break serialization FC, `storage` no longer needs to
  161. # be a TypedStorage
  162. def _rebuild_tensor(storage, storage_offset, size, stride):
  163. # first construct a tensor with the correct dtype/device
  164. t = torch.empty((0,), dtype=storage.dtype, device=storage._untyped_storage.device)
  165. return t.set_(storage._untyped_storage, storage_offset, size, stride)
  166. def get_tensor_metadata(tensor):
  167. # Tensor's Metadata for serializing.
  168. # Currently, this only returns a dict[string, bool] specifying whether
  169. # `conj` or `neg` bit is set.
  170. assert isinstance(tensor, torch.Tensor)
  171. return torch._C._get_tensor_metadata(tensor) # type: ignore[attr-defined]
  172. def set_tensor_metadata(tensor, metadata):
  173. # See `get_tensor_metadata` above
  174. assert isinstance(metadata, dict)
  175. assert isinstance(tensor, torch.Tensor)
  176. torch._C._set_tensor_metadata(tensor, metadata) # type: ignore[attr-defined]
  177. def _restore_device_fake_mode(tensor):
  178. if torch._guards.detect_fake_mode(None) is not None:
  179. if tensor.untyped_storage()._fake_device is not None:
  180. device = _get_restore_location(tensor.untyped_storage()._fake_device)
  181. if not isinstance(device, torch.device):
  182. device = torch.device(device)
  183. tensor.fake_device = torch.device(device)
  184. return tensor
  185. def _rebuild_tensor_v2(
  186. storage,
  187. storage_offset,
  188. size,
  189. stride,
  190. requires_grad,
  191. backward_hooks,
  192. metadata=None,
  193. ):
  194. tensor = _rebuild_tensor(storage, storage_offset, size, stride)
  195. tensor.requires_grad = requires_grad
  196. if metadata:
  197. set_tensor_metadata(tensor, metadata)
  198. # NB: This line exists only for backwards compatibility; the
  199. # general expectation is that backward_hooks is an empty
  200. # OrderedDict. See Note [Don't serialize hooks]
  201. tensor._backward_hooks = backward_hooks
  202. tensor = _restore_device_fake_mode(tensor)
  203. return tensor
  204. def _rebuild_tensor_v3(
  205. storage,
  206. storage_offset,
  207. size,
  208. stride,
  209. requires_grad,
  210. backward_hooks,
  211. dtype,
  212. metadata=None,
  213. ):
  214. t = torch.empty(
  215. (0,),
  216. dtype=dtype,
  217. device=storage._untyped_storage.device,
  218. requires_grad=requires_grad,
  219. )
  220. t.set_(storage._untyped_storage, storage_offset, size, stride)
  221. if metadata:
  222. set_tensor_metadata(t, metadata)
  223. t._backward_hooks = backward_hooks
  224. t = _restore_device_fake_mode(t)
  225. return t
  226. _sparse_tensors_to_validate: list["torch.Tensor"] = []
  227. # In _legacy_load() in serialization.py we unpickle storages after the sparse
  228. # tensors have been already unpickled. Those storages contain data necessary for
  229. # validating sparse tensors: indices and values. That's why sparse tensors are
  230. # first unpickled without any validation, and then this function is called just
  231. # before _legacy_load() returns, so that all the sparse tensors can be validated
  232. # in bulk.
  233. #
  234. # The same procedure must be followed by _load() in serialization.py because due
  235. # to Pickler semantics, we have to use the same (non-validating) function for
  236. # unpickling sparse tensors, regardless of the caller.
  237. def _validate_loaded_sparse_tensors():
  238. if not torch.sparse.check_sparse_tensor_invariants().is_enabled():
  239. # Skip sparse tensor invariants validation for better
  240. # performance. See check_sparse_tensor_invariants
  241. # documentation for how to control sparse tensor invariants
  242. # checking.
  243. _sparse_tensors_to_validate.clear()
  244. return
  245. try:
  246. # We disable pinning check (see check_pinning=False below) to
  247. # avoid gh-153143. In fact, pinning check is unnecessary
  248. # anywhy when loading sparse data from external sources.
  249. for t in _sparse_tensors_to_validate:
  250. if t.layout is torch.sparse_coo:
  251. torch._validate_sparse_coo_tensor_args(
  252. t._indices(),
  253. t._values(),
  254. t.size(),
  255. t.is_coalesced(),
  256. check_pinning=False,
  257. )
  258. elif t.layout in {
  259. torch.sparse_csr,
  260. torch.sparse_csc,
  261. torch.sparse_bsr,
  262. torch.sparse_bsc,
  263. }:
  264. # TODO: Validation currently involves an expensive traversal
  265. # on CPU, which may include a device transfer.
  266. if t.layout in {torch.sparse_csr, torch.sparse_bsr}:
  267. compressed_indices, plain_indices = (
  268. t.crow_indices(),
  269. t.col_indices(),
  270. )
  271. else:
  272. compressed_indices, plain_indices = (
  273. t.ccol_indices(),
  274. t.row_indices(),
  275. )
  276. torch._validate_sparse_compressed_tensor_args(
  277. compressed_indices,
  278. plain_indices,
  279. t.values(),
  280. t.size(),
  281. t.layout,
  282. check_pinning=False,
  283. )
  284. else:
  285. raise NotImplementedError(
  286. f"_validate_loaded_sparse_tensors for layout `{t.layout}`"
  287. )
  288. finally:
  289. _sparse_tensors_to_validate.clear()
  290. def _rebuild_sparse_tensor(layout, data):
  291. """
  292. Rebuilds a sparse tensor from its sparse storage representation.
  293. Args:
  294. layout (str): The sparse storage layout of the tensor.
  295. data (tuple): The tensor's sparse storage representation.
  296. """
  297. if layout == torch.sparse_coo:
  298. if len(data) == 3:
  299. # For BC:
  300. indices, values, size = data
  301. is_coalesced = None
  302. else:
  303. indices, values, size, is_coalesced = data
  304. result = torch.sparse_coo_tensor(
  305. indices, values, size, check_invariants=False, is_coalesced=is_coalesced
  306. )
  307. _sparse_tensors_to_validate.append(result)
  308. return result
  309. elif layout in {
  310. torch.sparse_csr,
  311. torch.sparse_csc,
  312. torch.sparse_bsr,
  313. torch.sparse_bsc,
  314. }:
  315. compressed_indices, plain_indices, values, size = data
  316. result = torch.sparse_compressed_tensor(
  317. compressed_indices,
  318. plain_indices,
  319. values,
  320. size,
  321. layout=layout,
  322. check_invariants=False,
  323. )
  324. _sparse_tensors_to_validate.append(result)
  325. return result
  326. raise NotImplementedError(f"rebuilding sparse tensor for layout {layout}")
  327. def _rebuild_nested_tensor(buffer, sizes, strides, storage_offsets):
  328. return torch._nested_view_from_buffer(buffer, sizes, strides, storage_offsets)
  329. def _rebuild_device_tensor_from_cpu_tensor(data, dtype, device, requires_grad):
  330. device = _get_restore_location(device)
  331. tensor = data.to(dtype=dtype, device=device)
  332. tensor.requires_grad = requires_grad
  333. return tensor
  334. def _rebuild_device_tensor_from_numpy(data, dtype, device, requires_grad):
  335. device = _get_restore_location(device)
  336. tensor = torch.from_numpy(data).to(dtype=dtype, device=device)
  337. tensor.requires_grad = requires_grad
  338. return tensor
  339. # Should not be used, only here to be able to load Tensors serialized with older versions of pytorch
  340. _rebuild_xla_tensor = _rebuild_device_tensor_from_numpy
  341. def _rebuild_meta_tensor_no_storage(dtype, size, stride, requires_grad):
  342. return torch.empty_strided(
  343. size, stride, dtype=dtype, device="meta", requires_grad=requires_grad
  344. )
  345. def _rebuild_wrapper_subclass(
  346. cls,
  347. dtype,
  348. size,
  349. stride,
  350. storage_offset,
  351. layout,
  352. device,
  353. requires_grad,
  354. ):
  355. device = _get_restore_location(device)
  356. return torch.Tensor._make_wrapper_subclass(
  357. cls,
  358. size,
  359. strides=stride,
  360. dtype=dtype,
  361. storage_offset=storage_offset,
  362. layout=layout,
  363. device=device,
  364. requires_grad=requires_grad,
  365. )
  366. # TODO: Once we decide to break serialization FC, `storage` no longer needs to
  367. # be a TypedStorage
  368. def _rebuild_qtensor(
  369. storage,
  370. storage_offset,
  371. size,
  372. stride,
  373. quantizer_params,
  374. requires_grad,
  375. backward_hooks,
  376. ):
  377. qscheme = quantizer_params[0]
  378. if qscheme == torch.per_tensor_affine:
  379. _, scale, zero_point = quantizer_params
  380. tensor = torch._empty_affine_quantized(
  381. size,
  382. scale=scale,
  383. zero_point=zero_point,
  384. dtype=storage.dtype,
  385. device=storage.device,
  386. )
  387. elif qscheme in (torch.per_channel_affine, torch.per_channel_affine_float_qparams):
  388. _, scales, zero_points, axis = quantizer_params
  389. if type(scales) is list and type(zero_points) is list:
  390. if qscheme == torch.per_channel_affine:
  391. scales = torch.tensor(scales, dtype=torch.double, device=storage.device)
  392. zero_points = torch.tensor(
  393. zero_points, dtype=torch.long, device=storage.device
  394. )
  395. else:
  396. scales = torch.tensor(scales, dtype=torch.float, device=storage.device)
  397. zero_points = torch.tensor(
  398. zero_points, dtype=torch.float, device=storage.device
  399. )
  400. tensor = torch._empty_per_channel_affine_quantized(
  401. size,
  402. scales=scales,
  403. zero_points=zero_points,
  404. axis=axis,
  405. dtype=storage.dtype,
  406. device=storage.device,
  407. )
  408. else:
  409. raise RuntimeError(f"Can't deserialize quantized tensor with qscheme {qscheme}")
  410. tensor.set_(storage, storage_offset, size, stride)
  411. tensor.requires_grad = requires_grad
  412. # NB: This line exists only for backwards compatibility; the
  413. # general expectation is that backward_hooks is an empty
  414. # OrderedDict. See Note [Don't serialize hooks]
  415. tensor._backward_hooks = backward_hooks
  416. return tensor
  417. def _rebuild_parameter(data, requires_grad, backward_hooks):
  418. param = torch.nn.Parameter(data, requires_grad)
  419. # NB: This line exists only for backwards compatibility; the
  420. # general expectation is that backward_hooks is an empty
  421. # OrderedDict. See Note [Don't serialize hooks]
  422. param._backward_hooks = backward_hooks
  423. return param
  424. def _rebuild_parameter_with_state(data, requires_grad, backward_hooks, state):
  425. param = torch.nn.Parameter(data, requires_grad)
  426. # NB: This line exists only for backwards compatibility; the
  427. # general expectation is that backward_hooks is an empty
  428. # OrderedDict. See Note [Don't serialize hooks]
  429. param._backward_hooks = backward_hooks
  430. # Restore state on Parameter like python attr.
  431. param = _set_obj_state(param, state)
  432. return param
  433. def _get_obj_state(obj):
  434. # Get the state of the python subclass
  435. # This loosely mimics the function on the object class but since Tensor do not inherit
  436. # from it, we cannot call that function directly
  437. # https://github.com/python/cpython/blob/c83919bd635f4433f1c6ae8504996a9fe3c215e5/Objects/typeobject.c#L4891
  438. # Note that starting with Python 3.11, this `__getstate__` is always defined and thus
  439. # the else branch will never be taken.
  440. getstate_fn = getattr(obj, "__getstate__", None)
  441. if getstate_fn:
  442. state = getstate_fn()
  443. else:
  444. slots_to_save = copyreg._slotnames(obj.__class__) # type: ignore[attr-defined]
  445. if slots_to_save:
  446. state = (
  447. obj.__dict__,
  448. {
  449. name: getattr(obj, name)
  450. for name in slots_to_save
  451. if hasattr(obj, name)
  452. },
  453. )
  454. else:
  455. state = obj.__dict__
  456. return state
  457. def _set_obj_state(obj, state):
  458. if isinstance(state, tuple):
  459. if not len(state) == 2:
  460. raise RuntimeError(f"Invalid serialized state: {state}")
  461. dict_state = state[0]
  462. slots_state = state[1]
  463. else:
  464. dict_state = state
  465. slots_state = None
  466. # Starting with Python 3.11, the __dict__ attribute is lazily created
  467. # and is serialized as None when not needed.
  468. if dict_state:
  469. for k, v in dict_state.items():
  470. setattr(obj, k, v)
  471. if slots_state:
  472. for k, v in slots_state.items():
  473. setattr(obj, k, v)
  474. return obj
  475. def _import_dotted_name(name):
  476. components = name.split(".")
  477. obj = __import__(components[0])
  478. for component in components[1:]:
  479. obj = getattr(obj, component)
  480. return obj
  481. def _flatten_dense_tensors(tensors):
  482. """Flatten dense tensors into a contiguous 1D buffer. Assume tensors are of
  483. same dense type.
  484. Since inputs are dense, the resulting tensor will be a concatenated 1D
  485. buffer. Element-wise operation on this buffer will be equivalent to
  486. operating individually.
  487. Args:
  488. tensors (Iterable[Tensor]): dense tensors to flatten.
  489. Returns:
  490. A contiguous 1D buffer containing input tensors.
  491. """
  492. return torch._C._nn.flatten_dense_tensors(tensors)
  493. def _flatten_sparse_tensors(tensors):
  494. """Flatten sparse tensors into two contiguous 1D buffers, one of indices and
  495. one of values. Assume tensors are of same sparse type.
  496. Args:
  497. tensors (Iterable[Tensor]): sparse tensors to flatten.
  498. Returns:
  499. A tuple of two contiguous 1D buffers, one containing input tensors'
  500. indices and the other containing the values.
  501. """
  502. flat_indices = torch._C._nn.flatten_dense_tensors(
  503. [torch.Tensor._indices(t) for t in tensors]
  504. )
  505. flat_values = torch._C._nn.flatten_dense_tensors(
  506. [torch.Tensor._values(t) for t in tensors]
  507. )
  508. return flat_indices, flat_values
  509. def _unflatten_dense_tensors(flat, tensors):
  510. """View a flat buffer using the sizes of tensors. Assume that tensors are of
  511. same dense type, and that flat is given by _flatten_dense_tensors.
  512. Args:
  513. flat (Tensor): flattened dense tensors to unflatten.
  514. tensors (Iterable[Tensor]): dense tensors whose sizes will be used to
  515. unflatten flat.
  516. Returns:
  517. Unflattened dense tensors with sizes same as tensors and values from
  518. flat.
  519. """
  520. return torch._C._nn.unflatten_dense_tensors(flat, tensors)
  521. def _unflatten_sparse_tensors(flat, tensors):
  522. """View flat buffer (containing indices and values) using the sizes of
  523. tensors. Assume that tensors are of same sparse type, and that flat is given
  524. by _flatten_sparse_tensors.
  525. Args:
  526. flat (tuple(Tensor, Tensor)): flattened indices and values of sparse
  527. tensors to unflatten.
  528. tensors (Iterable[Tensor]): sparse tensors whose sizes will be used to
  529. unflatten flat.
  530. Returns:
  531. Unflattened sparse tensors with sizes same as tensors and values from
  532. flat.
  533. """
  534. flat_indices, flat_values = flat
  535. indices = torch._C._nn.unflatten_dense_tensors(
  536. flat_indices, [torch.Tensor._indices(t) for t in tensors]
  537. )
  538. values = torch._C._nn.unflatten_dense_tensors(
  539. flat_values, [torch.Tensor._values(t) for t in tensors]
  540. )
  541. outputs = []
  542. for t, i, v in zip(tensors, indices, values):
  543. outputs.append(t.new(i, v, t.size()))
  544. return tuple(outputs)
  545. def _reorder_tensors_as(tensors, ordered_tensors):
  546. """Assume that tensors are of same order as ordered_tensors within their
  547. types, e.g., from _take_tensors. Reorder them to be of same order as
  548. ordered_tensors.
  549. Args:
  550. tensors (Iterable[Tensor]): tensors to be reordered. They should be of
  551. the same order as ordered_tensors within their own types.
  552. ordered_tensors (Iterable[Tensor]): tensors whose order will be the
  553. reference.
  554. Returns:
  555. Ordered tuple of tensors with contents from tensors and order of
  556. ordered_tensors.
  557. """
  558. type_dict = defaultdict(list)
  559. for tensor in tensors:
  560. type_dict[tensor.type()].append(tensor)
  561. type_dict_ = {t: iter(coll) for t, coll in type_dict.items()}
  562. return tuple(next(type_dict_[tensor.type()]) for tensor in ordered_tensors)
  563. def _take_tensors(tensors, size_limit):
  564. """Group tensors into chunks. This generator yields a chunk at each time,
  565. each containing tensors of same type up to certain byte limit in total size.
  566. Args:
  567. tensors (Sequence): A sequence of tensors to be separated into chunks.
  568. size_limit (int): The limit of each chunk in bytes.
  569. Yields:
  570. Blocks of tensors of same type and within size_limit. The yielded
  571. tensors are only ordered as the original sequence within its types.
  572. """
  573. buf_dict: defaultdict[str, list] = defaultdict(lambda: [[], 0])
  574. for tensor in tensors:
  575. t = tensor.type()
  576. if tensor.is_sparse:
  577. indices = torch.Tensor._indices(tensor)
  578. values = torch.Tensor._values(tensor)
  579. size = (
  580. indices.numel() * indices.element_size()
  581. + values.numel() * values.element_size()
  582. )
  583. else:
  584. size = tensor.numel() * tensor.element_size()
  585. buf_and_size = buf_dict[t]
  586. if buf_and_size[1] + size > size_limit and buf_and_size[1] > 0:
  587. yield buf_and_size[0]
  588. buf_and_size = buf_dict[t] = [[], 0]
  589. buf_and_size[0].append(tensor)
  590. buf_and_size[1] += size
  591. for buf, _ in buf_dict.values():
  592. if len(buf) > 0:
  593. yield buf
  594. # annotation decorator to get annotations in a way that is compatible
  595. # with both Python 2 and 3
  596. def annotate(ret, **kwargs):
  597. def dec(fun):
  598. fun.__annotations__ = dict(kwargs)
  599. fun.__annotations__["return"] = ret
  600. return fun
  601. return dec
  602. def render_call(fn, args, kwargs):
  603. str_fn = torch.overrides.resolve_name(fn)
  604. if str_fn is None:
  605. str_fn = str(fn)
  606. str_args: list[str] = []
  607. with torch._tensor_str.printoptions(threshold=0, edgeitems=0):
  608. str_args.extend(repr(a) for a in args)
  609. str_args.extend(f"{k}={repr(v)}" for k, v in kwargs.items())
  610. r = f"{str_fn}({', '.join(str_args)})"
  611. return r
  612. # NOTE [ Python Traceback Reference Cycle Problem ]
  613. #
  614. # When using sys.exc_info(), it is important to **not** store the exc_info[2],
  615. # which is the traceback, because otherwise you will run into the traceback
  616. # reference cycle problem, i.e., the traceback holding reference to the frame,
  617. # and the frame (which holds reference to all the object in its temporary scope)
  618. # holding reference the traceback.
  619. class KeyErrorMessage(str):
  620. r"""str subclass that returns itself in repr"""
  621. __slots__ = ()
  622. def __repr__(self):
  623. return self
  624. class ExceptionWrapper:
  625. r"""Wraps an exception plus traceback to communicate across threads"""
  626. def __init__(self, exc_info=None, where="in background"):
  627. # It is important that we don't store exc_info, see
  628. # NOTE [ Python Traceback Reference Cycle Problem ]
  629. if exc_info is None:
  630. exc_info = sys.exc_info()
  631. self.exc_type = exc_info[0]
  632. self.exc_msg = "".join(traceback.format_exception(*exc_info))
  633. self.where = where
  634. def reraise(self):
  635. r"""Reraises the wrapped exception in the current thread"""
  636. # Format a message such as: "Caught ValueError in DataLoader worker
  637. # process 2. Original Traceback:", followed by the traceback.
  638. msg = f"Caught {self.exc_type.__name__} {self.where}.\nOriginal {self.exc_msg}"
  639. if self.exc_type == KeyError:
  640. # KeyError calls repr() on its argument (usually a dict key). This
  641. # makes stack traces unreadable. It will not be changed in Python
  642. # (https://bugs.python.org/issue2651), so we work around it.
  643. msg = KeyErrorMessage(msg)
  644. elif getattr(self.exc_type, "message", None):
  645. # Some exceptions have first argument as non-str but explicitly
  646. # have message field
  647. raise self.exc_type(message=msg)
  648. try:
  649. exception = self.exc_type(msg)
  650. except Exception:
  651. # If the exception takes multiple arguments or otherwise can't
  652. # be constructed, don't try to instantiate since we don't know how to
  653. raise RuntimeError(msg) from None
  654. raise exception
  655. def _get_available_device_type():
  656. if torch.cuda.is_available():
  657. return "cuda"
  658. if torch.backends.mps.is_available():
  659. return "mps"
  660. if hasattr(torch, "xpu") and torch.xpu.is_available(): # type: ignore[attr-defined]
  661. return "xpu"
  662. if hasattr(torch, "mtia") and torch.mtia.is_available():
  663. return "mtia"
  664. custom_backend_name = torch._C._get_privateuse1_backend_name()
  665. custom_device_mod = getattr(torch, custom_backend_name, None)
  666. if custom_device_mod and custom_device_mod.is_available():
  667. return custom_backend_name
  668. # add more available device types here
  669. return None
  670. def _get_device_attr(get_member):
  671. device_type = _get_available_device_type()
  672. if device_type and device_type.lower() == "cuda":
  673. return get_member(torch.cuda)
  674. if device_type and device_type.lower() == "mps":
  675. return get_member(torch.mps)
  676. if device_type and device_type.lower() == "xpu":
  677. return get_member(torch.xpu) # type: ignore[attr-defined]
  678. if device_type and device_type.lower() == "mtia":
  679. return get_member(torch.mtia)
  680. if device_type == torch._C._get_privateuse1_backend_name():
  681. return get_member(getattr(torch, device_type))
  682. # add more available device types here
  683. return None
  684. def _get_current_device_index():
  685. # current device index
  686. return _get_device_attr(lambda m: m.current_device())
  687. def _get_all_device_indices():
  688. # all device index
  689. return _get_device_attr(lambda m: list(range(m.device_count())))
  690. def _get_devices_properties(device_ids):
  691. # all device properties
  692. return [_get_device_attr(lambda m: m.get_device_properties(i)) for i in device_ids]
  693. def get_current_device_index() -> int:
  694. r"""Checks if there are CUDA devices available and
  695. returns the device index of the current default CUDA device.
  696. Returns -1 in case there are no CUDA devices available.
  697. Arguments: ``None``
  698. """
  699. if torch.cuda.device_count() > 0:
  700. return torch.cuda.current_device()
  701. return -1
  702. def _get_device_index(
  703. device: Any,
  704. optional: bool = False,
  705. allow_cpu: bool = False,
  706. ) -> int:
  707. r"""Gets the device index from :attr:`device`, which can be a torch.device
  708. object, a Python integer, or ``None``.
  709. If :attr:`device` is a torch.device object, returns the device index if it
  710. has index. Note that for a device without a specified index,
  711. i.e., ``torch.device('xxx')``, this will return the current default
  712. device of that type if :attr:`optional` is ``True``. If :attr:`allow_cpu` is ``True``,
  713. CPU devices will be accepted and ``-1`` will be returned in this case.
  714. If :attr:`device` is a Python integer, it is returned as is.
  715. If :attr:`device` is ``None``, this will return the current default
  716. device of the supported runtime platform if :attr:`optional` is ``True``.
  717. i.e., the current default CUDA device will be returned if CUDA runtime is supported.
  718. """
  719. if isinstance(device, str):
  720. device = torch.device(device)
  721. device_idx: Optional[int] = None
  722. if isinstance(device, torch.device):
  723. if not allow_cpu and device.type == "cpu":
  724. raise ValueError(f"Expected a non cpu device, but got: {device}")
  725. device_idx = -1 if device.type == "cpu" else device.index
  726. if isinstance(device, int):
  727. device_idx = device
  728. if device_idx is None:
  729. if optional:
  730. # The eager API _get_current_device_index uses `lambda` functions which are
  731. # not supported in JIT and hence not scriptable. The JIT equivalent API to get
  732. # the current device index is `get_current_device_index()` which can
  733. # be scripted. We use is_scripting to check the mode we are in and call the
  734. # appropriate API.
  735. if torch.jit.is_scripting():
  736. device_idx = get_current_device_index()
  737. else:
  738. device_idx = _get_current_device_index()
  739. else:
  740. raise ValueError(
  741. f"Expected a torch.device with a specified index or an integer, but got:{device}"
  742. )
  743. return device_idx
  744. def _handle_complex(tensor):
  745. """
  746. Returns a real view of a tensor if complex dtype else just the tensor
  747. need to check if a UninitializedParameter because otherwise checking is_complex is an error for a LazyModule
  748. """
  749. return (
  750. torch.view_as_real(tensor)
  751. if not isinstance(tensor, torch.nn.UninitializedParameter)
  752. and tensor.is_complex()
  753. else tensor
  754. )
  755. def _element_size(dtype):
  756. """
  757. Returns the element size for a dtype, in bytes
  758. """
  759. if not isinstance(dtype, torch.dtype):
  760. raise RuntimeError(f"expected torch.dtype, but got {type(dtype)}")
  761. if dtype.is_complex:
  762. return torch.finfo(dtype).bits >> 2
  763. elif dtype.is_floating_point:
  764. return torch.finfo(dtype).bits >> 3
  765. elif dtype == torch.bool:
  766. # NOTE: torch.bool is not supported in torch.iinfo()
  767. return 1
  768. else:
  769. return torch.iinfo(dtype).bits >> 3
  770. class _ClassPropertyDescriptor:
  771. def __init__(self, fget, fset=None):
  772. self.fget = fget
  773. def __get__(self, instance, owner=None):
  774. if owner is None:
  775. owner = type(instance)
  776. return self.fget.__get__(instance, owner)()
  777. def classproperty(func):
  778. if not isinstance(func, (classmethod, staticmethod)):
  779. func = classmethod(func)
  780. return _ClassPropertyDescriptor(func)
  781. if TYPE_CHECKING:
  782. # TorchScript does not support `@deprecated`
  783. # This is a workaround to avoid breaking TorchScript
  784. @deprecated(
  785. "`torch._utils.is_compiling` is deprecated. Use `torch.compiler.is_compiling` instead.",
  786. category=FutureWarning,
  787. )
  788. def is_compiling() -> bool:
  789. return torch.compiler.is_compiling()
  790. else:
  791. def is_compiling() -> bool:
  792. """
  793. Indicates whether we are tracing/compiling with torch.compile() or torch.export().
  794. """
  795. warnings.warn( # use `warnings.warn` instead of `@deprecated`
  796. "`torch._utils.is_compiling` is deprecated. Use `torch.compiler.is_compiling` instead.",
  797. # FutureWarning, # TorchScript does not support Warning type
  798. stacklevel=2,
  799. )
  800. return torch.compiler.is_compiling()
  801. def _functionalize_sync(t):
  802. # This code lives in python instead of C++ since conditioning on a certain python subclass
  803. # is much more of a pain in C++.
  804. from torch._subclasses.functional_tensor import FunctionalTensor
  805. if isinstance(t, FunctionalTensor):
  806. # If a FunctionalTensorMode is active while syncing, we don't want it to intercept any ops that get called
  807. # when we sync our inner tensor.
  808. # Why?
  809. # (1) If there are input mutations in the graph, then they will be re-applied during
  810. # AOTAutograd when we call _sync() from inside of our functionalization kernels.
  811. # (2) _sync() causes us to regenerate our updated the tensor from the updated base,
  812. # which dispatches to a bunch of view ops
  813. # (3) The input to these view ops is our inner FunctionalTensorWrapper
  814. # (since the sync was called from C++), not the python FunctionalTensor
  815. # (4) if a python FunctionalTensorMode is active, it will complain when it intercepts
  816. # the view op, since it will see an input that is a C++ FunctionalTensorWrapper
  817. # (aka a normal torch.Tensor) instead of a python `FunctionalTensor).
  818. maybe_functional_mode = torch._C._unset_dispatch_mode(
  819. torch._C._TorchDispatchModeKey.FUNCTIONAL
  820. )
  821. try:
  822. torch._functionalize_sync(t.elem) # type: ignore[attr-defined]
  823. finally:
  824. if maybe_functional_mode is not None:
  825. torch._C._set_dispatch_mode(maybe_functional_mode)
  826. else:
  827. torch._functionalize_sync(t) # type: ignore[attr-defined]
  828. @functools.lru_cache(2)
  829. def _get_device_module(device_type: str):
  830. device_module = getattr(torch, device_type, None)
  831. if device_module is None:
  832. raise RuntimeError(
  833. f"Device '{device_type}' does not have a corresponding module registered as 'torch.{device_type}'."
  834. )
  835. return device_module
  836. def _dummy_type(name: str) -> type:
  837. def get_err_fn(is_init: bool):
  838. def err_fn(obj, *args, **kwargs):
  839. if is_init:
  840. class_name = obj.__class__.__name__
  841. else:
  842. class_name = obj.__name__
  843. raise RuntimeError(f"Tried to instantiate dummy base class {class_name}")
  844. return err_fn
  845. return type(
  846. name, (object,), {"__init__": get_err_fn(True), "__new__": get_err_fn(False)}
  847. )
  848. class _LazySeedTracker:
  849. # Since seeding is memory-less, only track the latest seed.
  850. # Note: `manual_seed_all` followed by `manual_seed` overwrites
  851. # the seed on current device. We track the order of **latest**
  852. # calls between these two API.
  853. def __init__(self):
  854. self.manual_seed_all_cb = None
  855. self.manual_seed_cb = None
  856. self.call_order = []
  857. def queue_seed_all(self, cb, traceback):
  858. self.manual_seed_all_cb = (cb, traceback)
  859. # update seed_all to be latest
  860. self.call_order = [self.manual_seed_cb, self.manual_seed_all_cb]
  861. def queue_seed(self, cb, traceback):
  862. self.manual_seed_cb = (cb, traceback)
  863. # update seed to be latest
  864. self.call_order = [self.manual_seed_all_cb, self.manual_seed_cb]
  865. def get_calls(self) -> list:
  866. return self.call_order
  867. logger = logging.getLogger(__name__)
  868. P = ParamSpec("P")
  869. class CallbackRegistry(Generic[P]):
  870. def __init__(self, name: str):
  871. self.name = name
  872. self.callback_list: list[Callable[P, None]] = []
  873. def add_callback(self, cb: Callable[P, None]) -> None:
  874. self.callback_list.append(cb)
  875. def fire_callbacks(self, *args: P.args, **kwargs: P.kwargs) -> None:
  876. for cb in self.callback_list:
  877. try:
  878. cb(*args, **kwargs)
  879. except Exception:
  880. logger.exception(
  881. "Exception in callback for %s registered with gpu trace", self.name
  882. )
  883. def try_import(module_name: str) -> Optional[ModuleType]:
  884. # Implementation based on
  885. # https://docs.python.org/3/library/importlib.html#checking-if-a-module-can-be-imported
  886. if (module := sys.modules.get(module_name, None)) is not None:
  887. return module
  888. if (spec := importlib.util.find_spec(module_name)) is not None:
  889. module = importlib.util.module_from_spec(spec)
  890. sys.modules[module_name] = module
  891. # https://docs.python.org/3/library/importlib.html#importlib.machinery.ModuleSpec.loader
  892. # "The finder should always set this attribute"
  893. assert spec.loader is not None, "The loader attribute should always be set"
  894. spec.loader.exec_module(module)
  895. return module
  896. return None
  897. # IMPORT_MAPPING and NAME_MAPPING are adapted from https://github.com/python/cpython/blob/main/Lib/_compat_pickle.py
  898. # for use in the weights_only Unpickler.
  899. IMPORT_MAPPING = {
  900. "__builtin__": "builtins",
  901. "copy_reg": "copyreg",
  902. "Queue": "queue",
  903. "repr": "reprlib",
  904. "_abcoll": "collections.abc",
  905. # Non-mutual mappings.
  906. "UserDict": "collections",
  907. "UserList": "collections",
  908. "UserString": "collections",
  909. "whichdb": "dbm",
  910. "StringIO": "io",
  911. "cStringIO": "io",
  912. }
  913. # This contains rename rules that are easy to handle. We ignore the more
  914. # complex stuff (e.g. mapping the names in the urllib and types modules).
  915. # These rules should be run before import names are fixed.
  916. NAME_MAPPING = {
  917. ("__builtin__", "xrange"): ("builtins", "range"),
  918. ("__builtin__", "reduce"): ("functools", "reduce"),
  919. ("__builtin__", "intern"): ("sys", "intern"),
  920. ("__builtin__", "unichr"): ("builtins", "chr"),
  921. ("__builtin__", "unicode"): ("builtins", "str"),
  922. ("__builtin__", "long"): ("builtins", "int"),
  923. ("itertools", "izip"): ("builtins", "zip"),
  924. ("itertools", "imap"): ("builtins", "map"),
  925. ("itertools", "ifilter"): ("builtins", "filter"),
  926. ("itertools", "ifilterfalse"): ("itertools", "filterfalse"),
  927. ("itertools", "izip_longest"): ("itertools", "zip_longest"),
  928. ("UserDict", "IterableUserDict"): ("collections", "UserDict"),
  929. ("UserList", "UserList"): ("collections", "UserList"),
  930. ("UserString", "UserString"): ("collections", "UserString"),
  931. # Non-mutual mappings.
  932. ("__builtin__", "basestring"): ("builtins", "str"),
  933. ("exceptions", "StandardError"): ("builtins", "Exception"),
  934. ("UserDict", "UserDict"): ("collections", "UserDict"),
  935. }