nnschemaparser 0.1.0__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.
Files changed (38) hide show
  1. nnschemaparser/__init__.py +27 -0
  2. nnschemaparser/core/__init__.py +16 -0
  3. nnschemaparser/core/container_keys.py +69 -0
  4. nnschemaparser/core/instructions.py +399 -0
  5. nnschemaparser/core/model_schema.py +94 -0
  6. nnschemaparser/core/tensor_schema.py +55 -0
  7. nnschemaparser/dump.py +487 -0
  8. nnschemaparser/forward.py +29 -0
  9. nnschemaparser/interpreter/__init__.py +19 -0
  10. nnschemaparser/interpreter/control_flow.py +570 -0
  11. nnschemaparser/interpreter/execution.py +767 -0
  12. nnschemaparser/interpreter/interpreter.py +338 -0
  13. nnschemaparser/interpreter/scope.py +121 -0
  14. nnschemaparser/interpreter/tracer.py +346 -0
  15. nnschemaparser/parser/__init__.py +14 -0
  16. nnschemaparser/parser/children.py +171 -0
  17. nnschemaparser/parser/lowering/__init__.py +16 -0
  18. nnschemaparser/parser/lowering/base.py +354 -0
  19. nnschemaparser/parser/lowering/block_calls.py +350 -0
  20. nnschemaparser/parser/lowering/body.py +37 -0
  21. nnschemaparser/parser/lowering/calls.py +689 -0
  22. nnschemaparser/parser/lowering/control_flow.py +519 -0
  23. nnschemaparser/parser/lowering/effects.py +164 -0
  24. nnschemaparser/parser/lowering/errors.py +24 -0
  25. nnschemaparser/parser/lowering/expressions.py +805 -0
  26. nnschemaparser/parser/lowering/naming.py +179 -0
  27. nnschemaparser/parser/lowering/operators.py +333 -0
  28. nnschemaparser/parser/lowering/runtime_ops.py +260 -0
  29. nnschemaparser/parser/lowering/statements.py +600 -0
  30. nnschemaparser/parser/lowering/values.py +97 -0
  31. nnschemaparser/parser/parser.py +599 -0
  32. nnschemaparser/parser/resolution.py +280 -0
  33. nnschemaparser/py.typed +0 -0
  34. nnschemaparser-0.1.0.dist-info/METADATA +200 -0
  35. nnschemaparser-0.1.0.dist-info/RECORD +38 -0
  36. nnschemaparser-0.1.0.dist-info/WHEEL +5 -0
  37. nnschemaparser-0.1.0.dist-info/licenses/LICENSE +21 -0
  38. nnschemaparser-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,27 @@
