pytorch/torch/_numpy
Edward Z. Yang 9bce208dfb Replace follow_imports = silent with normal (#118414)
This is a lot of files changed! Don't panic! Here's how it works:

* Previously, we set `follow_imports = silent` for our mypy.ini configuration. Per https://mypy.readthedocs.io/en/stable/running_mypy.html#follow-imports, what this does is whenever we have an import to a module which is not listed as a file to be typechecked in mypy, we typecheck it as normal but suppress all errors that occurred in that file.
* When mypy is run inside lintrunner, the list of files is precisely the files covered by the glob in lintrunner.toml, but with files in excludes excluded.
* The top-level directive `# mypy: ignore-errors` instructs mypy to typecheck the file as normal, but ignore all errors.
* Therefore, it should be equivalent to set `follow_imports = normal`, if we put `# mypy: ignore-errors` on all files that were previously excluded from the file list.
* Having done this, we can remove the exclude list from .lintrunner.toml, since excluding a file from typechecking is baked into the files themselves.
* torch/_dynamo and torch/_inductor were previously in the exclude list, because they were covered by MYPYINDUCTOR. It is not OK to mark these as `# mypy: ignore-errors` as this will impede typechecking on the alternate configuration. So they are temporarily being checked twice, but I am suppressing the errors in these files as the configurations are not quite the same. I plan to unify the configurations so this is only a temporary state.
* There were some straggler type errors after these changes somehow, so I fixed them as needed. There weren't that many.

In the future, to start type checking a file, just remove the ignore-errors directive from the top of the file.

The codemod was done with this script authored by GPT-4:

```
import glob

exclude_patterns = [
    ...
]

for pattern in exclude_patterns:
    for filepath in glob.glob(pattern, recursive=True):
        if filepath.endswith('.py'):
            with open(filepath, 'r+') as f:
                content = f.read()
                f.seek(0, 0)
                f.write('# mypy: ignore-errors\n\n' + content)
```

Signed-off-by: Edward Z. Yang <ezyang@meta.com>

Pull Request resolved: https://github.com/pytorch/pytorch/pull/118414
Approved by: https://github.com/thiagocrepaldi, https://github.com/albanD
2024-01-27 02:44:11 +00:00
..
testing Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
__init__.py Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
_binary_ufuncs_impl.py Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
_casting_dicts.py Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
_dtypes_impl.py Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
_dtypes.py Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
_funcs_impl.py Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
_funcs.py Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
_getlimits.py Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
_ndarray.py Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
_normalizations.py Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
_reductions_impl.py Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
_ufuncs.py Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
_unary_ufuncs_impl.py Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
_util.py Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
fft.py Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
linalg.py Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
random.py Replace follow_imports = silent with normal (#118414) 2024-01-27 02:44:11 +00:00
README.md

NumPy <> PyTorch Compat Layer

This folder contains an implementation of (most of) the NumPy public API using PyTorch tensors. Note that this folder does not depend on NumPy in any way. This is a standalone implementation.

This implementation is used by Dynamo to through NumPy code and lower it into PyTorch code.

To see design decisions that went into this implementation, please see the rfc.

Structure of the code

This folder exports a drop-in replacement for the NumPy namespace and its modules linalg, fft and random via its __init__.py.

The implementation is split into files that work with PyTorch objects (PyTorch Tensors, dtypes, etc) and files that use these PyTorch-only files and convert them into functions/objects that can process all the types that the NumPy functions accept. In particular, they accept torch._numpy.dtypes or torch._numpy.ndarrays.

The PyTorch-only files are the *_impl.py files, while the wrapper files are those that do not have an *_impl.py. This creates a hierarchy, wherein, for example, _dtypes.py will import _dtypes_impl.py, but not the other way around. In particular, *_impl.py will only depend on other *_impl.py files.

As discussed in the rfc, we use types as tags in our PyTorch implementations. We then use a decorator called normalizer that will inspect these types and preprocess the inputs before sending them to the function. This preprocessing is the one in charge of mapping array-like objects into Tensors, dtype-like objects into PyTorch dtypes, implement the out= behaviour and so on.

In the files _funcs.py and _ufuncs.py we use register the normalizer decorator to all the *_impl.py functions.

In the file _ndarray.py we define the ndarray class, which is just a thin wrapper around a PyTorch tensor. We use the free functions and a bit of metaprogramming to implement many of the methods.

Adding a new function

You just need to add a function in the relevant *_impl.py file. You will need to tag the inputs with the relevant Types. After that, you can assume that the inputs are all PyTorch objects. Your function should return PyTorch tensors. The normalizer will make sure that you always get PyTorch objects. If in doubt, you can see the implementation of the normalization attached to each type annotation in the file _normalizations.py.

Debugging

It may be useful to figure out whether a given bug is caused by dynamo or the compatibility layer. You may use the compat layer in eager mode simply by changing import numpy as np by import torch._numpy as np in your program, without having to call torch.compile at all. Note that torch._numpy will be quite slow when used in eager mode, and it is in no way a replacement or an alternative to the regular PyTorch API. This should only be used as a debugging tool.