torchdt 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- torchdt-0.1.0/LICENSE +21 -0
- torchdt-0.1.0/PKG-INFO +35 -0
- torchdt-0.1.0/README.md +1 -0
- torchdt-0.1.0/pyproject.toml +41 -0
- torchdt-0.1.0/setup.cfg +4 -0
- torchdt-0.1.0/torchdt/__init__.py +11 -0
- torchdt-0.1.0/torchdt/_build_ext.py +71 -0
- torchdt-0.1.0/torchdt/autograd.py +280 -0
- torchdt-0.1.0/torchdt/csrc/dispatcher.h +101 -0
- torchdt-0.1.0/torchdt/csrc/extension.cpp +273 -0
- torchdt-0.1.0/torchdt/csrc/lns16.cpp +137 -0
- torchdt-0.1.0/torchdt/csrc/lns16.h +8 -0
- torchdt-0.1.0/torchdt/csrc/ops_impl.cpp +1091 -0
- torchdt-0.1.0/torchdt/csrc/ops_impl.h +118 -0
- torchdt-0.1.0/torchdt/datatype.py +325 -0
- torchdt-0.1.0/torchdt/funcs/__init__.py +9 -0
- torchdt-0.1.0/torchdt/funcs/activation_funcs.py +105 -0
- torchdt-0.1.0/torchdt/funcs/arithmetic_funcs.py +193 -0
- torchdt-0.1.0/torchdt/funcs/comparison_funcs.py +194 -0
- torchdt-0.1.0/torchdt/funcs/creation_funcs.py +116 -0
- torchdt-0.1.0/torchdt/funcs/init_funcs.py +50 -0
- torchdt-0.1.0/torchdt/funcs/layer_funcs.py +56 -0
- torchdt-0.1.0/torchdt/funcs/loss_funcs.py +104 -0
- torchdt-0.1.0/torchdt/funcs/misc_funcs.py +132 -0
- torchdt-0.1.0/torchdt/funcs/unary_funcs.py +41 -0
- torchdt-0.1.0/torchdt/include/torchdt/registry.h +107 -0
- torchdt-0.1.0/torchdt/lns/__init__.py +9 -0
- torchdt-0.1.0/torchdt/lns/lns16.py +184 -0
- torchdt-0.1.0/torchdt/lns/lns32.py +184 -0
- torchdt-0.1.0/torchdt/lns/lns64.py +438 -0
- torchdt-0.1.0/torchdt/ops/__init__.py +20 -0
- torchdt-0.1.0/torchdt/ops/activation_ops.py +278 -0
- torchdt-0.1.0/torchdt/ops/arithmetic_ops.py +655 -0
- torchdt-0.1.0/torchdt/ops/base.py +525 -0
- torchdt-0.1.0/torchdt/ops/comparison_ops.py +462 -0
- torchdt-0.1.0/torchdt/ops/cpp_ops.py +153 -0
- torchdt-0.1.0/torchdt/ops/layer_ops.py +908 -0
- torchdt-0.1.0/torchdt/ops/loss_ops.py +954 -0
- torchdt-0.1.0/torchdt/ops/misc_ops.py +475 -0
- torchdt-0.1.0/torchdt/ops/triton_ops.py +2550 -0
- torchdt-0.1.0/torchdt/ops/unary_ops.py +61 -0
- torchdt-0.1.0/torchdt/optim/__init__.py +16 -0
- torchdt-0.1.0/torchdt/optim/adam.py +188 -0
- torchdt-0.1.0/torchdt/optim/lr_scheduler.py +224 -0
- torchdt-0.1.0/torchdt/optim/madam.py +175 -0
- torchdt-0.1.0/torchdt/optim/optimizer.py +33 -0
- torchdt-0.1.0/torchdt/optim/sgd.py +142 -0
- torchdt-0.1.0/torchdt/transforms.py +120 -0
- torchdt-0.1.0/torchdt.egg-info/PKG-INFO +35 -0
- torchdt-0.1.0/torchdt.egg-info/SOURCES.txt +51 -0
- torchdt-0.1.0/torchdt.egg-info/dependency_links.txt +1 -0
- torchdt-0.1.0/torchdt.egg-info/requires.txt +2 -0
- torchdt-0.1.0/torchdt.egg-info/top_level.txt +1 -0
torchdt-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 XLNS
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
torchdt-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: torchdt
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: torchdt is a python package that provides custom datatypes for training and inference in PyTorch.
|
|
5
|
+
Author-email: Alexander Krentz <alexkrentz2@gmail.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2024 XLNS
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
Project-URL: Homepage, https://github.com/akrentz6/torchdt
|
|
28
|
+
Keywords: python,pytorch,machine learning
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Requires-Dist: torch>=2.0
|
|
32
|
+
Requires-Dist: numpy
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
A Python addon package to PyTorch that provides experimentation with custom tensor datatypes for training and inference.
|
torchdt-0.1.0/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
A Python addon package to PyTorch that provides experimentation with custom tensor datatypes for training and inference.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = [
|
|
3
|
+
"setuptools>=69.0",
|
|
4
|
+
"wheel",
|
|
5
|
+
"torch>=2.0",
|
|
6
|
+
"ninja",
|
|
7
|
+
]
|
|
8
|
+
build-backend = "setuptools.build_meta"
|
|
9
|
+
|
|
10
|
+
[project]
|
|
11
|
+
name = "torchdt"
|
|
12
|
+
version = "0.1.0"
|
|
13
|
+
description = "torchdt is a python package that provides custom datatypes for training and inference in PyTorch."
|
|
14
|
+
readme = "README.md"
|
|
15
|
+
license = {file = "LICENSE"}
|
|
16
|
+
authors = [{name = "Alexander Krentz", email = "alexkrentz2@gmail.com"}]
|
|
17
|
+
keywords = ["python", "pytorch", "machine learning"]
|
|
18
|
+
dependencies = ["torch>=2.0", "numpy"]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
"Homepage" = "https://github.com/akrentz6/torchdt"
|
|
22
|
+
|
|
23
|
+
[tool.setuptools]
|
|
24
|
+
include-package-data = true
|
|
25
|
+
|
|
26
|
+
[tool.setuptools.package-data]
|
|
27
|
+
"torchdt" = [
|
|
28
|
+
"include/torchdt/**/*.h",
|
|
29
|
+
"csrc/**/*.cpp",
|
|
30
|
+
"csrc/**/*.h",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[[tool.setuptools.ext-modules]]
|
|
34
|
+
name = "torchdt._C"
|
|
35
|
+
sources = [] # Filled dynamically
|
|
36
|
+
language = "c++"
|
|
37
|
+
extra-compile-args = ["-O3", "-std=c++17"]
|
|
38
|
+
optional = true
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.cmdclass]
|
|
41
|
+
build_ext = "torchdt._build_ext.BuildCxxExtension"
|
torchdt-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from torch.utils.cpp_extension import BuildExtension, include_paths
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import warnings
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
# The exact exception raised by a compiler/linker failure can vary
|
|
7
|
+
# between setuptools versions, so we catch the whole family.
|
|
8
|
+
try:
|
|
9
|
+
# setuptools ≥ 68
|
|
10
|
+
from setuptools.errors import (
|
|
11
|
+
CompileError,
|
|
12
|
+
LinkError,
|
|
13
|
+
PlatformError,
|
|
14
|
+
)
|
|
15
|
+
except ImportError:
|
|
16
|
+
# setuptools < 68
|
|
17
|
+
from distutils.errors import ( # type: ignore
|
|
18
|
+
CompileError,
|
|
19
|
+
LinkError,
|
|
20
|
+
DistutilsPlatformError as PlatformError,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
class BuildCxxExtension(BuildExtension):
|
|
24
|
+
"""
|
|
25
|
+
Build all .cpp files in csrc/ and include Torch headers.
|
|
26
|
+
Fails hard if compilation fails.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
user_options = BuildExtension.user_options + [
|
|
30
|
+
("no-cpp", None, "Do not build the C++ extension"),
|
|
31
|
+
]
|
|
32
|
+
boolean_options = getattr(BuildExtension, "boolean_options", []) + ["no-cpp"]
|
|
33
|
+
|
|
34
|
+
def initialize_options(self):
|
|
35
|
+
super().initialize_options()
|
|
36
|
+
self.no_cpp = False
|
|
37
|
+
|
|
38
|
+
def finalize_options(self):
|
|
39
|
+
super().finalize_options()
|
|
40
|
+
if os.environ.get("TORCHDT_NO_CPP", "").lower() in {"1", "true", "yes", "on"}:
|
|
41
|
+
self.no_cpp = True
|
|
42
|
+
|
|
43
|
+
def run(self):
|
|
44
|
+
try:
|
|
45
|
+
super().run()
|
|
46
|
+
except (CompileError, LinkError, PlatformError) as exc:
|
|
47
|
+
warnings.warn(
|
|
48
|
+
"C++ backend for xlnstorch was not built; "
|
|
49
|
+
"installation will succeed but will run in pure-python mode.",
|
|
50
|
+
RuntimeWarning,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def build_extensions(self):
|
|
54
|
+
if self.no_cpp:
|
|
55
|
+
self.extensions = []
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
src_dir = Path(__file__).resolve().parent / "csrc"
|
|
59
|
+
include_dir = Path(__file__).resolve().parent / "include"
|
|
60
|
+
cpp_files = [str(p) for p in src_dir.glob("*.cpp")]
|
|
61
|
+
|
|
62
|
+
torch_includes = include_paths()
|
|
63
|
+
|
|
64
|
+
for ext in self.extensions:
|
|
65
|
+
if ext.name == "torchdt._C":
|
|
66
|
+
ext.sources = cpp_files
|
|
67
|
+
ext.include_dirs = list(ext.include_dirs or [])
|
|
68
|
+
ext.include_dirs += torch_includes
|
|
69
|
+
ext.include_dirs += [str(include_dir)]
|
|
70
|
+
|
|
71
|
+
super().build_extensions()
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from torch.autograd.graph import get_gradient_edge
|
|
3
|
+
import functools
|
|
4
|
+
from typing import Optional, Tuple
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"DTFunction",
|
|
8
|
+
"DTNonDifferentiableFunction",
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
def _find_first_grad_tensor(args):
|
|
12
|
+
if not isinstance(args, (list, tuple)):
|
|
13
|
+
args = (args,)
|
|
14
|
+
for arg in args:
|
|
15
|
+
if isinstance(arg, torch.Tensor) and arg.requires_grad:
|
|
16
|
+
return arg
|
|
17
|
+
return None
|
|
18
|
+
|
|
19
|
+
def _cast_int(x, dtype):
|
|
20
|
+
return x.view(dtype.int_dtype) if isinstance(x, torch.Tensor) and x.dtype == dtype.float_dtype else x
|
|
21
|
+
|
|
22
|
+
def _cast_float(x, dtype):
|
|
23
|
+
return x.view(dtype.float_dtype) if isinstance(x, torch.Tensor) and x.dtype == dtype.int_dtype else x
|
|
24
|
+
|
|
25
|
+
def _cast_values(values, indices, cast_fn, dtype):
|
|
26
|
+
all_indices = indices is None
|
|
27
|
+
|
|
28
|
+
if isinstance(values, tuple):
|
|
29
|
+
return tuple(
|
|
30
|
+
cast_fn(values[i], dtype) if all_indices or i in indices else values[i]
|
|
31
|
+
for i in range(len(values))
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
elif isinstance(values, list):
|
|
35
|
+
return [
|
|
36
|
+
cast_fn(values[i], dtype) if all_indices or i in indices else values[i]
|
|
37
|
+
for i in range(len(values))
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
else:
|
|
41
|
+
return cast_fn(values, dtype) if all_indices or 0 in indices else values
|
|
42
|
+
|
|
43
|
+
# added to forward only if setup_context is not defined
|
|
44
|
+
def forward_ctx_decorator(func, cls):
|
|
45
|
+
|
|
46
|
+
@functools.wraps(func)
|
|
47
|
+
def wrapper(*args, **kwargs):
|
|
48
|
+
ctx = args[0]
|
|
49
|
+
ops = args[1]
|
|
50
|
+
inputs = args[2:]
|
|
51
|
+
|
|
52
|
+
ctx._dtype = cls.dtype
|
|
53
|
+
ctx._input_indices = cls.input_indices
|
|
54
|
+
|
|
55
|
+
cast_inputs = _cast_values(inputs, cls.input_indices, _cast_int, cls.dtype)
|
|
56
|
+
output = func(ctx, ops, *cast_inputs, **kwargs)
|
|
57
|
+
return _cast_values(output, cls.output_indices, _cast_float, cls.dtype)
|
|
58
|
+
|
|
59
|
+
return wrapper
|
|
60
|
+
|
|
61
|
+
def forward_no_ctx_decorator(func, cls):
|
|
62
|
+
|
|
63
|
+
@functools.wraps(func)
|
|
64
|
+
def wrapper(*args, **kwargs):
|
|
65
|
+
ops = args[0]
|
|
66
|
+
inputs = args[1:]
|
|
67
|
+
|
|
68
|
+
cast_inputs = _cast_values(inputs, cls.input_indices, _cast_int, cls.dtype)
|
|
69
|
+
output = func(ops, *cast_inputs, **kwargs)
|
|
70
|
+
return _cast_values(output, cls.output_indices, _cast_float, cls.dtype)
|
|
71
|
+
|
|
72
|
+
return wrapper
|
|
73
|
+
|
|
74
|
+
def setup_context_decorator(func, cls):
|
|
75
|
+
|
|
76
|
+
@functools.wraps(func)
|
|
77
|
+
def wrapper(*args, **kwargs):
|
|
78
|
+
ctx = args[0]
|
|
79
|
+
ops = args[1][0]
|
|
80
|
+
inputs = args[1][1:]
|
|
81
|
+
output = args[2]
|
|
82
|
+
|
|
83
|
+
ctx._dtype = cls.dtype
|
|
84
|
+
ctx._input_indices = cls.input_indices
|
|
85
|
+
|
|
86
|
+
cast_inputs = _cast_values(inputs, cls.input_indices, _cast_int, cls.dtype)
|
|
87
|
+
cast_output = _cast_values(output, cls.output_indices, _cast_int, cls.dtype)
|
|
88
|
+
return func(ctx, ops, cast_inputs, cast_output, **kwargs)
|
|
89
|
+
|
|
90
|
+
return wrapper
|
|
91
|
+
|
|
92
|
+
def backward_decorator(func, cls):
|
|
93
|
+
|
|
94
|
+
@functools.wraps(func)
|
|
95
|
+
def wrapper(*args, **kwargs):
|
|
96
|
+
ctx = args[0]
|
|
97
|
+
grads = args[1:]
|
|
98
|
+
|
|
99
|
+
dtype = ctx._dtype
|
|
100
|
+
ops = ctx._dtype.ops
|
|
101
|
+
input_indices = ctx._input_indices
|
|
102
|
+
|
|
103
|
+
cast_grads = _cast_values(grads, cls.output_indices, _cast_int, dtype)
|
|
104
|
+
output = func(ctx, ops, *cast_grads, **kwargs)
|
|
105
|
+
cast_output = _cast_values(output, input_indices, _cast_float, dtype)
|
|
106
|
+
|
|
107
|
+
if isinstance(output, tuple):
|
|
108
|
+
return (None,) + cast_output
|
|
109
|
+
return None, cast_output
|
|
110
|
+
|
|
111
|
+
return wrapper
|
|
112
|
+
|
|
113
|
+
class DTFunction(torch.autograd.Function):
|
|
114
|
+
"""
|
|
115
|
+
Parent class for custom autograd Functions that work with DType tensors.
|
|
116
|
+
Subclasses should implement static methods `forward` and `backward` (and
|
|
117
|
+
optionally `setup_context`).
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
output_indices: Optional[Tuple[int, ...]] = None
|
|
121
|
+
|
|
122
|
+
@classmethod
|
|
123
|
+
def _register_ops_decorator(cls, func_name, decorator):
|
|
124
|
+
func = getattr(cls, func_name)
|
|
125
|
+
|
|
126
|
+
if not getattr(func, "_is_decorated", False):
|
|
127
|
+
decorated = decorator(func, cls)
|
|
128
|
+
decorated._is_lns_decorated = True
|
|
129
|
+
setattr(cls, func_name, decorated)
|
|
130
|
+
|
|
131
|
+
def __init_subclass__(cls, **kwargs):
|
|
132
|
+
super().__init_subclass__(**kwargs)
|
|
133
|
+
|
|
134
|
+
# check if setup_context is defined in subclass or inherited
|
|
135
|
+
# from torch.autograd.function._SingleLevelFunction. If it is
|
|
136
|
+
# inherited, we only need to decorate forward and backward.
|
|
137
|
+
# If it is defined, we need to decorate setup_context instead
|
|
138
|
+
# of forward.
|
|
139
|
+
if cls.setup_context is torch.autograd.function._SingleLevelFunction.setup_context:
|
|
140
|
+
cls._register_ops_decorator("forward", forward_ctx_decorator)
|
|
141
|
+
else:
|
|
142
|
+
cls._register_ops_decorator("forward", forward_no_ctx_decorator)
|
|
143
|
+
cls._register_ops_decorator("setup_context", setup_context_decorator)
|
|
144
|
+
|
|
145
|
+
cls._register_ops_decorator("backward", backward_decorator)
|
|
146
|
+
|
|
147
|
+
@classmethod
|
|
148
|
+
def apply(cls, *args, **kwargs):
|
|
149
|
+
from torchdt import DType # avoid circular import
|
|
150
|
+
|
|
151
|
+
if kwargs:
|
|
152
|
+
raise ValueError(
|
|
153
|
+
"torch.autograd.Function does not support keyword arguments. "
|
|
154
|
+
"Please use positional arguments only."
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
input_indices = []
|
|
158
|
+
subtypes = []
|
|
159
|
+
prepped_inputs = []
|
|
160
|
+
|
|
161
|
+
for i, arg in enumerate(args):
|
|
162
|
+
if isinstance(arg, DType):
|
|
163
|
+
subtypes.append(arg.__class__)
|
|
164
|
+
input_indices.append(i)
|
|
165
|
+
prepped_inputs.append(arg._float)
|
|
166
|
+
else:
|
|
167
|
+
prepped_inputs.append(arg)
|
|
168
|
+
|
|
169
|
+
if not subtypes:
|
|
170
|
+
raise ValueError("DTFunction.apply() requires at least one DType tensor argument.")
|
|
171
|
+
|
|
172
|
+
dtype = subtypes[0]
|
|
173
|
+
if any(st != dtype for st in subtypes):
|
|
174
|
+
raise ValueError("All DType arguments to DTFunction.apply() must be of the same type.")
|
|
175
|
+
|
|
176
|
+
# perform the operation using the Ops class for this DType
|
|
177
|
+
cls.dtype = dtype
|
|
178
|
+
cls.input_indices = input_indices
|
|
179
|
+
result = super().apply(dtype.ops, *prepped_inputs)
|
|
180
|
+
del cls.input_indices
|
|
181
|
+
del cls.dtype
|
|
182
|
+
|
|
183
|
+
# get gradient edge to correctly handle grads for DType tensors
|
|
184
|
+
first_tensor = _find_first_grad_tensor(result)
|
|
185
|
+
if first_tensor is not None:
|
|
186
|
+
edge = get_gradient_edge(first_tensor)
|
|
187
|
+
|
|
188
|
+
j = 0
|
|
189
|
+
for arg in args:
|
|
190
|
+
|
|
191
|
+
# ignore non-tensor arguments
|
|
192
|
+
if not isinstance(arg, torch.Tensor):
|
|
193
|
+
continue
|
|
194
|
+
j += 1
|
|
195
|
+
|
|
196
|
+
# only register hooks for DType inputs with gradients
|
|
197
|
+
if not (isinstance(arg, dtype) and arg.requires_grad):
|
|
198
|
+
continue
|
|
199
|
+
|
|
200
|
+
arg._track_operation(edge, j - 1)
|
|
201
|
+
|
|
202
|
+
if isinstance(result, torch.Tensor):
|
|
203
|
+
return dtype(
|
|
204
|
+
result, internal=True
|
|
205
|
+
) if cls.output_indices is None or 0 in cls.output_indices else result
|
|
206
|
+
|
|
207
|
+
elif isinstance(result, list):
|
|
208
|
+
return [
|
|
209
|
+
dtype(result[i], internal=True)
|
|
210
|
+
if cls.output_indices is None or i in cls.output_indices else result[i]
|
|
211
|
+
for i in range(len(result))
|
|
212
|
+
]
|
|
213
|
+
|
|
214
|
+
elif isinstance(result, tuple):
|
|
215
|
+
return tuple(
|
|
216
|
+
dtype(result[i], internal=True)
|
|
217
|
+
if cls.output_indices is None or i in cls.output_indices else result[i]
|
|
218
|
+
for i in range(len(result))
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
return result
|
|
222
|
+
|
|
223
|
+
class DTNonDifferentiableFunction:
|
|
224
|
+
|
|
225
|
+
output_indices: Optional[Tuple[int, ...]] = None
|
|
226
|
+
|
|
227
|
+
@staticmethod
|
|
228
|
+
def forward(ops, *args, **kwargs):
|
|
229
|
+
"""Forward pass for non-differentiable function."""
|
|
230
|
+
raise NotImplementedError
|
|
231
|
+
|
|
232
|
+
@classmethod
|
|
233
|
+
def apply(cls, *args, **kwargs):
|
|
234
|
+
from torchdt import DType # avoid circular import
|
|
235
|
+
|
|
236
|
+
if kwargs:
|
|
237
|
+
raise ValueError(
|
|
238
|
+
"DTNonDifferentiableFunction does not support keyword arguments. "
|
|
239
|
+
"Please use positional arguments only."
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
subtypes = []
|
|
243
|
+
prepped_inputs = []
|
|
244
|
+
|
|
245
|
+
for i, arg in enumerate(args):
|
|
246
|
+
if isinstance(arg, DType):
|
|
247
|
+
subtypes.append(arg.__class__)
|
|
248
|
+
prepped_inputs.append(arg._int)
|
|
249
|
+
else:
|
|
250
|
+
prepped_inputs.append(arg)
|
|
251
|
+
|
|
252
|
+
if not subtypes:
|
|
253
|
+
raise ValueError("DTFunction.apply() requires at least one DType tensor argument.")
|
|
254
|
+
|
|
255
|
+
dtype = subtypes[0]
|
|
256
|
+
if any(st != dtype for st in subtypes):
|
|
257
|
+
raise ValueError("All DType arguments to DTFunction.apply() must be of the same type.")
|
|
258
|
+
|
|
259
|
+
result = cls.forward(dtype.ops, *prepped_inputs)
|
|
260
|
+
|
|
261
|
+
if isinstance(result, torch.Tensor):
|
|
262
|
+
return dtype(
|
|
263
|
+
result, internal=True
|
|
264
|
+
) if cls.output_indices is None or 0 in cls.output_indices else result
|
|
265
|
+
|
|
266
|
+
elif isinstance(result, list):
|
|
267
|
+
return [
|
|
268
|
+
dtype(result[i], internal=True)
|
|
269
|
+
if cls.output_indices is None or i in cls.output_indices else result[i]
|
|
270
|
+
for i in range(len(result))
|
|
271
|
+
]
|
|
272
|
+
|
|
273
|
+
elif isinstance(result, tuple):
|
|
274
|
+
return tuple(
|
|
275
|
+
dtype(result[i], internal=True)
|
|
276
|
+
if cls.output_indices is None or i in cls.output_indices else result[i]
|
|
277
|
+
for i in range(len(result))
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
return result
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#ifndef DISPATCHER_H
|
|
2
|
+
#define DISPATCHER_H
|
|
3
|
+
|
|
4
|
+
#include <torchdt/registry.h>
|
|
5
|
+
#include "ops_impl.h"
|
|
6
|
+
|
|
7
|
+
class Dispatcher {
|
|
8
|
+
public:
|
|
9
|
+
static Dispatcher& instance() {
|
|
10
|
+
static Dispatcher d;
|
|
11
|
+
return d;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Get cached OpsBase* for name and bitwidth.
|
|
15
|
+
// If not cached and the public registry contains an Ops<bitwidth> for this name,
|
|
16
|
+
// lazily construct an OpsImpl<bitwidth> from the stored Ops and cache it.
|
|
17
|
+
// Returns nullptr if no Ops registered for (name,bitwidth).
|
|
18
|
+
OpsBase* get(const std::string& name, size_t bitwidth) {
|
|
19
|
+
const std::string key = make_key(name, bitwidth);
|
|
20
|
+
|
|
21
|
+
// fast path: check cached map under lock
|
|
22
|
+
{
|
|
23
|
+
std::lock_guard<std::mutex> guard(mutex_);
|
|
24
|
+
auto it = impl_map_.find(key);
|
|
25
|
+
if (it != impl_map_.end()) return it->second.get();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// not cached: try to construct based on bitwidth
|
|
29
|
+
std::unique_ptr<OpsBase> created = make_impl_from_registry(name, bitwidth);
|
|
30
|
+
if (!created) return nullptr;
|
|
31
|
+
|
|
32
|
+
// store & return
|
|
33
|
+
{
|
|
34
|
+
std::lock_guard<std::mutex> guard(mutex_);
|
|
35
|
+
// double-check another thread didn't create it
|
|
36
|
+
auto it = impl_map_.find(key);
|
|
37
|
+
if (it == impl_map_.end()) {
|
|
38
|
+
OpsBase* raw = created.get();
|
|
39
|
+
impl_map_.emplace(key, std::move(created));
|
|
40
|
+
return raw;
|
|
41
|
+
} else {
|
|
42
|
+
return it->second.get();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Convenience template: typed get that returns pointer to OpsImpl<bitwidth>
|
|
48
|
+
template <size_t bitwidth>
|
|
49
|
+
OpsImpl<bitwidth>* get_typed(const std::string& name) {
|
|
50
|
+
OpsBase* base = get(name, bitwidth);
|
|
51
|
+
return static_cast<OpsImpl<bitwidth>*>(base);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private:
|
|
55
|
+
Dispatcher() = default;
|
|
56
|
+
|
|
57
|
+
// Create OpsImpl for supported bitwidths by fetching the registered Ops<bitwidth> from public registry
|
|
58
|
+
std::unique_ptr<OpsBase> make_impl_from_registry(const std::string& name, size_t bitwidth) {
|
|
59
|
+
switch (bitwidth) {
|
|
60
|
+
case 8: {
|
|
61
|
+
auto p = Registry::instance().get_ops_typed<8>(name);
|
|
62
|
+
if (!p) return nullptr;
|
|
63
|
+
return std::make_unique<OpsImpl<8>>(*p);
|
|
64
|
+
}
|
|
65
|
+
case 16: {
|
|
66
|
+
auto p = Registry::instance().get_ops_typed<16>(name);
|
|
67
|
+
if (!p) return nullptr;
|
|
68
|
+
return std::make_unique<OpsImpl<16>>(*p);
|
|
69
|
+
}
|
|
70
|
+
case 32: {
|
|
71
|
+
auto p = Registry::instance().get_ops_typed<32>(name);
|
|
72
|
+
if (!p) return nullptr;
|
|
73
|
+
return std::make_unique<OpsImpl<32>>(*p);
|
|
74
|
+
}
|
|
75
|
+
case 64: {
|
|
76
|
+
auto p = Registry::instance().get_ops_typed<64>(name);
|
|
77
|
+
if (!p) return nullptr;
|
|
78
|
+
return std::make_unique<OpsImpl<64>>(*p);
|
|
79
|
+
}
|
|
80
|
+
default:
|
|
81
|
+
return nullptr;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
std::map<std::string, std::unique_ptr<OpsBase>> impl_map_;
|
|
86
|
+
std::mutex mutex_;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// -----------------------------
|
|
90
|
+
// Helper functions (optional)
|
|
91
|
+
// -----------------------------
|
|
92
|
+
inline OpsBase* get_ops_impl(const std::string& name, size_t bitwidth) {
|
|
93
|
+
return Dispatcher::instance().get(name, bitwidth);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
template <size_t bitwidth>
|
|
97
|
+
inline OpsImpl<bitwidth>* get_ops_impl_typed(const std::string& name) {
|
|
98
|
+
return Dispatcher::instance().get_typed<bitwidth>(name);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
#endif // DISPATCHER_H
|