docc-pytorch 0.8.3__py3-none-any.whl
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.
- docc/pytorch/__init__.py +9 -0
- docc/pytorch/graph_parser/__init__.py +398 -0
- docc/pytorch/graph_parser/blas.py +193 -0
- docc/pytorch/graph_parser/builtin.py +61 -0
- docc/pytorch/graph_parser/convolution.py +99 -0
- docc/pytorch/graph_parser/creation.py +250 -0
- docc/pytorch/graph_parser/elementwise.py +467 -0
- docc/pytorch/graph_parser/nonlinear_activation.py +372 -0
- docc/pytorch/graph_parser/normalization.py +203 -0
- docc/pytorch/graph_parser/padding.py +64 -0
- docc/pytorch/graph_parser/pooling.py +105 -0
- docc/pytorch/graph_parser/reduction.py +342 -0
- docc/pytorch/graph_parser/reshaping.py +408 -0
- docc/pytorch/graph_parser/sparse.py +115 -0
- docc/pytorch/graph_parser/tensor.py +457 -0
- docc/pytorch/graph_parser/utils.py +1621 -0
- docc/pytorch/graph_parser/vision.py +129 -0
- docc/pytorch/pytorch_program.py +728 -0
- docc_pytorch-0.8.3.dist-info/METADATA +181 -0
- docc_pytorch-0.8.3.dist-info/RECORD +21 -0
- docc_pytorch-0.8.3.dist-info/WHEEL +5 -0
docc/pytorch/__init__.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from docc.benchmarks.rtl import try_ensure_instrumentation_ready
|
|
2
|
+
|
|
3
|
+
# Ready the counter backends (e.g. ROCm) early: they must initialize before the
|
|
4
|
+
# workload sets up its own runtime. Best-effort so importing docc never fails when
|
|
5
|
+
# instrumentation is not configured.
|
|
6
|
+
try_ensure_instrumentation_ready()
|
|
7
|
+
|
|
8
|
+
from docc.pytorch.pytorch_program import PyTorchProgram
|
|
9
|
+
from docc.pytorch.graph_parser import GraphParser
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
"""PyTorch GraphModule Parser
|
|
2
|
+
|
|
3
|
+
This module contains the PyTorch GraphModule Parser.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import torch
|
|
8
|
+
import torch.export
|
|
9
|
+
import torch.fx
|
|
10
|
+
import torch.utils._pytree
|
|
11
|
+
import torch._functorch._aot_autograd.descriptors
|
|
12
|
+
from torch.fx.node import Argument
|
|
13
|
+
|
|
14
|
+
from typing import Any
|
|
15
|
+
from typing_extensions import Buffer
|
|
16
|
+
|
|
17
|
+
from docc.sdfg import (
|
|
18
|
+
StructuredSDFGBuilder,
|
|
19
|
+
StructuredSDFG,
|
|
20
|
+
Type,
|
|
21
|
+
Tensor,
|
|
22
|
+
Scalar,
|
|
23
|
+
Pointer,
|
|
24
|
+
DebugInfo,
|
|
25
|
+
Block,
|
|
26
|
+
ConstantNode,
|
|
27
|
+
AccessNode,
|
|
28
|
+
Tasklet,
|
|
29
|
+
TaskletCode,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
from docc.pytorch.graph_parser.utils import (
|
|
33
|
+
TensorName,
|
|
34
|
+
ContainerMemory,
|
|
35
|
+
TensorInfo,
|
|
36
|
+
ContainerInfo,
|
|
37
|
+
TensorMetadata,
|
|
38
|
+
GraphParserError,
|
|
39
|
+
GraphParserBase,
|
|
40
|
+
get_node_target_name,
|
|
41
|
+
dispatch_to_module,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
import docc.pytorch.graph_parser.blas
|
|
45
|
+
import docc.pytorch.graph_parser.builtin
|
|
46
|
+
import docc.pytorch.graph_parser.convolution
|
|
47
|
+
import docc.pytorch.graph_parser.creation
|
|
48
|
+
import docc.pytorch.graph_parser.elementwise
|
|
49
|
+
import docc.pytorch.graph_parser.nonlinear_activation
|
|
50
|
+
import docc.pytorch.graph_parser.normalization
|
|
51
|
+
import docc.pytorch.graph_parser.padding
|
|
52
|
+
import docc.pytorch.graph_parser.pooling
|
|
53
|
+
import docc.pytorch.graph_parser.reduction
|
|
54
|
+
import docc.pytorch.graph_parser.reshaping
|
|
55
|
+
import docc.pytorch.graph_parser.sparse
|
|
56
|
+
import docc.pytorch.graph_parser.tensor
|
|
57
|
+
import docc.pytorch.graph_parser.vision
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class GraphParser(GraphParserBase):
|
|
61
|
+
"""
|
|
62
|
+
This is the main PyTorch GraphModule Parser class. It creates a structured SDFG from a
|
|
63
|
+
GraphModule obtained after calling torch.export and its example inputs. It dispatches to the
|
|
64
|
+
GraphParser modules for parsing individual operations.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
name: str,
|
|
70
|
+
ep: torch.export.ExportedProgram,
|
|
71
|
+
example_input: tuple[Any, ...],
|
|
72
|
+
):
|
|
73
|
+
"""
|
|
74
|
+
Intialization with the GraphModule after calling torch.export and the example inputs.
|
|
75
|
+
"""
|
|
76
|
+
super().__init__()
|
|
77
|
+
|
|
78
|
+
self.name: str = name
|
|
79
|
+
self.ep: torch.export.ExportedProgram = ep
|
|
80
|
+
self.example_input: tuple[Any, ...] = example_input
|
|
81
|
+
|
|
82
|
+
self.builder: StructuredSDFGBuilder = StructuredSDFGBuilder("__docc_" + name)
|
|
83
|
+
self._arguments: list[Any] = []
|
|
84
|
+
|
|
85
|
+
self.metadata: TensorMetadata = TensorMetadata()
|
|
86
|
+
self.result_copy: dict[TensorName, TensorName] = {}
|
|
87
|
+
|
|
88
|
+
def flatten_arg(self, arg: Argument) -> list[torch.fx.Node]:
|
|
89
|
+
"""
|
|
90
|
+
Flattens a nested tuple/list to a list of torch.fx.Node's.
|
|
91
|
+
Example: ((arg_0, arg_1), arg_2) -> ["arg_0", "arg_1", "arg_2"]
|
|
92
|
+
"""
|
|
93
|
+
result: list[torch.fx.Node] = []
|
|
94
|
+
if isinstance(arg, (tuple, list)):
|
|
95
|
+
for elem in arg:
|
|
96
|
+
result: list[torch.fx.Node] = result + self.flatten_arg(elem)
|
|
97
|
+
elif isinstance(arg, torch.fx.Node):
|
|
98
|
+
result.append(arg)
|
|
99
|
+
return result
|
|
100
|
+
|
|
101
|
+
def get_tensors_from_arg(self, arg: Argument) -> list[TensorName]:
|
|
102
|
+
"""
|
|
103
|
+
Returns a list of all tensor names form an argument. Recursively calls itself for lists and
|
|
104
|
+
tuples.
|
|
105
|
+
"""
|
|
106
|
+
return [node.name for node in self.flatten_arg(arg)]
|
|
107
|
+
|
|
108
|
+
def parse(self) -> None:
|
|
109
|
+
"""
|
|
110
|
+
Parses the GraphModule (exported program) to a structured SDFG. This is done in two steps.
|
|
111
|
+
The first step is the pre-parsing step, in which all tensor information are collected and
|
|
112
|
+
written to the tensor metadata. Except for input and output arguments those tensors do not
|
|
113
|
+
have an underlying container yet.
|
|
114
|
+
The second step is the parsing step, in which all operations are actually translated to SDFG
|
|
115
|
+
operations. Containers are conservatively create along the way and also stored in the tensor
|
|
116
|
+
metadata. At the end all allocated memory is freed.
|
|
117
|
+
"""
|
|
118
|
+
nodes = self.ep.graph_module.graph.nodes
|
|
119
|
+
|
|
120
|
+
# Collect all outputs for out args
|
|
121
|
+
for node in nodes:
|
|
122
|
+
if node.op == "placeholder":
|
|
123
|
+
self.parse_placeholder(node)
|
|
124
|
+
elif node.op == "call_function":
|
|
125
|
+
if get_node_target_name(node.target) in (
|
|
126
|
+
"aten._assert_tensor_metadata.default",
|
|
127
|
+
):
|
|
128
|
+
continue # Skip output-less ops
|
|
129
|
+
|
|
130
|
+
sdfg_tensors: tuple[Tensor | None, ...] = self.get_node_sdfg_tensors(
|
|
131
|
+
node
|
|
132
|
+
)
|
|
133
|
+
deps: list[TensorName] = self.get_tensors_from_arg(node.args)
|
|
134
|
+
if len(sdfg_tensors) == 1:
|
|
135
|
+
self.metadata.add_tensor(
|
|
136
|
+
node.name, TensorInfo(node.name, sdfg_tensors[0])
|
|
137
|
+
)
|
|
138
|
+
for dep in deps:
|
|
139
|
+
self.metadata.add_dependency(node.name, dep)
|
|
140
|
+
else:
|
|
141
|
+
contained_names: list[TensorName] = []
|
|
142
|
+
for i in range(len(sdfg_tensors)):
|
|
143
|
+
tensor_name: TensorName = f"{node.name}_{i}"
|
|
144
|
+
contained_names.append(tensor_name)
|
|
145
|
+
self.metadata.add_tensor(
|
|
146
|
+
tensor_name, TensorInfo(tensor_name, sdfg_tensors[i])
|
|
147
|
+
)
|
|
148
|
+
for dep in deps:
|
|
149
|
+
self.metadata.add_dependency(tensor_name, dep)
|
|
150
|
+
self.metadata.add_tensor_tuple(node.name, contained_names)
|
|
151
|
+
elif node.op == "output":
|
|
152
|
+
output_nodes: list[torch.fx.Node] = self.flatten_arg(node.args)
|
|
153
|
+
for output_node in output_nodes:
|
|
154
|
+
if not output_node.name in self.ep.graph_signature.user_outputs:
|
|
155
|
+
continue
|
|
156
|
+
|
|
157
|
+
sdfg_type: Type = self.get_node_sdfg_type(output_node)
|
|
158
|
+
out_name: str = output_node.name
|
|
159
|
+
# FX names like "sin" would shadow C math functions in generated code.
|
|
160
|
+
out_container: str = f"__daisy__{out_name}"
|
|
161
|
+
|
|
162
|
+
if self.metadata.has_container(
|
|
163
|
+
out_name
|
|
164
|
+
) or self.metadata.has_container(out_container):
|
|
165
|
+
continue
|
|
166
|
+
|
|
167
|
+
if not self.metadata.tensor(out_name).is_tight() or isinstance(
|
|
168
|
+
sdfg_type, Scalar
|
|
169
|
+
):
|
|
170
|
+
if not isinstance(sdfg_type, Pointer):
|
|
171
|
+
sdfg_type: Type = Pointer(sdfg_type)
|
|
172
|
+
new_out_name: str = f"{out_name}_out"
|
|
173
|
+
self.builder.add_container(
|
|
174
|
+
new_out_name, sdfg_type, is_argument=True
|
|
175
|
+
)
|
|
176
|
+
self.metadata.add_container(
|
|
177
|
+
new_out_name,
|
|
178
|
+
ContainerInfo(
|
|
179
|
+
new_out_name, sdfg_type, ContainerMemory.OUT_ARGUMENT
|
|
180
|
+
),
|
|
181
|
+
)
|
|
182
|
+
out_shape: list[str] = self.metadata.tensor(out_name).shape()
|
|
183
|
+
if len(out_shape) == 0:
|
|
184
|
+
out_shape: list[str] = ["1"]
|
|
185
|
+
new_out_tensor: Tensor = Tensor(
|
|
186
|
+
self.metadata.tensor(out_name).element_type(),
|
|
187
|
+
out_shape,
|
|
188
|
+
)
|
|
189
|
+
self.metadata.add_tensor(
|
|
190
|
+
new_out_name,
|
|
191
|
+
TensorInfo(new_out_name, new_out_tensor, new_out_name),
|
|
192
|
+
)
|
|
193
|
+
self.metadata.add_dependency(new_out_name, out_name)
|
|
194
|
+
self.result_copy[out_name] = new_out_name
|
|
195
|
+
else:
|
|
196
|
+
self.builder.add_container(
|
|
197
|
+
out_container, sdfg_type, is_argument=True
|
|
198
|
+
)
|
|
199
|
+
self.metadata.add_container(
|
|
200
|
+
out_container,
|
|
201
|
+
ContainerInfo(
|
|
202
|
+
out_container, sdfg_type, ContainerMemory.OUT_ARGUMENT
|
|
203
|
+
),
|
|
204
|
+
)
|
|
205
|
+
self.metadata.tensor(out_name).set_container(out_container)
|
|
206
|
+
|
|
207
|
+
for node in nodes:
|
|
208
|
+
if node.op == "placeholder":
|
|
209
|
+
self.metadata.live(self.metadata.tensor(node.name).container())
|
|
210
|
+
elif node.op == "call_function":
|
|
211
|
+
pass
|
|
212
|
+
dispatch_to_module(
|
|
213
|
+
node,
|
|
214
|
+
self.builder,
|
|
215
|
+
self.metadata,
|
|
216
|
+
)
|
|
217
|
+
elif node.op == "output":
|
|
218
|
+
self.parse_output(node)
|
|
219
|
+
else:
|
|
220
|
+
raise GraphParserError(self, node, "Unknown op kind: " + node.op)
|
|
221
|
+
|
|
222
|
+
for name in self.metadata.containers_memory_managed():
|
|
223
|
+
if isinstance(self.metadata.container(name).sdfg_type(), Pointer):
|
|
224
|
+
self.builder.add_free_block(name)
|
|
225
|
+
self.metadata.dead(name)
|
|
226
|
+
|
|
227
|
+
def parse_placeholder(self, node: torch.fx.Node) -> None:
|
|
228
|
+
"""
|
|
229
|
+
Parses a PyTorch placeholder operation by creating a tensor information, a container
|
|
230
|
+
information and, an actual SDFG container for it. Notice, that all arguments of an SDFG must
|
|
231
|
+
have C-strides. This is also ensured here.
|
|
232
|
+
"""
|
|
233
|
+
if not "desc" in node.meta:
|
|
234
|
+
raise GraphParserError(self, node, "Missing desc metadata in placeholder")
|
|
235
|
+
desc: Any = node.meta["desc"]
|
|
236
|
+
if isinstance(desc, torch._functorch._aot_autograd.descriptors.PlainAOTInput):
|
|
237
|
+
# Normal argument
|
|
238
|
+
if desc.idx >= len(self.example_input):
|
|
239
|
+
raise GraphParserError(
|
|
240
|
+
self,
|
|
241
|
+
node,
|
|
242
|
+
f"No example input for placeholder {desc.idx}",
|
|
243
|
+
)
|
|
244
|
+
sdfg_type: Type = self.determine_sdfg_type(
|
|
245
|
+
node, self.example_input[desc.idx]
|
|
246
|
+
)
|
|
247
|
+
sdfg_tensor: Tensor | None = self.determine_sdfg_tensor_type(
|
|
248
|
+
node, self.example_input[desc.idx]
|
|
249
|
+
)
|
|
250
|
+
self._arguments.append(self.example_input[desc.idx])
|
|
251
|
+
|
|
252
|
+
self.builder.add_container(node.name, sdfg_type, is_argument=True)
|
|
253
|
+
self.metadata.add_container(
|
|
254
|
+
node.name,
|
|
255
|
+
ContainerInfo(node.name, sdfg_type, ContainerMemory.IN_ARGUMENT),
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# The call always provides a tensor with C-strides. If the tensor has non C-strides we
|
|
259
|
+
# enforce them here.
|
|
260
|
+
if sdfg_tensor is None:
|
|
261
|
+
contiguous_tensor: Tensor | None = None
|
|
262
|
+
else:
|
|
263
|
+
contiguous_tensor: Tensor | None = Tensor(
|
|
264
|
+
sdfg_tensor.element_type, sdfg_tensor.shape
|
|
265
|
+
)
|
|
266
|
+
self.metadata.add_tensor(
|
|
267
|
+
node.name, TensorInfo(node.name, contiguous_tensor, node.name)
|
|
268
|
+
)
|
|
269
|
+
elif isinstance(
|
|
270
|
+
desc, torch._functorch._aot_autograd.descriptors.BufferAOTInput
|
|
271
|
+
):
|
|
272
|
+
# Constant argument
|
|
273
|
+
sdfg_type: Type = self.get_node_sdfg_type(node)
|
|
274
|
+
sdfg_tensor: Tensor | None = self.get_node_sdfg_tensor(node)
|
|
275
|
+
if sdfg_tensor is None:
|
|
276
|
+
raise GraphParserError(self, node, "Could not get SDFG tensor type")
|
|
277
|
+
|
|
278
|
+
self.builder.add_container(node.name, sdfg_type)
|
|
279
|
+
self.metadata.add_container(
|
|
280
|
+
node.name,
|
|
281
|
+
ContainerInfo(node.name, sdfg_type, ContainerMemory.UNMANAGED),
|
|
282
|
+
)
|
|
283
|
+
self.metadata.add_tensor(
|
|
284
|
+
node.name, TensorInfo(node.name, sdfg_tensor, node.name)
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
if desc.target not in self.ep.constants:
|
|
288
|
+
raise GraphParserError(
|
|
289
|
+
self,
|
|
290
|
+
node,
|
|
291
|
+
"Encountered constant argument that is not present in the constants dictionary: "
|
|
292
|
+
+ desc.target,
|
|
293
|
+
)
|
|
294
|
+
from torch.fx._symbolic_trace import _ConstantAttributeType
|
|
295
|
+
|
|
296
|
+
constant: _ConstantAttributeType = self.ep.constants[desc.target]
|
|
297
|
+
if not isinstance(constant, torch.Tensor):
|
|
298
|
+
raise GraphParserError(
|
|
299
|
+
self,
|
|
300
|
+
node,
|
|
301
|
+
"Expected constant to be torch.Tensor type but got: "
|
|
302
|
+
+ str(type(constant)),
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
debug_info: DebugInfo = self.get_debug_info(node)
|
|
306
|
+
if constant.shape in (torch.Size([]), torch.Size([1])):
|
|
307
|
+
# A single-value constant
|
|
308
|
+
block: Block = self.builder.add_block(debug_info)
|
|
309
|
+
const_access: ConstantNode = self.builder.add_constant(
|
|
310
|
+
block, str(constant.item()), sdfg_tensor.element_type, debug_info
|
|
311
|
+
)
|
|
312
|
+
output_access: AccessNode = self.builder.add_access(
|
|
313
|
+
block, node.name, debug_info
|
|
314
|
+
)
|
|
315
|
+
tasklet: Tasklet = self.builder.add_tasklet(
|
|
316
|
+
block, TaskletCode.assign, ["_in"], ["_out"], debug_info
|
|
317
|
+
)
|
|
318
|
+
self.builder.add_memlet(
|
|
319
|
+
block, const_access, "void", tasklet, "_in", debug_info=debug_info
|
|
320
|
+
)
|
|
321
|
+
self.builder.add_memlet(
|
|
322
|
+
block,
|
|
323
|
+
tasklet,
|
|
324
|
+
"_out",
|
|
325
|
+
output_access,
|
|
326
|
+
"void",
|
|
327
|
+
subset="" if isinstance(sdfg_type, Scalar) else "0",
|
|
328
|
+
debug_info=debug_info,
|
|
329
|
+
)
|
|
330
|
+
else:
|
|
331
|
+
# Multi-value constant
|
|
332
|
+
constant_arr: np.ndarray = constant.detach().cpu().contiguous().numpy()
|
|
333
|
+
constant_raw: Buffer = constant_arr.reshape(-1).view(np.uint8) # type: ignore
|
|
334
|
+
|
|
335
|
+
self.builder.add_load_const_op(
|
|
336
|
+
node.name, sdfg_type, constant_raw, debug_info
|
|
337
|
+
)
|
|
338
|
+
else:
|
|
339
|
+
raise GraphParserError(
|
|
340
|
+
self, node, "Unsupported desc metadata type: " + str(desc)
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
def parse_output(self, node: torch.fx.Node) -> None:
|
|
344
|
+
"""
|
|
345
|
+
Parses a PyTorch output operation by setting the SDFG metadata about output arguments and
|
|
346
|
+
their shape information.
|
|
347
|
+
"""
|
|
348
|
+
outputs: list[TensorName] = self.get_tensors_from_arg(node.args)
|
|
349
|
+
non_user_outputs: list[int] = []
|
|
350
|
+
for i in range(len(outputs)):
|
|
351
|
+
output: TensorName = outputs[i]
|
|
352
|
+
if not output in self.ep.graph_signature.user_outputs:
|
|
353
|
+
non_user_outputs.append(i)
|
|
354
|
+
continue
|
|
355
|
+
|
|
356
|
+
if output in self.result_copy:
|
|
357
|
+
new_output: TensorName = self.result_copy[output]
|
|
358
|
+
output_info: TensorInfo = self.metadata.tensor(output)
|
|
359
|
+
new_output_info: TensorInfo = self.metadata.tensor(new_output)
|
|
360
|
+
self.copy_output_tensor(
|
|
361
|
+
node, self.builder, self.metadata, output_info, new_output_info
|
|
362
|
+
)
|
|
363
|
+
outputs[i] = new_output
|
|
364
|
+
|
|
365
|
+
for non_user_output in non_user_outputs:
|
|
366
|
+
del outputs[non_user_output]
|
|
367
|
+
|
|
368
|
+
output_containers: list[str] = [
|
|
369
|
+
self.metadata.tensor(output).container() for output in outputs
|
|
370
|
+
]
|
|
371
|
+
self.builder.add_metadata("output_args", ",".join(output_containers))
|
|
372
|
+
for output, output_container in zip(outputs, output_containers):
|
|
373
|
+
self.builder.add_metadata(
|
|
374
|
+
f"{output_container}_shape",
|
|
375
|
+
self.metadata.tensor(output).shape_str(),
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
# Preserve the original PyTorch output structure (nested tuples, dicts,
|
|
379
|
+
# dataclasses, single tensor, None leaves, ...) as a serialized pytree
|
|
380
|
+
# spec. At runtime the flat outputs produced by the compiled artifact are
|
|
381
|
+
# reassembled with this spec so the returned structure matches eager
|
|
382
|
+
# PyTorch exactly.
|
|
383
|
+
out_spec: torch.utils._pytree.TreeSpec | None = self.ep.call_spec.out_spec
|
|
384
|
+
if out_spec is not None:
|
|
385
|
+
self.builder.add_metadata(
|
|
386
|
+
"output_pytree_spec", torch.utils._pytree.treespec_dumps(out_spec)
|
|
387
|
+
)
|
|
388
|
+
self.builder.add_metadata("output_num_leaves", str(out_spec.num_leaves))
|
|
389
|
+
|
|
390
|
+
def to_sdfg(self) -> StructuredSDFG:
|
|
391
|
+
"""
|
|
392
|
+
Detaches the structured SDFG from the internal structured SDFG builder and returns it.
|
|
393
|
+
"""
|
|
394
|
+
return self.builder.move()
|
|
395
|
+
|
|
396
|
+
def get_arguments(self) -> tuple[Any, ...]:
|
|
397
|
+
"""Return all the arguments"""
|
|
398
|
+
return tuple(self._arguments)
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""
|
|
2
|
+
GraphParser modules for parsing BLAS and LAPACK operations.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import torch.fx
|
|
6
|
+
from torch.fx.node import Argument
|
|
7
|
+
|
|
8
|
+
from docc.sdfg import StructuredSDFGBuilder, Tensor, DebugInfo
|
|
9
|
+
|
|
10
|
+
from docc.pytorch.graph_parser.utils import (
|
|
11
|
+
TensorInfo,
|
|
12
|
+
TensorConstant,
|
|
13
|
+
TensorMetadata,
|
|
14
|
+
GraphParserError,
|
|
15
|
+
GraphParserModule,
|
|
16
|
+
register_module,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class MMParser(GraphParserModule):
|
|
21
|
+
"""Formula is: result = self @ mat2"""
|
|
22
|
+
|
|
23
|
+
def parse(
|
|
24
|
+
self,
|
|
25
|
+
node: torch.fx.Node,
|
|
26
|
+
builder: StructuredSDFGBuilder,
|
|
27
|
+
metadata: TensorMetadata,
|
|
28
|
+
) -> None:
|
|
29
|
+
if len(node.args) != 2:
|
|
30
|
+
raise GraphParserError(
|
|
31
|
+
self,
|
|
32
|
+
node,
|
|
33
|
+
"Expected exactly 2 arguments but got " + str(len(node.args)),
|
|
34
|
+
)
|
|
35
|
+
if len(node.kwargs) != 0:
|
|
36
|
+
raise GraphParserError(
|
|
37
|
+
self, node, "Unsupported kwargs: " + str(node.kwargs)
|
|
38
|
+
)
|
|
39
|
+
self_info: TensorInfo = self.get_arg_tensor_info(node, metadata, 0)
|
|
40
|
+
mat2_info: TensorInfo = self.get_arg_tensor_info(node, metadata, 1)
|
|
41
|
+
result_info: TensorInfo = self.get_result_tensor_info(node, builder, metadata)
|
|
42
|
+
debug_info: DebugInfo = self.get_debug_info(node)
|
|
43
|
+
builder.add_matmul_op(
|
|
44
|
+
self_info.container(),
|
|
45
|
+
self_info.sdfg_tensor_type(),
|
|
46
|
+
mat2_info.container(),
|
|
47
|
+
mat2_info.sdfg_tensor_type(),
|
|
48
|
+
result_info.container(),
|
|
49
|
+
result_info.sdfg_tensor_type(),
|
|
50
|
+
debug_info,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
register_module("aten.mm.default", MMParser())
|
|
55
|
+
register_module("aten.bmm.default", MMParser())
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class AddMMParser(GraphParserModule):
|
|
59
|
+
"""Formula is: result = beta * self + alpha * (mat1 @ mat2)"""
|
|
60
|
+
|
|
61
|
+
def parse(
|
|
62
|
+
self,
|
|
63
|
+
node: torch.fx.Node,
|
|
64
|
+
builder: StructuredSDFGBuilder,
|
|
65
|
+
metadata: TensorMetadata,
|
|
66
|
+
) -> None:
|
|
67
|
+
if len(node.args) != 3:
|
|
68
|
+
raise GraphParserError(
|
|
69
|
+
self,
|
|
70
|
+
node,
|
|
71
|
+
"Expected exactly 3 arguments but got " + str(len(node.args)),
|
|
72
|
+
)
|
|
73
|
+
alpha_arg: Argument = None
|
|
74
|
+
beta_arg: Argument = None
|
|
75
|
+
for key in node.kwargs:
|
|
76
|
+
if key == "alpha":
|
|
77
|
+
alpha_arg: Argument = node.kwargs[key]
|
|
78
|
+
elif key == "beta":
|
|
79
|
+
beta_arg: Argument = node.kwargs[key]
|
|
80
|
+
else:
|
|
81
|
+
raise GraphParserError(
|
|
82
|
+
self, node, "Unsupported kwargs: " + str(node.kwargs)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
self_info: TensorInfo = self.get_arg_tensor_info(node, metadata, 0)
|
|
86
|
+
mat1_info: TensorInfo = self.get_arg_tensor_info(node, metadata, 1)
|
|
87
|
+
mat2_info: TensorInfo = self.get_arg_tensor_info(node, metadata, 2)
|
|
88
|
+
result_info: TensorInfo = self.get_result_tensor_info(node, builder, metadata)
|
|
89
|
+
debug_info: DebugInfo = self.get_debug_info(node)
|
|
90
|
+
|
|
91
|
+
# matmul = mat1 @ mat2
|
|
92
|
+
matmul_info: TensorInfo = self.create_intermediate_tensor_info(
|
|
93
|
+
node,
|
|
94
|
+
builder,
|
|
95
|
+
metadata,
|
|
96
|
+
result_info.sdfg_type(),
|
|
97
|
+
result_info.sdfg_tensor_type(),
|
|
98
|
+
[mat1_info, mat2_info],
|
|
99
|
+
)
|
|
100
|
+
builder.add_matmul_op(
|
|
101
|
+
mat1_info.container(),
|
|
102
|
+
mat1_info.sdfg_tensor_type(),
|
|
103
|
+
mat2_info.container(),
|
|
104
|
+
mat2_info.sdfg_tensor_type(),
|
|
105
|
+
matmul_info.container(),
|
|
106
|
+
matmul_info.sdfg_tensor_type(),
|
|
107
|
+
debug_info,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# mul1 = alpha * matmul
|
|
111
|
+
if alpha_arg is None:
|
|
112
|
+
mul1_info: TensorInfo = matmul_info
|
|
113
|
+
else:
|
|
114
|
+
alpha_info_or_const: TensorInfo | TensorConstant = (
|
|
115
|
+
self.convert_arg_to_tensor_info_or_constant(
|
|
116
|
+
node,
|
|
117
|
+
metadata,
|
|
118
|
+
alpha_arg,
|
|
119
|
+
align_constant_type=matmul_info.element_type(),
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
mul1_info: TensorInfo = self.create_intermediate_tensor_info(
|
|
123
|
+
node,
|
|
124
|
+
builder,
|
|
125
|
+
metadata,
|
|
126
|
+
matmul_info.sdfg_type(),
|
|
127
|
+
matmul_info.sdfg_tensor_type(),
|
|
128
|
+
[alpha_info_or_const, matmul_info],
|
|
129
|
+
)
|
|
130
|
+
builder.add_elementwise_op(
|
|
131
|
+
"mul",
|
|
132
|
+
alpha_info_or_const.container(),
|
|
133
|
+
alpha_info_or_const.sdfg_tensor_type(),
|
|
134
|
+
matmul_info.container(),
|
|
135
|
+
matmul_info.sdfg_tensor_type(),
|
|
136
|
+
mul1_info.container(),
|
|
137
|
+
mul1_info.sdfg_tensor_type(),
|
|
138
|
+
debug_info,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# mul2 = beta * self
|
|
142
|
+
if beta_arg is None:
|
|
143
|
+
mul2_info: TensorInfo = self_info
|
|
144
|
+
else:
|
|
145
|
+
beta_info_or_const: TensorInfo | TensorConstant = (
|
|
146
|
+
self.convert_arg_to_tensor_info_or_constant(
|
|
147
|
+
node,
|
|
148
|
+
metadata,
|
|
149
|
+
beta_arg,
|
|
150
|
+
align_constant_type=self_info.element_type(),
|
|
151
|
+
)
|
|
152
|
+
)
|
|
153
|
+
mul2_info: TensorInfo = self.create_intermediate_tensor_info(
|
|
154
|
+
node,
|
|
155
|
+
builder,
|
|
156
|
+
metadata,
|
|
157
|
+
self_info.sdfg_type(),
|
|
158
|
+
self_info.sdfg_tensor_type(),
|
|
159
|
+
[beta_info_or_const, self_info],
|
|
160
|
+
)
|
|
161
|
+
builder.add_elementwise_op(
|
|
162
|
+
"mul",
|
|
163
|
+
beta_info_or_const.container(),
|
|
164
|
+
beta_info_or_const.sdfg_tensor_type(),
|
|
165
|
+
self_info.container(),
|
|
166
|
+
self_info.sdfg_tensor_type(),
|
|
167
|
+
mul2_info.container(),
|
|
168
|
+
mul2_info.sdfg_tensor_type(),
|
|
169
|
+
debug_info,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Broadcast mul2 shape to mul1 shape if needed
|
|
173
|
+
mul1_tensor: Tensor = mul1_info.sdfg_tensor_type()
|
|
174
|
+
mul2_tensor: Tensor = mul2_info.sdfg_tensor_type()
|
|
175
|
+
if len(mul1_info.shape()) != len(mul2_info.shape()):
|
|
176
|
+
mul1_tensor, mul2_tensor = self.align_elementwise_tensors(
|
|
177
|
+
node, mul1_tensor, mul2_tensor
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# result = mul2 + mul1
|
|
181
|
+
builder.add_elementwise_op(
|
|
182
|
+
"add",
|
|
183
|
+
mul2_info.container(),
|
|
184
|
+
mul2_tensor,
|
|
185
|
+
mul1_info.container(),
|
|
186
|
+
mul1_tensor,
|
|
187
|
+
result_info.container(),
|
|
188
|
+
result_info.sdfg_tensor_type(),
|
|
189
|
+
debug_info,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
register_module("aten.addmm.default", AddMMParser())
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""
|
|
2
|
+
GraphParser modules for parsing builtin Python functions.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import torch.fx
|
|
6
|
+
from torch.fx.node import Argument
|
|
7
|
+
|
|
8
|
+
from docc.sdfg import StructuredSDFGBuilder
|
|
9
|
+
|
|
10
|
+
from docc.pytorch.graph_parser.utils import (
|
|
11
|
+
TensorName,
|
|
12
|
+
TensorMetadata,
|
|
13
|
+
GraphParserError,
|
|
14
|
+
GraphParserModule,
|
|
15
|
+
register_module,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class GetitemParser(GraphParserModule):
|
|
20
|
+
def parse(
|
|
21
|
+
self,
|
|
22
|
+
node: torch.fx.Node,
|
|
23
|
+
builder: StructuredSDFGBuilder,
|
|
24
|
+
metadata: TensorMetadata,
|
|
25
|
+
) -> None:
|
|
26
|
+
if len(node.args) != 2:
|
|
27
|
+
raise GraphParserError(
|
|
28
|
+
self,
|
|
29
|
+
node,
|
|
30
|
+
"Expected exactly 2 arguments but got " + str(len(node.args)),
|
|
31
|
+
)
|
|
32
|
+
if len(node.kwargs) != 0:
|
|
33
|
+
raise GraphParserError(
|
|
34
|
+
self, node, "Unsupported kwargs: " + str(node.kwargs)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
first: Argument = node.args[0]
|
|
38
|
+
second: Argument = node.args[1]
|
|
39
|
+
if not isinstance(first, torch.fx.Node):
|
|
40
|
+
raise GraphParserError(
|
|
41
|
+
self,
|
|
42
|
+
node,
|
|
43
|
+
"Expected first argument to be forch.fx.Node type but got: "
|
|
44
|
+
+ str(type(first)),
|
|
45
|
+
)
|
|
46
|
+
if not isinstance(second, int):
|
|
47
|
+
raise GraphParserError(
|
|
48
|
+
self,
|
|
49
|
+
node,
|
|
50
|
+
"Expected second argument to be int type but got: " + str(type(second)),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
ref_name: TensorName = f"{first.name}_{second}"
|
|
54
|
+
if not metadata.has_tensor(ref_name):
|
|
55
|
+
raise GraphParserError(
|
|
56
|
+
self, node, f"Could not find tensor information for '{ref_name}'"
|
|
57
|
+
)
|
|
58
|
+
self.create_result_view(node, builder, metadata, metadata.tensor(ref_name))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
register_module("_operator.getitem", GetitemParser())
|