1
+ """
2
+ NNSchemaParser.
3
+
4
+ Converts a torch nn.Module into an inspectable ModelBlockSchema graph that
5
+ keeps the original variable names of the forward source and every sub-module,
6
+ and executes it with a schema interpreter re-dispatching to the real torch
7
+ callables.
8
+ """
9
+
10
+ # Import Modules
11
+ from nnschemaparser.core import ModelBlockSchema, TensorRepr, TensorReprWithValue
12
+ from nnschemaparser.dump import dump_schema
13
+ from nnschemaparser.interpreter import SchemaInterpreter, TraceListener, TraceRecorder
14
+ from nnschemaparser.parser import NNParser
15
+
16
+ __all__ = [
17
+ "ModelBlockSchema",
18
+ "TraceListener",
19
+ "TraceRecorder",
20
+ "dump_schema",
21
+ "NNParser",
22
+ "SchemaInterpreter",
23
+ "TensorRepr",
24
+ "TensorReprWithValue"
25
+ ]
26
+
27
+ __version__: str = "0.1.0"
@@ -0,0 +1,16 @@
1
+ """
2
+ Core data model of NNSchemaParser.
3
+
4
+ The schema types describing a parsed model: the block schema tree, the tensor
5
+ representations and the instruction types of the block bodies.
6
+ """
7
+
8
+ # Import Modules
9
+ from nnschemaparser.core.model_schema import ModelBlockSchema
10
+ from nnschemaparser.core.tensor_schema import TensorRepr, TensorReprWithValue
11
+
12
+ __all__ = [
13
+ "ModelBlockSchema",
14
+ "TensorRepr",
15
+ "TensorReprWithValue"
16
+ ]
@@ -0,0 +1,69 @@
1
+ """
2
+ Container entry key utilities shared by the lowering and the interpreter.
3
+
4
+ The entries of a sub-block container are keyed by "container.index" in the
5
+ sub-block dictionaries, and must be iterated in their declaration order.
6
+ """
7
+
8
+ # Import Modules
9
+
10
+
11
+ def container_entry_sort_key(entry_key: str) -> int:
12
+ """
13
+ Get the numeric sort key of a container entry key.
14
+
15
+ Args:
16
+ entry_key (str): The entry key (e.g. "encoders.2").
17
+
18
+ Returns:
19
+ int: The numeric suffix of the key, or -1 when not numeric.
20
+ """
21
+
22
+ # Split the key into its container prefix and its suffix
23
+ parts: list[str] = entry_key.rsplit(".", 1)
24
+
25
+ # A numeric suffix is sorted by its value
26
+ if len(parts) == 2 and parts[1].isdigit():
27
+ return int(parts[1])
28
+
29
+ # A non numeric suffix is sorted last
30
+ return -1
31
+
32
+
33
+ def container_entry_keys(
34
+ sub_block_names: set[str] | dict[str, str],
35
+ container_name: str
36
+ ) -> list[str]:
37
+ """
38
+ Get the ordered entry keys of a container in the parsed sub-blocks.
39
+
40
+ Args:
41
+ sub_block_names (set[str] | dict[str, str]): The names of the parsed
42
+ sub-blocks, either a set of names or the sub-block dictionary.
43
+ container_name (str): The name of the container.
44
+
45
+ Returns:
46
+ list[str]: The entry keys sorted by their numeric suffix.
47
+ """
48
+
49
+ # The special self path holds the direct numeric entries of the module
50
+ if container_name == "$self":
51
+ return sorted(
52
+ [
53
+ sub_block_name for sub_block_name in sub_block_names
54
+ if sub_block_name.isdigit()
55
+ ],
56
+ key=container_entry_sort_key
57
+ )
58
+
59
+ # Collect the sub-block keys of the container entries
60
+ keys: list[str] = [
61
+ sub_block_name for sub_block_name in sub_block_names
62
+ if sub_block_name.startswith(f"{container_name}.")
63
+ ]
64
+
65
+ # Sort the entry keys by their numeric suffix
66
+ keys.sort(key=container_entry_sort_key)
67
+
68
+ # Return the ordered entry keys of the container
69
+ return keys
@@ -0,0 +1,399 @@
1
+ """
2
+ Instruction types of the NNSchemaParser schema.
3
+
4
+ Each instruction represents one step of a block body and keeps the original
5
+ variable names of the parsed forward source.
6
+
7
+ Each instruction represents one step of a model block body, and keeps the
8
+ original variable names of the parsed Python code so the WebUI can display a
9
+ graph that maps back to the source.
10
+ """
11
+
12
+ # Import Modules
13
+ from typing import Any, Callable
14
+
15
+ from torch import Tensor
16
+
17
+ from nnschemaparser.core.tensor_schema import TensorRepr, TensorReprWithValue
18
+
19
+
20
+ class VarRef:
21
+ """
22
+ Reference to a variable of the current block scope.
23
+ """
24
+
25
+ def __init__(self, name: str) -> None:
26
+ """
27
+ Init function.
28
+
29
+ Args:
30
+ name (str): The name of the referenced variable.
31
+ """
32
+
33
+ # Store here the name of the referenced variable
34
+ self.name: str = name
35
+
36
+
37
+ class BlockRef:
38
+ """
39
+ Reference to a sub-block of the current block.
40
+
41
+ The path is the attribute name of the sub-block inside its parent module
42
+ (e.g. "attention"). For containers (nn.ModuleList, nn.ModuleDict), the
43
+ index_var is the name of the scope variable holding the index or the key.
44
+ """
45
+
46
+ def __init__(self, path: str, index_var: str | None = None) -> None:
47
+ """
48
+ Init function.
49
+
50
+ Args:
51
+ path (str): The attribute path of the referenced sub-block.
52
+ index_var (str | None): The name of the scope variable holding the
53
+ container index, if the sub-block belongs to a container.
54
+ """
55
+
56
+ # Store here the attribute path of the referenced sub-block
57
+ self.path: str = path
58
+
59
+ # Store here the index variable name for container sub-blocks
60
+ self.index_var: str | None = index_var
61
+
62
+
63
+ class Instruction:
64
+ """
65
+ Base class of all the instructions of a block body.
66
+ """
67
+
68
+ def __init__(self, out_var: str) -> None:
69
+ """
70
+ Init function.
71
+
72
+ Args:
73
+ out_var (str): The name of the variable the result is bound to.
74
+ """
75
+
76
+ # Store here the output variable name of the instruction
77
+ self.out_var: str = out_var
78
+
79
+ # This field is filled later by the shape resolution pass
80
+ self.resolved: TensorRepr | TensorReprWithValue | None = None
81
+
82
+ def resolve(self, value: Any, capture_values: bool = False) -> None:
83
+ """
84
+ Fill the resolved representation of the instruction output.
85
+
86
+ Args:
87
+ value (Any): The value produced by the instruction at execution.
88
+ capture_values (bool): If True, the tensor values are kept inside
89
+ the resolved representation.
90
+ """
91
+
92
+ # If the value is a tensor, we store a tensor representation
93
+ if isinstance(value, Tensor):
94
+
95
+ # If values must be captured, keep a representation with the value
96
+ if capture_values:
97
+ self.resolved = TensorReprWithValue(
98
+ shape=tuple(int(dim) for dim in value.shape),
99
+ dtype=str(value.dtype),
100
+ value=value.detach()
101
+ )
102
+
103
+ # Else we only keep the shape and the dtype of the tensor
104
+ else:
105
+ self.resolved = TensorRepr(
106
+ shape=tuple(int(dim) for dim in value.shape),
107
+ dtype=str(value.dtype)
108
+ )
109
+
110
+ # Else the value is a python literal, no shape is available
111
+ else:
112
+ self.resolved = TensorRepr(shape=(), dtype=type(value).__name__)
113
+
114
+
115
+ class OpInstruction(Instruction):
116
+ """
117
+ Call to a torch operation or function (e.g. torch.nn.functional.softmax).
118
+ """
119
+
120
+ def __init__(
121
+ self,
122
+ target: Callable[..., Any] | VarRef,
123
+ qualified_name: str,
124
+ args: list[VarRef | Any],
125
+ kwargs: dict[str, VarRef | Any],
126
+ out_var: str,
127
+ kwargs_var: str | None = None,
128
+ args_var: str | None = None
129
+ ) -> None:
130
+ """
131
+ Init function.
132
+
133
+ Args:
134
+ target (Callable[..., Any] | VarRef): The callable applied by the
135
+ instruction, or a variable reference to a callable resolved at
136
+ execution time (e.g. a method obtained with getattr).
137
+ qualified_name (str): The display name of the target
138
+ (e.g. "torch.nn.functional.softmax").
139
+ args (list[VarRef | Any]): The positional arguments, variable
140
+ references or literals.
141
+ kwargs (dict[str, VarRef | Any]): The keyword arguments, variable
142
+ references or literals.
143
+ out_var (str): The name of the variable the result is bound to.
144
+ kwargs_var (str | None): The name of a scope variable holding a dict
145
+ unpacked as extra keyword arguments (the **kwargs of the call).
146
+ args_var (str | None): The name of a scope variable holding a tuple
147
+ unpacked as extra positional arguments (the *args of the call).
148
+ """
149
+
150
+ # Init parent
151
+ super().__init__(out_var=out_var)
152
+
153
+ # Store here the callable applied by the instruction
154
+ self.target: Callable[..., Any] | VarRef = target
155
+
156
+ # Store here the display name of the target
157
+ self.qualified_name: str = qualified_name
158
+
159
+ # Store here the positional arguments of the call
160
+ self.args: list[VarRef | Any] = args
161
+
162
+ # Store here the keyword arguments of the call
163
+ self.kwargs: dict[str, VarRef | Any] = kwargs
164
+
165
+ # Store here the name of the unpacked keyword arguments variable
166
+ self.kwargs_var: str | None = kwargs_var
167
+
168
+ # Store here the name of the unpacked positional arguments variable
169
+ self.args_var: str | None = args_var
170
+
171
+
172
+ class BlockInstruction(Instruction):
173
+ """
174
+ Call to a sub-block of the current block.
175
+ """
176
+
177
+ def __init__(
178
+ self,
179
+ block_ref: BlockRef,
180
+ args: list[VarRef | Any],
181
+ out_var: str,
182
+ kwargs: dict[str, VarRef | Any] | None = None,
183
+ splat_args: bool = False,
184
+ kwargs_var: str | None = None,
185
+ args_var: str | None = None
186
+ ) -> None:
187
+ """
188
+ Init function.
189
+
190
+ Args:
191
+ block_ref (BlockRef): The reference to the called sub-block.
192
+ args (list[VarRef | Any]): The positional arguments of the call.
193
+ When splat_args is True, this list holds a single variable
194
+ reference whose tuple value is unpacked as the arguments.
195
+ out_var (str): The name of the variable the result is bound to.
196
+ kwargs (dict[str, VarRef | Any] | None): The keyword arguments of
197
+ the call, bound to the sub-block inputs by name.
198
+ splat_args (bool): Whether the argument variable holds a tuple to
199
+ unpack as the positional arguments of the call.
200
+ kwargs_var (str | None): The name of a scope variable holding a dict
201
+ unpacked as extra keyword arguments (the **kwargs of the call).
202
+ args_var (str | None): The name of a scope variable holding a
203
+ tuple unpacked as extra positional arguments (the *args of
204
+ the call).
205
+ """
206
+
207
+ # Init parent
208
+ super().__init__(out_var=out_var)
209
+
210
+ # Store here the reference to the called sub-block
211
+ self.block_ref: BlockRef = block_ref
212
+
213
+ # Store here the arguments of the sub-block call
214
+ self.args: list[VarRef | Any] = args
215
+
216
+ # Store here the keyword arguments of the sub-block call
217
+ self.kwargs: dict[str, VarRef | Any] = kwargs or {}
218
+
219
+ # Store here whether the argument variable is unpacked as the arguments
220
+ self.splat_args: bool = splat_args
221
+
222
+ # Store here the name of the unpacked keyword arguments variable
223
+ self.kwargs_var: str | None = kwargs_var
224
+
225
+ # Store here the name of the unpacked positional arguments variable
226
+ self.args_var: str | None = args_var
227
+
228
+
229
+ class ControlInstruction(Instruction):
230
+ """
231
+ Control flow instruction, an if statement or a for loop.
232
+
233
+ For an if statement, the condition is an OpInstruction binding a temporary
234
+ variable of the block scope. For a for loop, the iterable is either an
235
+ OpInstruction, or the sub-block container referenced by iter_container,
236
+ whose entries are bound to bind_var (as sub-block schemas).
237
+ """
238
+
239
+ def __init__(
240
+ self,
241
+ kind: str,
242
+ condition: OpInstruction | None,
243
+ body: list[Instruction],
244
+ else_body: list[Instruction],
245
+ bind_var: str | None,
246
+ iter_container: BlockRef | None = None,
247
+ index_var: str | None = None,
248
+ out_var: str = ""
249
+ ) -> None:
250
+ """
251
+ Init function.
252
+
253
+ Args:
254
+ kind (str): The kind of control flow, either "if" or "for".
255
+ condition (OpInstruction | None): The condition expression for an
256
+ if statement, the iterable expression for a for loop, or None
257
+ when iterating a container of sub-blocks.
258
+ body (list[Instruction]): The instructions of the main body.
259
+ else_body (list[Instruction]): The instructions of the else body.
260
+ bind_var (str | None): The name of the loop variable bound at each
261
+ iteration of a for loop.
262
+ iter_container (BlockRef | None): For a for loop over a container
263
+ of sub-blocks, the reference to the iterated container.
264
+ index_var (str | None): For a for loop with enumerate over a
265
+ container, the name of the variable bound to the index.
266
+ out_var (str): The name of the variable the result is bound to.
267
+ """
268
+
269
+ # Init parent
270
+ super().__init__(out_var=out_var)
271
+
272
+ # Only the if, for and while kinds are supported
273
+ if kind not in ("if", "for", "while"):
274
+ raise ValueError(f"Unsupported control flow kind: {kind}")
275
+
276
+ # Store here the kind of control flow
277
+ self.kind: str = kind
278
+
279
+ # Store here the condition or iterable expression of the control flow
280
+ self.condition: OpInstruction | None = condition
281
+
282
+ # Store here the instructions of the main body
283
+ self.body: list[Instruction] = body
284
+
285
+ # Store here the instructions of the else body
286
+ self.else_body: list[Instruction] = else_body
287
+
288
+ # Store here the name of the loop variable of a for loop
289
+ self.bind_var: str | None = bind_var
290
+
291
+ # Store here the iterated container reference of a for loop
292
+ self.iter_container: BlockRef | None = iter_container
293
+
294
+ # Store here the enumerate index variable of a for loop
295
+ self.index_var: str | None = index_var
296
+
297
+
298
+ class TryInstruction(Instruction):
299
+ """
300
+ Try instruction of a block body, with exception handlers and a finally.
301
+
302
+ The exception types of the handlers are resolved at lowering time and
303
+ stored as python classes. The name of the caught exception object is not
304
+ bound for now.
305
+ """
306
+
307
+ def __init__(
308
+ self,
309
+ body: list[Instruction],
310
+ handlers: list[tuple[tuple[type[BaseException], ...], list[Instruction]]],
311
+ handler_names: list[str | None],
312
+ final_body: list[Instruction],
313
+ out_var: str = ""
314
+ ) -> None:
315
+ """
316
+ Init function.
317
+
318
+ Args:
319
+ body (list[Instruction]): The instructions of the try body.
320
+ handlers (list[tuple[tuple[type[BaseException], ...], list[Instruction]]]):
321
+ The exception types and bodies of the handlers.
322
+ handler_names (list[str | None]): The caught exception names of
323
+ the handlers, bound in the scope during their execution.
324
+ final_body (list[Instruction]): The instructions of the finally body.
325
+ out_var (str): The name of the variable the result is bound to.
326
+ """
327
+
328
+ # Init parent, a try instruction binds no new variable
329
+ super().__init__(out_var=out_var)
330
+
331
+ # Store here the instructions of the try body
332
+ self.body: list[Instruction] = body
333
+
334
+ # Store here the exception types and bodies of the handlers
335
+ self.handlers: list[
336
+ tuple[tuple[type[BaseException], ...], list[Instruction]]
337
+ ] = handlers
338
+
339
+ # Store here the caught exception names of the handlers
340
+ self.handler_names: list[str | None] = handler_names
341
+
342
+ # Store here the instructions of the finally body
343
+ self.final_body: list[Instruction] = final_body
344
+
345
+
346
+ class ReturnInstruction(Instruction):
347
+ """
348
+ Return instruction of a block body.
349
+
350
+ A return instruction may be nested inside a control flow body, the
351
+ interpreter propagates its outputs up to the enclosing block.
352
+ """
353
+
354
+ def __init__(self, out_vars: list[str]) -> None:
355
+ """
356
+ Init function.
357
+
358
+ Args:
359
+ out_vars (list[str]): The names of the returned variables.
360
+ """
361
+
362
+ # Init parent, a return instruction binds no new variable
363
+ super().__init__(out_var="")
364
+
365
+ # Store here the names of the returned variables
366
+ self.out_vars: list[str] = out_vars
367
+
368
+
369
+ class ContinueInstruction(Instruction):
370
+ """
371
+ Continue instruction of a loop body.
372
+
373
+ The execution of the current iteration stops and the loop goes to its
374
+ next iteration, like the python continue statement.
375
+ """
376
+
377
+ def __init__(self) -> None:
378
+ """
379
+ Init function.
380
+ """
381
+
382
+ # Init parent, a continue instruction binds no new variable
383
+ super().__init__(out_var="")
384
+
385
+
386
+ class BreakInstruction(Instruction):
387
+ """
388
+ Break instruction of a loop body.
389
+
390
+ The execution of the loop stops, like the python break statement.
391
+ """
392
+
393
+ def __init__(self) -> None:
394
+ """
395
+ Init function.
396
+ """
397
+
398
+ # Init parent, a break instruction binds no new variable
399
+ super().__init__(out_var="")
@@ -0,0 +1,94 @@
1
+ """
2
+ Core schema types of NNSchemaParser.
3
+
4
+ A parsed model is a tree of ModelBlockSchema blocks, each holding its inputs,
5
+ outputs, weights, sub-blocks and the instruction list of its forward body.
6
+ """
7
+
8
+ # Import Modules
9
+ from typing import Any
10
+
11
+ from nnschemaparser.core.instructions import Instruction
12
+ from nnschemaparser.core.tensor_schema import TensorRepr, TensorReprWithValue
13
+
14
+
15
+ class ModelBlockSchema:
16
+ """
17
+ Schema of one block of a parsed model.
18
+
19
+ A block mirrors one nn.Module of the source model: its forward parameters,
20
+ its returned values, its direct parameters and buffers, its child modules
21
+ as sub-blocks, and the lowered body of its forward method.
22
+ """
23
+
24
+ def __init__(
25
+ self,
26
+ inputs: dict[str, TensorRepr],
27
+ outputs: list[TensorRepr],
28
+ weights: dict[str, TensorReprWithValue],
29
+ sub_blocks: dict[str, "ModelBlockSchema"],
30
+ body: list[Instruction],
31
+ module: Any | None = None,
32
+ methods: dict[str, "ModelBlockSchema"] | None = None,
33
+ method_name: str | None = None,
34
+ input_defaults: dict[str, Any] | None = None,
35
+ skipped: dict[str, str] | None = None
36
+ ) -> None:
37
+ """
38
+ Init function.
39
+
40
+ Args:
41
+ inputs (dict[str, TensorRepr]): The list of all named input values of the model
42
+ (often one X value)
43
+ outputs (list[TensorRepr]): The list of all named output values of the model
44
+ (often one Y value)
45
+ weights: dict[str, TensorReprWithValue]: The weights of the current block.
46
+ sub_blocks (dict[str, ModelBlockSchema]): The list of all the sub block of the model
47
+ (all the other nn.Modules in the model class attribute)
48
+ body (list[Instruction]): What is happening inside the current model block
49
+ (the graph that will be shown in the webUI)
50
+ module (Any | None): The source module of the block, used by the
51
+ interpreter to resolve dynamic attributes.
52
+ methods (dict[str, ModelBlockSchema] | None): The other methods of the
53
+ source module called by the body, parsed
54
+ as block schemas (e.g. get_image_features).
55
+ method_name (str | None): The name of the parsed method when the block
56
+ describes a method instead of the forward.
57
+ input_defaults (dict[str, Any] | None): The default values of the
58
+ optional inputs, by parameter name.
59
+ skipped (dict[str, str] | None): The children of the module that
60
+ could not be parsed, by name with
61
+ their skip reasons. They are
62
+ reached at run time through the
63
+ source module instead.
64
+ """
65
+
66
+ # Store here the model input(s) representation(s)
67
+ self.inputs: dict[str, TensorRepr] = inputs
68
+
69
+ # Store here the model output(s) representation(s)
70
+ self.outputs: list[TensorRepr] = outputs
71
+
72
+ # Store here the weights of the block
73
+ self.weights: dict[str, TensorReprWithValue] = weights
74
+
75
+ # Store here the model sub blocks
76
+ self.sub_blocks: dict[str, ModelBlockSchema] = sub_blocks
77
+
78
+ # Store here the model body list of instructions / expressions
79
+ self.body: list[Instruction] = body
80
+
81
+ # Store here the source module of the block
82
+ self.module: Any | None = module
83
+
84
+ # Store here the other methods of the module called by the body
85
+ self.methods: dict[str, ModelBlockSchema] = methods or {}
86
+
87
+ # Store here the default values of the optional inputs
88
+ self.input_defaults: dict[str, Any] = input_defaults or {}
89
+
90
+ # Store here the skipped children re-dispatched at run time
91
+ self.skipped: dict[str, str] = skipped or {}
92
+
93
+ # Store here the name of the described method, if not the forward
94
+ self.method_name: str | None = method_name
@@ -0,0 +1,55 @@
1
+ """
2
+ Tensor representation types of NNSchemaParser.
3
+
4
+ A TensorRepr describes a tensor flowing through the graph by its shape and its
5
+ dtype, and TensorReprWithValue additionally holds the value itself.
6
+ """
7
+
8
+ # Import Modules
9
+ from torch import Tensor
10
+
11
+ # The placeholder dtype of unresolved tensor representations
12
+ PLACEHOLDER_DTYPE: str = ""
13
+
14
+
15
+ class TensorRepr:
16
+ """
17
+ Representation of a tensor flowing through the graph, without its value.
18
+ """
19
+
20
+ def __init__(self, shape: tuple[int, ...], dtype: str) -> None:
21
+ """
22
+ Init function.
23
+
24
+ Args:
25
+ shape (tuple[int | str, ...]): shape of the tensor, the str type is for symbolic values
26
+ dtype (str): The type of the tensor's values
27
+ """
28
+
29
+ # Store here the shape of the tensor
30
+ self.shape: tuple[int, ...] = shape
31
+
32
+ # Store here the type of the tensor
33
+ self.dtype: str = dtype
34
+
35
+
36
+ class TensorReprWithValue(TensorRepr):
37
+ """
38
+ Representation of a tensor keeping a snapshot of its value.
39
+ """
40
+
41
+ def __init__(self, shape: tuple[int, ...], dtype: str, value: Tensor) -> None:
42
+ """
43
+ Init function.
44
+
45
+ Args:
46
+ shape (tuple[int, ...]): Shape of the tensor.
47
+ dtype (str): The type of the tensor's values.
48
+ value (Tensor): The value of the tensor.
49
+ """
50
+
51
+ # Init parent
52
+ super().__init__(shape=shape, dtype=dtype)
53
+
54
+ # Store here the value of the tensor
55
+ self.value: Tensor = value