CoolFace
Apppublic

bbqddt2/Antigravity

sourceHugging Faceupdated 2mo agoView on Hugging Face
0likes
_dtypes.py453 linesDownload Raw Back to root
1# mypy: ignore-errors
2
3"""Define analogs of numpy dtypes supported by pytorch.
4Define the scalar types and supported dtypes and numpy <--> torch dtype mappings.
5"""
6
7import builtins
8
9import torch
10
11from . import _dtypes_impl
12
13
14# ### Scalar types ###
15
16
17class generic:
18    name = "generic"
19
20    def __new__(cls, value):
21        # NumPy scalars are modelled as 0-D arrays
22        # so a call to np.float32(4) produces a 0-D array.
23
24        from ._ndarray import asarray, ndarray
25
26        if isinstance(value, str) and value in ["inf", "nan"]:
27            value = {"inf": torch.inf, "nan": torch.nan}[value]
28
29        if isinstance(value, ndarray):
30            return value.astype(cls)
31        else:
32            return asarray(value, dtype=cls)
33
34
35##################
36# abstract types #
37##################
38
39
40class number(generic):
41    name = "number"
42
43
44class integer(number):
45    name = "integer"
46
47
48class inexact(number):
49    name = "inexact"
50
51
52class signedinteger(integer):
53    name = "signedinteger"
54
55
56class unsignedinteger(integer):
57    name = "unsignedinteger"
58
59
60class floating(inexact):
61    name = "floating"
62
63
64class complexfloating(inexact):
65    name = "complexfloating"
66
67
68_abstract_dtypes = [
69    "generic",
70    "number",
71    "integer",
72    "signedinteger",
73    "unsignedinteger",
74    "inexact",
75    "floating",
76    "complexfloating",
77]
78
79# ##### concrete types
80
81# signed integers
82
83
84class int8(signedinteger):
85    name = "int8"
86    typecode = "b"
87    torch_dtype = torch.int8
88
89
90class int16(signedinteger):
91    name = "int16"
92    typecode = "h"
93    torch_dtype = torch.int16
94
95
96class int32(signedinteger):
97    name = "int32"
98    typecode = "i"
99    torch_dtype = torch.int32
100
101
102class int64(signedinteger):
103    name = "int64"
104    typecode = "l"
105    torch_dtype = torch.int64
106
107
108# unsigned integers
109
110
111class uint8(unsignedinteger):
112    name = "uint8"
113    typecode = "B"
114    torch_dtype = torch.uint8
115
116
117class uint16(unsignedinteger):
118    name = "uint16"
119    typecode = "H"
120    torch_dtype = torch.uint16
121
122
123class uint32(signedinteger):
124    name = "uint32"
125    typecode = "I"
126    torch_dtype = torch.uint32
127
128
129class uint64(signedinteger):
130    name = "uint64"
131    typecode = "L"
132    torch_dtype = torch.uint64
133
134
135# floating point
136
137
138class float16(floating):
139    name = "float16"
140    typecode = "e"
141    torch_dtype = torch.float16
142
143
144class float32(floating):
145    name = "float32"
146    typecode = "f"
147    torch_dtype = torch.float32
148
149
150class float64(floating):
151    name = "float64"
152    typecode = "d"
153    torch_dtype = torch.float64
154
155
156class complex64(complexfloating):
157    name = "complex64"
158    typecode = "F"
159    torch_dtype = torch.complex64
160
161
162class complex128(complexfloating):
163    name = "complex128"
164    typecode = "D"
165    torch_dtype = torch.complex128
166
167
168class bool_(generic):
169    name = "bool_"
170    typecode = "?"
171    torch_dtype = torch.bool
172
173
174# name aliases
175_name_aliases = {
176    "intp": int64,
177    "int_": int64,
178    "intc": int32,
179    "byte": int8,
180    "short": int16,
181    "longlong": int64,  # XXX: is this correct?
182    "ulonglong": uint64,
183    "ubyte": uint8,
184    "half": float16,
185    "single": float32,
186    "double": float64,
187    "float_": float64,
188    "csingle": complex64,
189    "singlecomplex": complex64,
190    "cdouble": complex128,
191    "cfloat": complex128,
192    "complex_": complex128,
193}
194# We register float_ = float32 and so on
195for name, obj in _name_aliases.items():
196    vars()[name] = obj
197
198
199# Replicate this NumPy-defined way of grouping scalar types,
200# cf tests/core/test_scalar_methods.py
201sctypes = {
202    "int": [int8, int16, int32, int64],
203    "uint": [uint8, uint16, uint32, uint64],
204    "float": [float16, float32, float64],
205    "complex": [complex64, complex128],
206    "others": [bool_],
207}
208
209
210# Support mappings/functions
211
212_names = {st.name: st for cat in sctypes for st in sctypes[cat]}
213_typecodes = {st.typecode: st for cat in sctypes for st in sctypes[cat]}
214_torch_dtypes = {st.torch_dtype: st for cat in sctypes for st in sctypes[cat]}
215
216
217_aliases = {
218    "u1": uint8,
219    "i1": int8,
220    "i2": int16,
221    "i4": int32,
222    "i8": int64,
223    "b": int8,  # XXX: srsly?
224    "f2": float16,
225    "f4": float32,
226    "f8": float64,
227    "c8": complex64,
228    "c16": complex128,
229    # numpy-specific trailing underscore
230    "bool_": bool_,
231}
232
233
234_python_types = {
235    int: int64,
236    float: float64,
237    complex: complex128,
238    builtins.bool: bool_,
239    # also allow stringified names of python types
240    int.__name__: int64,
241    float.__name__: float64,
242    complex.__name__: complex128,
243    builtins.bool.__name__: bool_,
244}
245
246
247def sctype_from_string(s):
248    """Normalize a string value: a type 'name' or a typecode or a width alias."""
249    if s in _names:
250        return _names[s]
251    if s in _name_aliases:
252        return _name_aliases[s]
253    if s in _typecodes:
254        return _typecodes[s]
255    if s in _aliases:
256        return _aliases[s]
257    if s in _python_types:
258        return _python_types[s]
259    raise TypeError(f"data type {s!r} not understood")
260
261
262def sctype_from_torch_dtype(torch_dtype):
263    return _torch_dtypes[torch_dtype]
264
265
266# ### DTypes. ###
267
268
269def dtype(arg):
270    if arg is None:
271        arg = _dtypes_impl.default_dtypes().float_dtype
272    return DType(arg)
273
274
275class DType:
276    def __init__(self, arg):
277        # a pytorch object?
278        if isinstance(arg, torch.dtype):
279            sctype = _torch_dtypes[arg]
280        elif isinstance(arg, torch.Tensor):
281            sctype = _torch_dtypes[arg.dtype]
282        # a scalar type?
283        elif issubclass_(arg, generic):
284            sctype = arg
285        # a dtype already?
286        elif isinstance(arg, DType):
287            sctype = arg._scalar_type
288        # a has a right attribute?
289        elif hasattr(arg, "dtype"):
290            sctype = arg.dtype._scalar_type
291        else:
292            sctype = sctype_from_string(arg)
293        self._scalar_type = sctype
294
295    @property
296    def name(self):
297        return self._scalar_type.name
298
299    @property
300    def type(self):
301        return self._scalar_type
302
303    @property
304    def kind(self):
305        # https://numpy.org/doc/stable/reference/generated/numpy.dtype.kind.html
306        return _torch_dtypes[self.torch_dtype].name[0]
307
308    @property
309    def typecode(self):
310        return self._scalar_type.typecode
311
312    def __eq__(self, other):
313        if isinstance(other, DType):
314            return self._scalar_type == other._scalar_type
315        try:
316            other_instance = DType(other)
317        except TypeError:
318            return False
319        return self._scalar_type == other_instance._scalar_type
320
321    @property
322    def torch_dtype(self):
323        return self._scalar_type.torch_dtype
324
325    def __hash__(self):
326        return hash(self._scalar_type.name)
327
328    def __repr__(self):
329        return f'dtype("{self.name}")'
330
331    __str__ = __repr__
332
333    @property
334    def itemsize(self):
335        elem = self.type(1)
336        return elem.tensor.element_size()
337
338    def __getstate__(self):
339        return self._scalar_type
340
341    def __setstate__(self, value):
342        self._scalar_type = value
343
344
345typecodes = {
346    "All": "efdFDBbhil?",
347    "AllFloat": "efdFD",
348    "AllInteger": "Bbhil",
349    "Integer": "bhil",
350    "UnsignedInteger": "B",
351    "Float": "efd",
352    "Complex": "FD",
353}
354
355
356# ### Defaults and dtype discovery
357
358
359def set_default_dtype(fp_dtype="numpy", int_dtype="numpy"):
360    """Set the (global) defaults for fp, complex, and int dtypes.
361
362    The complex dtype is inferred from the float (fp) dtype. It has
363    a width at least twice the width of the float dtype,
364    i.e., it's complex128 for float64 and complex64 for float32.
365
366    Parameters
367    ----------
368    fp_dtype
369        Allowed values are "numpy", "pytorch" or dtype_like things which
370        can be converted into a DType instance.
371        Default is "numpy" (i.e. float64).
372    int_dtype
373        Allowed values are "numpy", "pytorch" or dtype_like things which
374        can be converted into a DType instance.
375        Default is "numpy" (i.e. int64).
376
377    Returns
378    -------
379    The old default dtype state: a namedtuple with attributes ``float_dtype``,
380    ``complex_dtypes`` and ``int_dtype``. These attributes store *pytorch*
381    dtypes.
382
383    Notes
384    ------------
385    This functions has a side effect: it sets the global state with the provided dtypes.
386
387    The complex dtype has bit width of at least twice the width of the float
388    dtype, i.e. it's complex128 for float64 and complex64 for float32.
389
390    """
391    if fp_dtype not in ["numpy", "pytorch"]:
392        fp_dtype = dtype(fp_dtype).torch_dtype
393    if int_dtype not in ["numpy", "pytorch"]:
394        int_dtype = dtype(int_dtype).torch_dtype
395
396    if fp_dtype == "numpy":
397        float_dtype = torch.float64
398    elif fp_dtype == "pytorch":
399        float_dtype = torch.float32
400    else:
401        float_dtype = fp_dtype
402
403    complex_dtype = {
404        torch.float64: torch.complex128,
405        torch.float32: torch.complex64,
406        torch.float16: torch.complex64,
407    }[float_dtype]
408
409    if int_dtype in ["numpy", "pytorch"]:
410        int_dtype = torch.int64
411
412    new_defaults = _dtypes_impl.DefaultDTypes(
413        float_dtype=float_dtype, complex_dtype=complex_dtype, int_dtype=int_dtype
414    )
415
416    # set the new global state and return the old state
417    old_defaults = _dtypes_impl.default_dtypes
418    _dtypes_impl._default_dtypes = new_defaults
419    return old_defaults
420
421
422def issubclass_(arg, klass):
423    try:
424        return issubclass(arg, klass)
425    except TypeError:
426        return False
427
428
429def issubdtype(arg1, arg2):
430    # cf https://github.com/numpy/numpy/blob/v1.24.0/numpy/core/numerictypes.py#L356-L420
431
432    # We also accept strings even if NumPy doesn't as dtypes are serialized as their
433    # string representation in dynamo's graph
434    def str_to_abstract(t):
435        if isinstance(t, str) and t in _abstract_dtypes:
436            return globals()[t]
437        return t
438
439    arg1 = str_to_abstract(arg1)
440    arg2 = str_to_abstract(arg2)
441
442    if not issubclass_(arg1, generic):
443        arg1 = dtype(arg1).type
444    if not issubclass_(arg2, generic):
445        arg2 = dtype(arg2).type
446    return issubclass(arg1, arg2)
447
448
449__all__ = ["dtype", "DType", "typecodes", "issubdtype", "set_default_dtype", "sctypes"]
450__all__ += list(_names.keys())  # noqa: PLE0605
451__all__ += list(_name_aliases.keys())  # noqa: PLE0605
452__all__ += _abstract_dtypes  # noqa: PLE0605
453