nnschemaparser 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.
Files changed (47) hide show
  1. nnschemaparser-0.1.0/LICENSE +21 -0
  2. nnschemaparser-0.1.0/PKG-INFO +200 -0
  3. nnschemaparser-0.1.0/README.md +145 -0
  4. nnschemaparser-0.1.0/nnschemaparser/__init__.py +27 -0
  5. nnschemaparser-0.1.0/nnschemaparser/core/__init__.py +16 -0
  6. nnschemaparser-0.1.0/nnschemaparser/core/container_keys.py +69 -0
  7. nnschemaparser-0.1.0/nnschemaparser/core/instructions.py +399 -0
  8. nnschemaparser-0.1.0/nnschemaparser/core/model_schema.py +94 -0
  9. nnschemaparser-0.1.0/nnschemaparser/core/tensor_schema.py +55 -0
  10. nnschemaparser-0.1.0/nnschemaparser/dump.py +487 -0
  11. nnschemaparser-0.1.0/nnschemaparser/forward.py +29 -0
  12. nnschemaparser-0.1.0/nnschemaparser/interpreter/__init__.py +19 -0
  13. nnschemaparser-0.1.0/nnschemaparser/interpreter/control_flow.py +570 -0
  14. nnschemaparser-0.1.0/nnschemaparser/interpreter/execution.py +767 -0
  15. nnschemaparser-0.1.0/nnschemaparser/interpreter/interpreter.py +338 -0
  16. nnschemaparser-0.1.0/nnschemaparser/interpreter/scope.py +121 -0
  17. nnschemaparser-0.1.0/nnschemaparser/interpreter/tracer.py +346 -0
  18. nnschemaparser-0.1.0/nnschemaparser/parser/__init__.py +14 -0
  19. nnschemaparser-0.1.0/nnschemaparser/parser/children.py +171 -0
  20. nnschemaparser-0.1.0/nnschemaparser/parser/lowering/__init__.py +16 -0
  21. nnschemaparser-0.1.0/nnschemaparser/parser/lowering/base.py +354 -0
  22. nnschemaparser-0.1.0/nnschemaparser/parser/lowering/block_calls.py +350 -0
  23. nnschemaparser-0.1.0/nnschemaparser/parser/lowering/body.py +37 -0
  24. nnschemaparser-0.1.0/nnschemaparser/parser/lowering/calls.py +689 -0
  25. nnschemaparser-0.1.0/nnschemaparser/parser/lowering/control_flow.py +519 -0
  26. nnschemaparser-0.1.0/nnschemaparser/parser/lowering/effects.py +164 -0
  27. nnschemaparser-0.1.0/nnschemaparser/parser/lowering/errors.py +24 -0
  28. nnschemaparser-0.1.0/nnschemaparser/parser/lowering/expressions.py +805 -0
  29. nnschemaparser-0.1.0/nnschemaparser/parser/lowering/naming.py +179 -0
  30. nnschemaparser-0.1.0/nnschemaparser/parser/lowering/operators.py +333 -0
  31. nnschemaparser-0.1.0/nnschemaparser/parser/lowering/runtime_ops.py +260 -0
  32. nnschemaparser-0.1.0/nnschemaparser/parser/lowering/statements.py +600 -0
  33. nnschemaparser-0.1.0/nnschemaparser/parser/lowering/values.py +97 -0
  34. nnschemaparser-0.1.0/nnschemaparser/parser/parser.py +599 -0
  35. nnschemaparser-0.1.0/nnschemaparser/parser/resolution.py +280 -0
  36. nnschemaparser-0.1.0/nnschemaparser/py.typed +0 -0
  37. nnschemaparser-0.1.0/nnschemaparser.egg-info/PKG-INFO +200 -0
  38. nnschemaparser-0.1.0/nnschemaparser.egg-info/SOURCES.txt +45 -0
  39. nnschemaparser-0.1.0/nnschemaparser.egg-info/dependency_links.txt +1 -0
  40. nnschemaparser-0.1.0/nnschemaparser.egg-info/requires.txt +8 -0
  41. nnschemaparser-0.1.0/nnschemaparser.egg-info/top_level.txt +1 -0
  42. nnschemaparser-0.1.0/pyproject.toml +51 -0
  43. nnschemaparser-0.1.0/setup.cfg +4 -0
  44. nnschemaparser-0.1.0/tests/test_methods_and_scalars.py +152 -0
  45. nnschemaparser-0.1.0/tests/test_new_syntax.py +221 -0
  46. nnschemaparser-0.1.0/tests/test_parser.py +193 -0
  47. nnschemaparser-0.1.0/tests/test_speech_models.py +243 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Cerisara Nathan
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.
@@ -0,0 +1,200 @@
1
+ Metadata-Version: 2.4
2
+ Name: nnschemaparser
3
+ Version: 0.1.0
4
+ Summary: Parse torch nn.Module models into an inspectable ModelBlockSchema graph and execute it with a faithful schema interpreter
5
+ Author: Cerisara Nathan
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Cerisara Nathan
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
+
28
+ Project-URL: Homepage, https://github.com/nath54/NNViewer
29
+ Project-URL: Documentation, https://github.com/nath54/NNViewer/blob/main/docs/DOCUMENTATION.md
30
+ Project-URL: Changelog, https://github.com/nath54/NNViewer/blob/main/CHANGELOG.md
31
+ Project-URL: Issues, https://github.com/nath54/NNViewer/issues
32
+ Keywords: pytorch,neural-network,graph,schema,interpreter,model-inspection
33
+ Classifier: Development Status :: 4 - Beta
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: Intended Audience :: Science/Research
36
+ Classifier: License :: OSI Approved :: MIT License
37
+ Classifier: Operating System :: OS Independent
38
+ Classifier: Programming Language :: Python :: 3
39
+ Classifier: Programming Language :: Python :: 3.10
40
+ Classifier: Programming Language :: Python :: 3.11
41
+ Classifier: Programming Language :: Python :: 3.12
42
+ Classifier: Programming Language :: Python :: 3.13
43
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
44
+ Classifier: Typing :: Typed
45
+ Requires-Python: >=3.10
46
+ Description-Content-Type: text/markdown
47
+ License-File: LICENSE
48
+ Requires-Dist: torch>=2.0
49
+ Provides-Extra: test
50
+ Requires-Dist: pytest; extra == "test"
51
+ Provides-Extra: research
52
+ Requires-Dist: transformers>=4.40; extra == "research"
53
+ Requires-Dist: espnet; extra == "research"
54
+ Dynamic: license-file
55
+
56
+ # NNSchemaParser
57
+
58
+ **NNSchemaParser** converts a PyTorch `nn.Module` into a serializable, inspectable
59
+ graph representation — the **`ModelBlockSchema`** — that keeps the *original variable
60
+ names of the forward source* and *every sub-module* (including MoE experts that a
61
+ single trace would never visit). The schema can then be executed by a built-in
62
+ **schema interpreter** that re-dispatches every recorded call to the real torch
63
+ callables, so the graph runs exactly like the model — and can optionally capture
64
+ the value of every intermediate tensor.
65
+
66
+ ```
67
+ nn.Module ──parse──► ModelBlockSchema ──interpret──► outputs / captured values
68
+ (your code) (names + graph) (identical to model(x))
69
+ ```
70
+
71
+ ## Why not `torch.fx`?
72
+
73
+ | | `torch.fx.symbolic_trace` | **NNSchemaParser** |
74
+ |---|---|---|
75
+ | Variable names | lost (`_0`, `_1`, …) | **kept** (`hidden_states`, `query`, …) |
76
+ | Untaken branches (MoE experts) | missing | **all present** |
77
+ | Control flow | `graph_module` nodes only | `if` / `for` / `try` as first-class instructions |
78
+ | `*args` / `**kwargs` | limited | supported in signatures and call sites |
79
+ | Comprehensions | unsupported | compiled to helper functions |
80
+ | Re-executable | via the FX machinery | via a small self-contained interpreter |
81
+
82
+ ## Quick start
83
+
84
+ ```python
85
+ import torch
86
+ from torch import nn
87
+
88
+ from nnschemaparser import NNParser, SchemaInterpreter, TraceRecorder, dump_schema
89
+
90
+
91
+ class MLP(nn.Module):
92
+ def __init__(self) -> None:
93
+ super().__init__()
94
+ self.fc1 = nn.Linear(784, 256)
95
+ self.fc2 = nn.Linear(256, 10)
96
+
97
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
98
+ hidden = self.fc1(x)
99
+ activated = hidden.relu()
100
+ return self.fc2(activated)
101
+
102
+
103
+ model = MLP().eval()
104
+ x = torch.randn(2, 784)
105
+
106
+ # 1. Parse the model into a schema (resolves shapes and dtypes)
107
+ schema = NNParser.parse_model(
108
+ model=model,
109
+ inputs_tensors_shapes={"x": (2, 784)},
110
+ inputs_tensors_dtypes={"x": "torch.float32"},
111
+ inputs_tensors_values={"x": x},
112
+ )
113
+
114
+ # 2. Dump a readable view of the whole block tree
115
+ dump_schema(schema=schema, path="schema.txt")
116
+
117
+ # 3. Execute it with a tracer and compare with the real forward
118
+ tracer = TraceRecorder()
119
+ outputs = SchemaInterpreter(capture_values=True, listeners=[tracer]).run(
120
+ block=schema,
121
+ scope={"x": x.clone()},
122
+ )
123
+ tracer.save("trace.txt")
124
+ reference = model(x)
125
+ assert torch.equal(outputs[0], reference)
126
+ ```
127
+
128
+ The schema dump of `MLP` reads exactly like the source — original variable
129
+ names, every sub-module, and the resolved shape of every step:
130
+
131
+ ```
132
+ === BLOCK MLP ===
133
+ inputs:
134
+ - x = torch.float32[2, 784]
135
+ outputs:
136
+ - torch.float32[2, 10]
137
+ weights:
138
+ (none)
139
+ sub-blocks:
140
+ - fc1(Linear)
141
+ - fc2(Linear)
142
+ body:
143
+ hidden = CALL fc1(x) -> torch.float32[2, 256]
144
+ $t1 = getattr(hidden, 'relu') -> builtin_function_or_method
145
+ activated = hidden.relu() -> torch.float32[2, 256]
146
+ $t2 = CALL fc2(activated) -> torch.float32[2, 10]
147
+ RETURN $t2
148
+ ```
149
+
150
+ The tracer records the same execution chronologically, with the value of
151
+ every intermediate tensor (one line per event, indented by the nesting
152
+ depth) — this is what a WebUI consumes to display every intermediate value:
153
+
154
+ ```
155
+ [00001] ENTER BLOCK MLP
156
+ [00002] in x = torch.float32[2, 784] abs_sum=1241.4496 min=-3.0303 max=3.3046
157
+ [00003] ENTER BLOCK Linear
158
+ [00004] in input = torch.float32[2, 784] abs_sum=1241.4496 min=-3.0303 max=3.3046
159
+ [00005] $t1 = F.linear(input, weight, bias) [torch.float32[2, 256] abs_sum=215.3293 ...]
160
+ [00006] EXIT BLOCK Linear -> (torch.float32[2, 256] abs_sum=215.3293 ...)
161
+ [00007] hidden = CALL fc1 [Linear] [torch.float32[2, 256] abs_sum=215.3293 ...]
162
+ ```
163
+
164
+ ## Installation
165
+
166
+ ```bash
167
+ pip install . # or: pip install -e .[research] for the harness extras
168
+ ```
169
+
170
+ Requires Python ≥ 3.12 and PyTorch ≥ 2.0. The model classes must be importable
171
+ from a **real file** (the parser reads the forward source with
172
+ `inspect.getsource`, which fails for classes defined in a REPL or stdin).
173
+
174
+ ## Validation
175
+
176
+ A robustness sweep of **58 models** — 48 Hugging Face hub checkpoints (text,
177
+ vision, audio, multimodal) plus 10 locally built architectures (MLP, RNN, CNN,
178
+ autoencoder, GAN, GNN) — runs each model through *parse → interpret → compare
179
+ against the real forward*. **All 58 pass with `maxdiff = 0.0`** against the real
180
+ forward. See [docs/DOCUMENTATION.md](docs/DOCUMENTATION.md) for the full model
181
+ matrix and the sweep CLI.
182
+
183
+ ## Documentation
184
+
185
+ - **[docs/API.md](docs/API.md)** — the public API: parsing, executing,
186
+ tracing, dumping and walking schemas.
187
+ - **[docs/DOCUMENTATION.md](docs/DOCUMENTATION.md)** — full documentation:
188
+ how the parser works, the schema and instruction reference, the
189
+ interpreter, usage recipes, supported syntax, supported/unsupported
190
+ models, and limitations.
191
+ - **[CHANGELOG.md](CHANGELOG.md)** — the release history.
192
+ - `tests/hf_sweep.py` — the validation harness CLI (58 HuggingFace models).
193
+ - `tests/test_new_syntax.py`, `tests/test_parser.py`,
194
+ `tests/test_methods_and_scalars.py` — the fixture test suite (pytest,
195
+ no network needed), also runnable with `python -m tests.test_new_syntax`.
196
+ - `tests/test_speech_models.py` — the espnet/torchaudio regression tests.
197
+
198
+ ## License
199
+
200
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,145 @@
1
+ # NNSchemaParser
2
+
3
+ **NNSchemaParser** converts a PyTorch `nn.Module` into a serializable, inspectable
4
+ graph representation — the **`ModelBlockSchema`** — that keeps the *original variable
5
+ names of the forward source* and *every sub-module* (including MoE experts that a
6
+ single trace would never visit). The schema can then be executed by a built-in
7
+ **schema interpreter** that re-dispatches every recorded call to the real torch
8
+ callables, so the graph runs exactly like the model — and can optionally capture
9
+ the value of every intermediate tensor.
10
+
11
+ ```
12
+ nn.Module ──parse──► ModelBlockSchema ──interpret──► outputs / captured values
13
+ (your code) (names + graph) (identical to model(x))
14
+ ```
15
+
16
+ ## Why not `torch.fx`?
17
+
18
+ | | `torch.fx.symbolic_trace` | **NNSchemaParser** |
19
+ |---|---|---|
20
+ | Variable names | lost (`_0`, `_1`, …) | **kept** (`hidden_states`, `query`, …) |
21
+ | Untaken branches (MoE experts) | missing | **all present** |
22
+ | Control flow | `graph_module` nodes only | `if` / `for` / `try` as first-class instructions |
23
+ | `*args` / `**kwargs` | limited | supported in signatures and call sites |
24
+ | Comprehensions | unsupported | compiled to helper functions |
25
+ | Re-executable | via the FX machinery | via a small self-contained interpreter |
26
+
27
+ ## Quick start
28
+
29
+ ```python
30
+ import torch
31
+ from torch import nn
32
+
33
+ from nnschemaparser import NNParser, SchemaInterpreter, TraceRecorder, dump_schema
34
+
35
+
36
+ class MLP(nn.Module):
37
+ def __init__(self) -> None:
38
+ super().__init__()
39
+ self.fc1 = nn.Linear(784, 256)
40
+ self.fc2 = nn.Linear(256, 10)
41
+
42
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
43
+ hidden = self.fc1(x)
44
+ activated = hidden.relu()
45
+ return self.fc2(activated)
46
+
47
+
48
+ model = MLP().eval()
49
+ x = torch.randn(2, 784)
50
+
51
+ # 1. Parse the model into a schema (resolves shapes and dtypes)
52
+ schema = NNParser.parse_model(
53
+ model=model,
54
+ inputs_tensors_shapes={"x": (2, 784)},
55
+ inputs_tensors_dtypes={"x": "torch.float32"},
56
+ inputs_tensors_values={"x": x},
57
+ )
58
+
59
+ # 2. Dump a readable view of the whole block tree
60
+ dump_schema(schema=schema, path="schema.txt")
61
+
62
+ # 3. Execute it with a tracer and compare with the real forward
63
+ tracer = TraceRecorder()
64
+ outputs = SchemaInterpreter(capture_values=True, listeners=[tracer]).run(
65
+ block=schema,
66
+ scope={"x": x.clone()},
67
+ )
68
+ tracer.save("trace.txt")
69
+ reference = model(x)
70
+ assert torch.equal(outputs[0], reference)
71
+ ```
72
+
73
+ The schema dump of `MLP` reads exactly like the source — original variable
74
+ names, every sub-module, and the resolved shape of every step:
75
+
76
+ ```
77
+ === BLOCK MLP ===
78
+ inputs:
79
+ - x = torch.float32[2, 784]
80
+ outputs:
81
+ - torch.float32[2, 10]
82
+ weights:
83
+ (none)
84
+ sub-blocks:
85
+ - fc1(Linear)
86
+ - fc2(Linear)
87
+ body:
88
+ hidden = CALL fc1(x) -> torch.float32[2, 256]
89
+ $t1 = getattr(hidden, 'relu') -> builtin_function_or_method
90
+ activated = hidden.relu() -> torch.float32[2, 256]
91
+ $t2 = CALL fc2(activated) -> torch.float32[2, 10]
92
+ RETURN $t2
93
+ ```
94
+
95
+ The tracer records the same execution chronologically, with the value of
96
+ every intermediate tensor (one line per event, indented by the nesting
97
+ depth) — this is what a WebUI consumes to display every intermediate value:
98
+
99
+ ```
100
+ [00001] ENTER BLOCK MLP
101
+ [00002] in x = torch.float32[2, 784] abs_sum=1241.4496 min=-3.0303 max=3.3046
102
+ [00003] ENTER BLOCK Linear
103
+ [00004] in input = torch.float32[2, 784] abs_sum=1241.4496 min=-3.0303 max=3.3046
104
+ [00005] $t1 = F.linear(input, weight, bias) [torch.float32[2, 256] abs_sum=215.3293 ...]
105
+ [00006] EXIT BLOCK Linear -> (torch.float32[2, 256] abs_sum=215.3293 ...)
106
+ [00007] hidden = CALL fc1 [Linear] [torch.float32[2, 256] abs_sum=215.3293 ...]
107
+ ```
108
+
109
+ ## Installation
110
+
111
+ ```bash
112
+ pip install . # or: pip install -e .[research] for the harness extras
113
+ ```
114
+
115
+ Requires Python ≥ 3.12 and PyTorch ≥ 2.0. The model classes must be importable
116
+ from a **real file** (the parser reads the forward source with
117
+ `inspect.getsource`, which fails for classes defined in a REPL or stdin).
118
+
119
+ ## Validation
120
+
121
+ A robustness sweep of **58 models** — 48 Hugging Face hub checkpoints (text,
122
+ vision, audio, multimodal) plus 10 locally built architectures (MLP, RNN, CNN,
123
+ autoencoder, GAN, GNN) — runs each model through *parse → interpret → compare
124
+ against the real forward*. **All 58 pass with `maxdiff = 0.0`** against the real
125
+ forward. See [docs/DOCUMENTATION.md](docs/DOCUMENTATION.md) for the full model
126
+ matrix and the sweep CLI.
127
+
128
+ ## Documentation
129
+
130
+ - **[docs/API.md](docs/API.md)** — the public API: parsing, executing,
131
+ tracing, dumping and walking schemas.
132
+ - **[docs/DOCUMENTATION.md](docs/DOCUMENTATION.md)** — full documentation:
133
+ how the parser works, the schema and instruction reference, the
134
+ interpreter, usage recipes, supported syntax, supported/unsupported
135
+ models, and limitations.
136
+ - **[CHANGELOG.md](CHANGELOG.md)** — the release history.
137
+ - `tests/hf_sweep.py` — the validation harness CLI (58 HuggingFace models).
138
+ - `tests/test_new_syntax.py`, `tests/test_parser.py`,
139
+ `tests/test_methods_and_scalars.py` — the fixture test suite (pytest,
140
+ no network needed), also runnable with `python -m tests.test_new_syntax`.
141
+ - `tests/test_speech_models.py` — the espnet/torchaudio regression tests.
142
+
143
+ ## License
144
+
145
+ MIT — see [LICENSE](LICENSE).
@@ -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