forked from pytorch/pytorch
-
Notifications
You must be signed in to change notification settings - Fork 0
/
__init__.py
2766 lines (2184 loc) · 96.2 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
"""
The torch package contains data structures for multi-dimensional
tensors and defines mathematical operations over these tensors.
Additionally, it provides many utilities for efficient serialization of
Tensors and arbitrary types, and other useful utilities.
It has a CUDA counterpart, that enables you to run your tensor computations
on an NVIDIA GPU with compute capability >= 3.0.
"""
# mypy: allow-untyped-defs
import builtins
import ctypes
import glob
import importlib
import inspect
import math
import os
import platform
import sys
import textwrap
import threading
from typing import (
Any as _Any,
Callable as _Callable,
Dict as _Dict,
get_origin as _get_origin,
Optional as _Optional,
overload as _overload,
Set as _Set,
Tuple as _Tuple,
Type as _Type,
TYPE_CHECKING,
TypeVar as _TypeVar,
Union as _Union,
)
from typing_extensions import ParamSpec as _ParamSpec
if TYPE_CHECKING:
from .types import IntLikeType
# multipy/deploy is setting this import before importing torch, this is the most
# reliable way we have to detect if we're running within deploy.
# https://github.com/pytorch/multipy/blob/d60f34ad38c371e441fe7ffdb77a3c3dda5a5d19/multipy/runtime/interpreter/interpreter_impl.cpp#L134-L137
def _running_with_deploy() -> builtins.bool:
return sys.modules.get("torch._meta_registrations", None) is object
from torch._utils import (
_functionalize_sync as _sync,
_import_dotted_name,
classproperty,
)
from torch._utils_internal import (
get_file_path,
prepare_multiprocessing_environment,
USE_GLOBAL_DEPS,
USE_RTLD_GLOBAL_WITH_LIBTORCH,
)
# TODO(torch_deploy) figure out how to freeze version.py in fbcode build
if _running_with_deploy():
__version__ = "torch-deploy-1.8"
# TODO: Remove this ugly hack when deploy typing extensions are updated to 4.10+
if not TYPE_CHECKING:
import typing_extensions
_TypeIs = typing_extensions.TypeGuard
typing_extensions.TypeIs = _TypeIs
else:
from typing_extensions import TypeIs as _TypeIs
from torch.torch_version import __version__ as __version__
__all__ = [
"BoolStorage",
"BoolTensor",
"ByteStorage",
"ByteTensor",
"CharStorage",
"CharTensor",
"DoubleStorage",
"DoubleTensor",
"FloatStorage",
"FloatTensor",
"GradScaler",
"IntStorage",
"IntTensor",
"LongStorage",
"LongTensor",
"ShortStorage",
"ShortTensor",
"SymBool",
"SymFloat",
"SymInt",
"Tensor",
"TypedStorage",
"UntypedStorage",
"are_deterministic_algorithms_enabled",
"autocast",
"chunk",
"compile",
"cond",
"enable_grad",
"export",
"get_default_device",
"get_deterministic_debug_mode",
"get_device_module",
"get_float32_matmul_precision",
"get_rng_state",
"inference_mode",
"initial_seed",
"is_deterministic_algorithms_warn_only_enabled",
"is_storage",
"is_tensor",
"is_warn_always_enabled",
"load",
"lobpcg",
"manual_seed",
"matmul",
"no_grad",
"rand",
"randn",
"save",
"seed",
"set_default_device",
"set_default_tensor_type",
"set_deterministic_debug_mode",
"set_float32_matmul_precision",
"set_printoptions",
"set_rng_state",
"set_warn_always",
"split",
"stack",
"sym_float",
"sym_fresh_size",
"sym_int",
"sym_ite",
"sym_max",
"sym_min",
"sym_not",
"sym_sum",
"typename",
"unravel_index",
"use_deterministic_algorithms",
"vmap",
]
# Please keep this list sorted
assert __all__ == sorted(__all__)
################################################################################
# Load the extension module
################################################################################
if sys.platform == "win32":
def _load_dll_libraries() -> None:
import sysconfig
from torch.version import cuda as cuda_version
pfiles_path = os.getenv("ProgramFiles", r"C:\Program Files")
py_dll_path = os.path.join(sys.exec_prefix, "Library", "bin")
th_dll_path = os.path.join(os.path.dirname(__file__), "lib")
usebase_path = os.path.join(
sysconfig.get_config_var("userbase"), "Library", "bin"
)
# When users create a virtualenv that inherits the base environment,
# we will need to add the corresponding library directory into
# DLL search directories. Otherwise, it will rely on `PATH` which
# is dependent on user settings.
if sys.exec_prefix != sys.base_exec_prefix:
base_py_dll_path = os.path.join(sys.base_exec_prefix, "Library", "bin")
else:
base_py_dll_path = ""
dll_paths = [
p
for p in (th_dll_path, py_dll_path, base_py_dll_path, usebase_path)
if os.path.exists(p)
]
if not builtins.any(
os.path.exists(os.path.join(p, "nvToolsExt64_1.dll")) for p in dll_paths
):
nvtoolsext_dll_path = os.path.join(
os.getenv(
"NVTOOLSEXT_PATH",
os.path.join(pfiles_path, "NVIDIA Corporation", "NvToolsExt"),
),
"bin",
"x64",
)
else:
nvtoolsext_dll_path = ""
if cuda_version and builtins.all(
not glob.glob(os.path.join(p, "cudart64*.dll")) for p in dll_paths
):
cuda_version_1 = cuda_version.replace(".", "_")
cuda_path_var = "CUDA_PATH_V" + cuda_version_1
default_path = os.path.join(
pfiles_path, "NVIDIA GPU Computing Toolkit", "CUDA", f"v{cuda_version}"
)
cuda_path = os.path.join(os.getenv(cuda_path_var, default_path), "bin")
else:
cuda_path = ""
dll_paths.extend(
p for p in (nvtoolsext_dll_path, cuda_path) if os.path.exists(p)
)
kernel32 = ctypes.WinDLL("kernel32.dll", use_last_error=True)
with_load_library_flags = hasattr(kernel32, "AddDllDirectory")
prev_error_mode = kernel32.SetErrorMode(0x0001)
kernel32.LoadLibraryW.restype = ctypes.c_void_p
if with_load_library_flags:
kernel32.LoadLibraryExW.restype = ctypes.c_void_p
for dll_path in dll_paths:
os.add_dll_directory(dll_path)
try:
ctypes.CDLL("vcruntime140.dll")
ctypes.CDLL("msvcp140.dll")
if platform.machine() != "ARM64":
ctypes.CDLL("vcruntime140_1.dll")
except OSError:
print(
textwrap.dedent(
"""
Microsoft Visual C++ Redistributable is not installed, this may lead to the DLL load failure.
It can be downloaded at https://aka.ms/vs/16/release/vc_redist.x64.exe
"""
).strip()
)
dlls = glob.glob(os.path.join(th_dll_path, "*.dll"))
path_patched = False
for dll in dlls:
is_loaded = False
if with_load_library_flags:
res = kernel32.LoadLibraryExW(dll, None, 0x00001100)
last_error = ctypes.get_last_error()
if res is None and last_error != 126:
err = ctypes.WinError(last_error)
err.strerror += (
f' Error loading "{dll}" or one of its dependencies.'
)
raise err
elif res is not None:
is_loaded = True
if not is_loaded:
if not path_patched:
os.environ["PATH"] = ";".join(dll_paths + [os.environ["PATH"]])
path_patched = True
res = kernel32.LoadLibraryW(dll)
if res is None:
err = ctypes.WinError(ctypes.get_last_error())
err.strerror += (
f' Error loading "{dll}" or one of its dependencies.'
)
raise err
kernel32.SetErrorMode(prev_error_mode)
_load_dll_libraries()
del _load_dll_libraries
def _preload_cuda_deps(lib_folder: str, lib_name: str) -> None:
"""Preloads cuda deps if they could not be found otherwise."""
# Should only be called on Linux if default path resolution have failed
assert platform.system() == "Linux", "Should only be called on Linux"
lib_path = None
for path in sys.path:
nvidia_path = os.path.join(path, "nvidia")
if not os.path.exists(nvidia_path):
continue
candidate_lib_paths = glob.glob(
os.path.join(nvidia_path, lib_folder, "lib", lib_name)
)
if candidate_lib_paths and not lib_path:
lib_path = candidate_lib_paths[0]
if lib_path:
break
if not lib_path:
raise ValueError(f"{lib_name} not found in the system path {sys.path}")
ctypes.CDLL(lib_path)
# See Note [Global dependencies]
def _load_global_deps() -> None:
if _running_with_deploy() or platform.system() == "Windows":
return
# Determine the file extension based on the platform
lib_ext = ".dylib" if platform.system() == "Darwin" else ".so"
lib_name = f"libtorch_global_deps{lib_ext}"
here = os.path.abspath(__file__)
global_deps_lib_path = os.path.join(os.path.dirname(here), "lib", lib_name)
try:
ctypes.CDLL(global_deps_lib_path, mode=ctypes.RTLD_GLOBAL)
except OSError as err:
# Can only happen for wheel with cuda libs as PYPI deps
# As PyTorch is not purelib, but nvidia-*-cu12 is
cuda_libs: _Dict[str, str] = {
"cublas": "libcublas.so.*[0-9]",
"cudnn": "libcudnn.so.*[0-9]",
"cuda_nvrtc": "libnvrtc.so.*[0-9]",
"cuda_runtime": "libcudart.so.*[0-9]",
"cuda_cupti": "libcupti.so.*[0-9]",
"cufft": "libcufft.so.*[0-9]",
"curand": "libcurand.so.*[0-9]",
"nvjitlink": "libnvJitLink.so.*[0-9]",
"cusparse": "libcusparse.so.*[0-9]",
"cusolver": "libcusolver.so.*[0-9]",
"nccl": "libnccl.so.*[0-9]",
"nvtx": "libnvToolsExt.so.*[0-9]",
}
is_cuda_lib_err = [
lib for lib in cuda_libs.values() if lib.split(".")[0] in err.args[0]
]
if not is_cuda_lib_err:
raise err
for lib_folder, lib_name in cuda_libs.items():
_preload_cuda_deps(lib_folder, lib_name)
ctypes.CDLL(global_deps_lib_path, mode=ctypes.RTLD_GLOBAL)
if (USE_RTLD_GLOBAL_WITH_LIBTORCH or os.getenv("TORCH_USE_RTLD_GLOBAL")) and (
_running_with_deploy() or platform.system() != "Windows"
):
# Do it the hard way. You might want to load libtorch with RTLD_GLOBAL in a
# few circumstances:
#
# 1. You're in a build environment (e.g., fbcode) where
# libtorch_global_deps is not available, but you still need
# to get mkl to link in with RTLD_GLOBAL or it will just
# not work.
#
# 2. You're trying to run PyTorch under UBSAN and you need
# to ensure that only one copy of libtorch is loaded, so
# vptr checks work properly
#
# If you're using this setting, you must verify that all the libraries
# you load consistently use the same libstdc++, or you may have
# mysterious segfaults.
#
old_flags = sys.getdlopenflags()
sys.setdlopenflags(os.RTLD_GLOBAL | os.RTLD_LAZY)
from torch._C import * # noqa: F403
sys.setdlopenflags(old_flags)
del old_flags
else:
# Easy way. You want this most of the time, because it will prevent
# C++ symbols from libtorch clobbering C++ symbols from other
# libraries, leading to mysterious segfaults.
#
# If building in an environment where libtorch_global_deps isn't available
# like parts of fbsource, but where RTLD_GLOBAL causes segfaults, you will
# want USE_RTLD_GLOBAL_WITH_LIBTORCH = False and USE_GLOBAL_DEPS = False
#
# See Note [Global dependencies]
if USE_GLOBAL_DEPS:
_load_global_deps()
from torch._C import * # noqa: F403
class SymInt:
"""
Like an int (including magic methods), but redirects all operations on the
wrapped node. This is used in particular to symbolically record operations
in the symbolic shape workflow.
"""
def __init__(self, node):
# This field MUST be named node; C++ binding code assumes that this
# class has a field named node that stores SymNode
self.node = node
def __bool__(self):
return builtins.bool(self != 0)
def __int__(self):
return self.node.int_()
def __index__(self):
return self.node.int_()
# Magic methods installed by torch.fx.experimental.sym_node
def __round__(self, ndigits=None):
return self
def __truediv__(self, other):
if isinstance(other, (builtins.float, SymFloat)):
return sym_float(self).__float_truediv__(other)
if not isinstance(other, (builtins.int, SymInt)):
return NotImplemented
return self.__int_truediv__(other)
def __rtruediv__(self, other):
if isinstance(other, (builtins.float, SymFloat)):
return sym_float(self).__rfloat_truediv__(other)
if not isinstance(other, (builtins.int, SymInt)):
return NotImplemented
return self.__rint_truediv__(other)
def __floordiv__(self, other):
if isinstance(other, (builtins.float, SymFloat)):
return sym_float(math.floor(sym_float(self) / other))
if not isinstance(other, (builtins.int, SymInt)):
return NotImplemented
return self.__int_floordiv__(other)
def __rfloordiv__(self, other):
if isinstance(other, (builtins.float, SymFloat)):
return sym_float(math.floor(other / sym_float(self)))
if not isinstance(other, (builtins.int, SymInt)):
return NotImplemented
return self.__rint_floordiv__(other)
# nb: complex is impossible to handle correctly lol, with
# negative base and integral float need to diverge semantics and
# just always return complex. Neener neener pretend this problem
# doesn't exist
def __pow__(self, other):
if isinstance(other, (builtins.float, SymFloat)):
return sym_float(self).__pow__(other)
if not isinstance(other, (builtins.int, SymInt)):
return NotImplemented
# Guards! This guard is necessary because we need to know it to
# determine the output type of this operation
if other >= 0:
return self.__pow_by_natural__(other)
else:
# Mercifully, when the exponent is negative, Python just promotes
# to doubles and does a float pow:
#
# if (Py_SIZE(b) < 0 && c == NULL) {
# /* if exponent is negative and there's no modulus:
# return a float. This works because we know
# that this calls float_pow() which converts its
# arguments to double. */
# Py_DECREF(a);
# Py_DECREF(b);
# return PyFloat_Type.tp_as_number->nb_power(v, w, x);
# }
return sym_float(self).__pow__(sym_float(other))
def __rpow__(self, other):
if isinstance(other, (builtins.float, SymFloat)):
return sym_float(self).__rpow__(other)
if not isinstance(other, (builtins.int, SymInt)):
return NotImplemented
if self >= 0: # self is exponent
return self.__rpow_by_natural__(other)
else:
return sym_float(self).__rpow__(sym_float(other))
def __eq__(self, other: object) -> builtins.bool:
raise TypeError("type stub not overridden")
def __lt__(self, other) -> builtins.bool:
raise TypeError("type stub not overridden")
def __gt__(self, other) -> builtins.bool:
raise TypeError("type stub not overridden")
def __le__(self, other) -> builtins.bool:
raise TypeError("type stub not overridden")
def __ge__(self, other) -> builtins.bool:
raise TypeError("type stub not overridden")
def __add__(self, other) -> "SymInt":
raise TypeError("type stub not overridden")
def __radd__(self, other) -> "SymInt":
raise TypeError("type stub not overridden")
def __rmul__(self, other) -> "SymInt":
raise TypeError("type stub not overridden")
def __mod__(self, other: "IntLikeType") -> "SymInt":
raise TypeError("type stub not overridden")
def __mul__(self, other) -> "SymInt":
raise TypeError("type stub not overridden")
def __pow_by_natural__(self, other) -> "SymInt":
raise TypeError("type stub not overridden")
def __rpow_by_natural__(self, other) -> "SymInt":
raise TypeError("type stub not overridden")
def __int_truediv__(self, other) -> "SymFloat":
raise TypeError("type stub not overridden")
def __rint_truediv__(self, other) -> "SymFloat":
raise TypeError("type stub not overridden")
def __int_floordiv__(self, other) -> "SymFloat":
raise TypeError("type stub not overridden")
def __rint_floordiv__(self, other) -> "SymFloat":
raise TypeError("type stub not overridden")
def __sym_max__(self, other):
raise TypeError("type stub not overridden")
def __sym_min__(self, other):
raise TypeError("type stub not overridden")
def __sym_float__(self):
raise TypeError("type stub not overridden")
def __neg__(self):
raise TypeError("type stub not overridden")
def __sub__(self, other: "IntLikeType") -> "SymInt":
raise TypeError("type stub not overridden")
def __rsub__(self, other: "IntLikeType") -> "SymInt":
raise TypeError("type stub not overridden")
def __and__(self, other) -> "SymInt":
raise TypeError("type stub not overridden")
def __or__(self, other) -> "SymInt":
raise TypeError("type stub not overridden")
def __repr__(self):
return self.node._graph_repr()
def _sympy_(self):
return self.node.expr
def __hash__(self) -> builtins.int:
if self.node.is_nested_int():
return hash(self.node.nested_int())
else:
# We could support constant SymInts as well, but not doing it for now
raise TypeError("unhashable type: non-nested SymInt")
# TODO: Force specialization
# This can't be done because the TypeError here is load bearing
# for einops
# https://github.com/arogozhnikov/einops/blob/6181e1e95dc58c00a3143c1726da1c6ee0463164/einops/einops.py#L237
# return hash(builtins.int(self))
def as_integer_ratio(self) -> _Tuple["SymInt", builtins.int]:
"""Represent this int as an exact integer ratio"""
return self, 1
def bit_length(self) -> builtins.int:
# TODO: A more relaxed guard is possible here, where you guard to
# allow all integer quantities which would result in the same bit
# length. We can also just make a dedicated Sympy function for
# computing this quantity and represent it symbolically.
return builtins.int(self).bit_length()
def conjugate(self) -> "SymInt":
return self
class SymFloat:
"""
Like an float (including magic methods), but redirects all operations on the
wrapped node. This is used in particular to symbolically record operations
in the symbolic shape workflow.
"""
def __init__(self, node):
# This field MUST be named node; C++ binding code assumes that this
# class has a field named node that stores SymNode
self.node = node
def __truediv__(self, other):
if not isinstance(other, (builtins.int, builtins.float, SymInt, SymFloat)):
return NotImplemented
return self.__float_truediv__(sym_float(other))
def __rtruediv__(self, other):
if not isinstance(other, (builtins.int, builtins.float, SymInt, SymFloat)):
return NotImplemented
return self.__rfloat_truediv__(sym_float(other))
def __floordiv__(self, other):
if not isinstance(other, (builtins.int, builtins.float, SymInt, SymFloat)):
return NotImplemented
return sym_float(math.floor(self / sym_float(other)))
def __rfloordiv__(self, other):
if not isinstance(other, (builtins.int, builtins.float, SymInt, SymFloat)):
return NotImplemented
return sym_float(math.floor(sym_float(other) / self))
def __bool__(self):
return self.node.bool_()
def __float__(self):
return self.node.guard_float("", 0)
# Symbolic power does NOT work with negative base, this is to avoid
# potential complex outputs
def __pow__(self, other):
if not isinstance(other, (builtins.int, builtins.float, SymInt, SymFloat)):
return NotImplemented
torch._check(self >= 0)
return self.__float_pow__(other)
def __rpow__(self, other):
if not isinstance(other, (builtins.int, builtins.float, SymInt, SymFloat)):
return NotImplemented
torch._check(other >= 0)
return self.__rfloat_pow__(other)
# Magic methods installed by torch.fx.experimental.sym_node
def __eq__(self, other: object) -> builtins.bool:
raise TypeError("type stub not overridden")
def __lt__(self, other) -> builtins.bool:
raise TypeError("type stub not overridden")
def __gt__(self, other) -> builtins.bool:
raise TypeError("type stub not overridden")
def __le__(self, other) -> builtins.bool:
raise TypeError("type stub not overridden")
def __ge__(self, other) -> builtins.bool:
raise TypeError("type stub not overridden")
def __float_pow__(self, other) -> "SymFloat":
raise TypeError("type stub not overridden")
def __rfloat_pow__(self, other) -> "SymFloat":
raise TypeError("type stub not overridden")
def __float_truediv__(self, other) -> "SymFloat":
raise TypeError("type stub not overridden")
def __rfloat_truediv__(self, other) -> "SymFloat":
raise TypeError("type stub not overridden")
def __trunc__(self):
raise TypeError("type stub not overridden")
def __sym_max__(self, other):
raise TypeError("type stub not overridden")
def __sym_min__(self, other):
raise TypeError("type stub not overridden")
def __sym_int__(self):
raise TypeError("type stub not overridden")
def is_integer(self):
"""Return True if the float is an integer."""
raise TypeError("type stub not overridden")
def as_integer_ratio(self) -> _Tuple[builtins.int, builtins.int]:
"""Represent this float as an exact integer ratio"""
return builtins.float(self).as_integer_ratio()
def __repr__(self):
return self.node._graph_repr()
def _sympy_(self):
return self.node.expr
def __hash__(self):
return hash(builtins.float(self))
def conjugate(self) -> "SymFloat":
"""Returns the complex conjugate of the float."""
return self
def hex(self) -> str:
"""Returns the hexadecimal representation of the float."""
return self.node.guard_float("", 0).hex()
class SymBool:
"""
Like an bool (including magic methods), but redirects all operations on the
wrapped node. This is used in particular to symbolically record operations
in the symbolic shape workflow.
Unlike regular bools, regular boolean operators will force extra guards instead
of symbolically evaluate. Use the bitwise operators instead to handle this.
"""
def __init__(self, node):
# This field MUST be named node; C++ binding code assumes that this
# class has a field named node that stores SymNode
self.node = node
def __bool__(self):
return self.node.bool_()
def __int__(self):
return builtins.int(self.node.bool_())
# Magic methods installed by torch.fx.experimental.sym_node
def __and__(self, other) -> "SymBool":
raise TypeError("type stub not overridden")
def __or__(self, other) -> "SymBool":
raise TypeError("type stub not overridden")
# We very carefully define __sym_not__, and not a number of other
# plausible alternatives:
#
# - We do not override __not__ because this is not a real magic
# method; you cannot override the meaning of the not builtin in
# Python. We use the name 'sym_not' to clarify that in user code you
# cannot use the builtin not or operator.not_ or operator.__not__ and
# hit this magic method; you must use our custom sym_not operator.
#
# - We do not override the __invert__ method because SymBool is
# meant to be usable in situations where bool is expected. However,
# bitwise negation ~a does the wrong thing with booleans (because
# bool is a subclass of int, so ~1 = -2 which is not falseish.)
# This would be a giant footgun, so we get around it by defining
# our own operator. Note that bitwise and/or do the right thing,
# so we reuse the conventional operators there for readability.
#
def __sym_not__(self) -> "SymBool":
raise TypeError("type stub not overridden")
def __sym_ite__(self, then_val, else_val):
raise TypeError("type stub not overridden")
def __eq__(self, other) -> builtins.bool:
raise TypeError("type stub not overridden")
def __repr__(self):
return self.node._graph_repr()
def _sympy_(self):
return self.node.expr
def __hash__(self):
if self.node.is_constant():
return hash(self.node.bool_())
else:
# Force specialization
return hash(builtins.bool(self))
def sym_not(a):
r"""SymInt-aware utility for logical negation.
Args:
a (SymBool or bool): Object to negate
"""
import sympy
if overrides.has_torch_function_unary(a):
return overrides.handle_torch_function(sym_not, (a,), a)
if hasattr(a, "__sym_not__"):
return a.__sym_not__()
if isinstance(a, sympy.Basic):
return ~a # type: ignore[operator]
return not a
def sym_float(a):
r"""SymInt-aware utility for float casting.
Args:
a (SymInt, SymFloat, or object): Object to cast
"""
if overrides.has_torch_function_unary(a):
return overrides.handle_torch_function(sym_float, (a,), a)
if isinstance(a, SymFloat):
return a
elif hasattr(a, "__sym_float__"):
return a.__sym_float__()
return builtins.float(a) # type: ignore[operator]
def sym_int(a):
r"""SymInt-aware utility for int casting.
Args:
a (SymInt, SymFloat, or object): Object to cast
"""
if overrides.has_torch_function_unary(a):
return overrides.handle_torch_function(sym_int, (a,), a)
if isinstance(a, SymInt):
return a
elif isinstance(a, SymFloat):
return math.trunc(a)
return builtins.int(a) # type: ignore[operator]
def sym_max(a, b):
"""
SymInt-aware utility for max which avoids branching on a < b.
Unlike builtins.max(), this only works for int/float, and it always
promotes to float if any argument is float (unlike builtins.max, which
will faithfully preserve the type of the input argument).
"""
if overrides.has_torch_function((a, b)):
return overrides.handle_torch_function(sym_max, (a, b), a, b)
if isinstance(a, (SymInt, SymFloat)):
return a.__sym_max__(b)
elif isinstance(b, (SymInt, SymFloat)):
# Due to promotion semantics, this is operator is commutative:
# max(1, 1.0) === max(1.0, 1) === 1.0
return b.__sym_max__(a)
# TODO: Probably can make bool work too, just lazy
all_types, float_types = __all_and_float_types()
assert isinstance(a, all_types), type(a)
assert isinstance(b, all_types), type(b)
if isinstance(a, float_types) or isinstance(b, float_types):
return builtins.float(builtins.max(a, b))
else:
return builtins.max(a, b)
def __all_and_float_types() -> _Tuple[_Tuple[_Type, ...], _Tuple[_Type, ...]]:
try:
import numpy as np
all_types: _Tuple[_Type, ...] = (
np.integer,
np.floating,
builtins.int,
builtins.float,
)
float_types: _Tuple[_Type, ...] = (np.floating, builtins.float)
except ModuleNotFoundError:
all_types = (builtins.int, builtins.float)
float_types = (builtins.float,)
return all_types, float_types
def sym_min(a, b):
"""SymInt-aware utility for min()."""
if overrides.has_torch_function((a, b)):
return overrides.handle_torch_function(sym_min, (a, b), a, b)
if isinstance(a, (SymInt, SymFloat)):
return a.__sym_min__(b)
elif isinstance(b, (SymInt, SymFloat)):
return b.__sym_min__(a)
all_types, float_types = __all_and_float_types()
assert isinstance(a, all_types), type(a)
assert isinstance(b, all_types), type(b)
if isinstance(a, float_types) or isinstance(b, float_types):
return builtins.float(builtins.min(a, b))
else:
return builtins.min(a, b)
def sym_sum(args):
"""
N-ary add which is faster to compute for long lists than iterated binary
addition. Only does something special for integers.
"""
if overrides.has_torch_function(args):
return overrides.handle_torch_function(sym_sum, args, args)
found = None
for a in args:
if not isinstance(a, (SymInt, builtins.int)):
return builtins.sum(args)
if isinstance(a, SymInt):
found = a.node
if found is None:
return builtins.sum(args)
from torch.fx.experimental.sym_node import to_node, wrap_node
return wrap_node(found.sym_sum(tuple(to_node(found, a) for a in args)))
# Drop in replacement for math.sqrt, math.sin, math.cos etc
def _get_sym_math_fn(name):
def fn(a):
if overrides.has_torch_function_unary(a):
return overrides.handle_torch_function(fn, (a,), a)
if isinstance(a, SymInt):
a = torch.sym_float(a)
if hasattr(a, f"__sym_{name}__"):
return getattr(a, f"__sym_{name}__")()
return getattr(math, name)(a)
return fn
__fn, __name, __sym_name = None, "", ""
for __name in (
"sqrt",
"cos",
"cosh",
"sin",
"sinh",
"tan",
"tanh",
"asin",
"acos",
"atan",
"log2",
):
__sym_name = f"_sym_{__name}"
__fn = _get_sym_math_fn(__name)
__fn.__qualname__ = __fn.__name__ = __sym_name
globals()[__sym_name] = __fn
del __fn, __name, __sym_name, _get_sym_math_fn
# Adding temporary shortcut
sym_sqrt = globals()["_sym_sqrt"]
__all__.append("sym_sqrt")
def sym_ite(b, t, f):
if overrides.has_torch_function((b, t, f)):
return overrides.handle_torch_function(sym_ite, (b, t, f), b, t, f)
assert isinstance(b, (SymBool, builtins.bool)) and type(t) == type(f)
if isinstance(b, SymBool):
return b.__sym_ite__(t, f)
return t if b else f
# Create a fresh unbacked int, from an (possibly unbacked int) expression.
def sym_fresh_size(expr):
return torch.tensor(expr).item()
# Check to see if we can load C extensions, and if not provide some guidance
# on what the problem might be.
try:
# _initExtension is chosen (arbitrarily) as a sentinel.
from torch._C import _initExtension
except ImportError:
import torch._C as _C_for_compiled_check
# The __file__ check only works for Python 3.7 and above.
if _C_for_compiled_check.__file__ is None:
raise ImportError(
textwrap.dedent(
"""
Failed to load PyTorch C extensions:
It appears that PyTorch has loaded the `torch/_C` folder
of the PyTorch repository rather than the C extensions which
are expected in the `torch._C` namespace. This can occur when
using the `install` workflow. e.g.
$ python setup.py install && python -c "import torch"
This error can generally be solved using the `develop` workflow
$ python setup.py develop && python -c "import torch" # This should succeed
or by running Python from a different directory.
"""
).strip()
) from None
raise # If __file__ is not None the cause is unknown, so just re-raise.
# The torch._C submodule is already loaded via `from torch._C import *` above
# Make an explicit reference to the _C submodule to appease linters
from torch import _C as _C
__name, __obj = "", None
for __name in dir(_C):
if __name[0] != "_" and not __name.endswith("Base"):
__all__.append(__name)
__obj = getattr(_C, __name)
if callable(__obj) or inspect.isclass(__obj):
if __obj.__module__ != __name__: # "torch"
# TODO: fix their module from C++ side
if __name not in {
"DisableTorchFunctionSubclass",
"DisableTorchFunction",
"Generator",
}:
__obj.__module__ = __name__ # "torch"