A wrapper tensor subclass used in the DispatchTracer to keep track of proxies to construct the FX graph. Wrapping something in PythonTensor implicitly detaches gradients. If something required grad, we will collect it as if it were a leaf. A consequence of detaching in this w
| 225 | |
| 226 | |
| 227 | class PythonTensor(torch.Tensor): |
| 228 | """ |
| 229 | A wrapper tensor subclass used in the DispatchTracer to keep track of |
| 230 | proxies to construct the FX graph. |
| 231 | |
| 232 | Wrapping something in PythonTensor implicitly detaches gradients. If |
| 233 | something required grad, we will collect it as if it were a leaf. A |
| 234 | consequence of detaching in this way is you need to maintain a parameter |
| 235 | cache when translating tensors into PythonTensor, so you don't create |
| 236 | multiple copies of a gradient (they are aliased, but they would count as |
| 237 | independent leaves). An alternate strategy would be to avoid implicitly |
| 238 | detaching and instead "catch" gradients as they exit the PythonTensor |
| 239 | boundary. |
| 240 | """ |
| 241 | |
| 242 | __slots__ = ["proxy", "is_immutable"] |
| 243 | |
| 244 | @staticmethod |
| 245 | def __new__( |
| 246 | cls, elem: torch.Tensor, proxy: torch.fx.Proxy, is_immutable: bool = False |
| 247 | ) -> torch.Tensor: |
| 248 | # assert not elem.requires_grad or not torch.is_grad_enabled() |
| 249 | |
| 250 | r = torch.Tensor._make_subclass(cls, elem, elem.requires_grad) |
| 251 | assert isinstance(r, PythonTensor) |
| 252 | r.is_immutable: bool = is_immutable |
| 253 | r.update_proxy(proxy) |
| 254 | return r |
| 255 | |
| 256 | def update_proxy(self, proxy: torch.fx.Proxy) -> None: |
| 257 | self.proxy = proxy |
| 258 | |
| 259 | def __repr__(self, *, tensor_contents: None = None) -> str: |
| 260 | with no_dispatch(): |
| 261 | return f"PythonTensor({self.as_subclass(torch.Tensor)})" |
| 262 | |
| 263 | @classmethod |
| 264 | def __torch_function__( |
| 265 | cls, |
| 266 | # pyre-ignore: Missing parameter annotation [2] |
| 267 | func, |
| 268 | # pyre-ignore: Missing parameter annotation [2] |
| 269 | types, |
| 270 | args: Tuple[Value, ...] = (), |
| 271 | kwargs: Optional[Dict[str, Value]] = None, |
| 272 | ) -> Value: |
| 273 | if kwargs is None: |
| 274 | kwargs = {} |
| 275 | if torch.is_inference_mode_enabled(): |
| 276 | if func is torch.nn.functional.layer_norm: |
| 277 | args, kwargs = normalize_function(func, args, kwargs) # pyre-fixme[23] |
| 278 | input, normalized_shape = args |
| 279 | normalized_shape = list(normalized_shape) |
| 280 | return cls.__torch_dispatch__( |
| 281 | torch.ops.aten.layer_norm.default, |
| 282 | types, |
| 283 | (input, normalized_shape), |
| 284 | kwargs, |
no outgoing calls
no test coverage detected