torch-flowgraph 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.
- torch_flowgraph/__init__.py +50 -0
- torch_flowgraph/__main__.py +8 -0
- torch_flowgraph/_post_import.py +78 -0
- torch_flowgraph/_version.py +24 -0
- torch_flowgraph/attributes.py +262 -0
- torch_flowgraph/cli.py +44 -0
- torch_flowgraph/clustering.py +282 -0
- torch_flowgraph/clusters.py +209 -0
- torch_flowgraph/dot.py +83 -0
- torch_flowgraph/graph.py +1090 -0
- torch_flowgraph/model/__init__.py +18 -0
- torch_flowgraph/model/graph.py +119 -0
- torch_flowgraph/model/pytorch.py +1058 -0
- torch_flowgraph/nodes.py +664 -0
- torch_flowgraph/parsing.py +1474 -0
- torch_flowgraph/patches.py +539 -0
- torch_flowgraph/plotting.py +368 -0
- torch_flowgraph/validation.py +135 -0
- torch_flowgraph-0.1.0.dist-info/METADATA +105 -0
- torch_flowgraph-0.1.0.dist-info/RECORD +25 -0
- torch_flowgraph-0.1.0.dist-info/WHEEL +5 -0
- torch_flowgraph-0.1.0.dist-info/entry_points.txt +2 -0
- torch_flowgraph-0.1.0.dist-info/licenses/LICENSE +79 -0
- torch_flowgraph-0.1.0.dist-info/licenses/NOTICE +8 -0
- torch_flowgraph-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 European Centre for Medium-Range Weather Forecasts (ECMWF)
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
"""torch-flowgraph: PyTorch computation graph tracing and visualization.
|
|
6
|
+
|
|
7
|
+
Public API
|
|
8
|
+
----------
|
|
9
|
+
:class:`GraphBase`
|
|
10
|
+
Base class for all computation graph representations. JSON-deserialised
|
|
11
|
+
graphs are returned as :class:`GraphBase` instances.
|
|
12
|
+
|
|
13
|
+
:class:`Clustering`
|
|
14
|
+
Abstract base class for reclustering strategies.
|
|
15
|
+
|
|
16
|
+
:class:`ClanClustering`
|
|
17
|
+
Re-cluster using the McCreary clan-based graph-parsing algorithm.
|
|
18
|
+
|
|
19
|
+
:class:`LevelClustering`
|
|
20
|
+
Keep only cluster boxes at a given depth; all nodes remain visible.
|
|
21
|
+
|
|
22
|
+
:class:`NameClustering`
|
|
23
|
+
Keep only cluster boxes with specific names; all nodes remain visible.
|
|
24
|
+
|
|
25
|
+
:func:`apply_clan_clustering`
|
|
26
|
+
Convenience wrapper around ``graph.recluster(ClanClustering())``.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from . import patches as _patches # noqa: F401 — apply torch meta-device patches
|
|
30
|
+
from ._post_import import on_import
|
|
31
|
+
from .clustering import ClanClustering
|
|
32
|
+
from .clustering import Clustering
|
|
33
|
+
from .clustering import LevelClustering
|
|
34
|
+
from .clustering import NameClustering
|
|
35
|
+
from .clustering import NoClustering
|
|
36
|
+
from .clustering import apply_clan_clustering
|
|
37
|
+
from .graph import GraphBase
|
|
38
|
+
from .validation import validate_graph
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
"GraphBase",
|
|
42
|
+
"Clustering",
|
|
43
|
+
"ClanClustering",
|
|
44
|
+
"LevelClustering",
|
|
45
|
+
"NameClustering",
|
|
46
|
+
"NoClustering",
|
|
47
|
+
"apply_clan_clustering",
|
|
48
|
+
"validate_graph",
|
|
49
|
+
"on_import",
|
|
50
|
+
]
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 European Centre for Medium-Range Weather Forecasts (ECMWF)
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
"""Post-import hooks.
|
|
5
|
+
|
|
6
|
+
Provides :func:`on_import` to register a callback that fires the first time
|
|
7
|
+
a given module is imported (or immediately, if already loaded). This lets a
|
|
8
|
+
package register patches that apply only when a third-party dependency is
|
|
9
|
+
actually pulled in by some other code path.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import builtins
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
_pending: dict = {}
|
|
16
|
+
_done: set = set()
|
|
17
|
+
_orig_import = None
|
|
18
|
+
_depth = 0
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def on_import(module_name, callback):
|
|
22
|
+
"""Run *callback(module)* the first time *module_name* is imported.
|
|
23
|
+
|
|
24
|
+
If the module is already in :data:`sys.modules`, *callback* is invoked
|
|
25
|
+
immediately. Otherwise an import hook is installed that fires the
|
|
26
|
+
callback exactly once, the first time the module appears in
|
|
27
|
+
:data:`sys.modules` *after* the outermost ``import`` statement that
|
|
28
|
+
caused it to load has fully completed. (Submodule imports during a
|
|
29
|
+
package's ``__init__.py`` execution don't fire callbacks for the parent
|
|
30
|
+
package, which would otherwise see a half-initialised module.)
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
module_name:
|
|
35
|
+
Fully qualified module name (e.g. ``"torch_geometric.utils._subgraph"``).
|
|
36
|
+
callback:
|
|
37
|
+
Callable taking the imported module as its single argument.
|
|
38
|
+
"""
|
|
39
|
+
if module_name in sys.modules and module_name not in _pending:
|
|
40
|
+
_done.add(module_name)
|
|
41
|
+
callback(sys.modules[module_name])
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
_pending.setdefault(module_name, []).append(callback)
|
|
45
|
+
_install_hook()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _install_hook():
|
|
49
|
+
global _orig_import
|
|
50
|
+
if _orig_import is not None:
|
|
51
|
+
return
|
|
52
|
+
_orig_import = builtins.__import__
|
|
53
|
+
|
|
54
|
+
def _wrapped_import(name, globals=None, locals=None, fromlist=(), level=0):
|
|
55
|
+
global _depth
|
|
56
|
+
_depth += 1
|
|
57
|
+
try:
|
|
58
|
+
result = _orig_import(name, globals, locals, fromlist, level)
|
|
59
|
+
finally:
|
|
60
|
+
_depth -= 1
|
|
61
|
+
|
|
62
|
+
# Only fire callbacks once we have returned to the outermost
|
|
63
|
+
# __import__ call. Otherwise modules in sys.modules may still be
|
|
64
|
+
# mid-execution (a package whose __init__.py is partially run).
|
|
65
|
+
if _depth == 0 and _pending:
|
|
66
|
+
for pending_name in list(_pending):
|
|
67
|
+
if pending_name in sys.modules and pending_name not in _done:
|
|
68
|
+
_done.add(pending_name)
|
|
69
|
+
callbacks = _pending.pop(pending_name)
|
|
70
|
+
mod = sys.modules[pending_name]
|
|
71
|
+
for cb in callbacks:
|
|
72
|
+
try:
|
|
73
|
+
cb(mod)
|
|
74
|
+
except Exception:
|
|
75
|
+
pass
|
|
76
|
+
return result
|
|
77
|
+
|
|
78
|
+
builtins.__import__ = _wrapped_import
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.1.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 European Centre for Medium-Range Weather Forecasts (ECMWF)
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
"""Graphviz DOT attribute name lists.
|
|
5
|
+
|
|
6
|
+
This module enumerates the valid attribute names for different element types
|
|
7
|
+
in Graphviz DOT files. The lists are used by the plotting functions to
|
|
8
|
+
filter out unknown attributes when building DOT output, ensuring that only
|
|
9
|
+
recognised attributes are emitted.
|
|
10
|
+
|
|
11
|
+
Attribute hierarchy
|
|
12
|
+
-------------------
|
|
13
|
+
``COMMON_ATTRIBUTES``
|
|
14
|
+
Attributes valid for all element types (nodes, edges, graphs, clusters).
|
|
15
|
+
``NODE_EDGE_ATTRIBUTES``
|
|
16
|
+
Attributes valid for both nodes and edges (extends ``COMMON_ATTRIBUTES``).
|
|
17
|
+
``NODE_ATTRIBUTES``
|
|
18
|
+
Node-specific attributes (extends ``NODE_EDGE_ATTRIBUTES``).
|
|
19
|
+
``EDGE_ATTRIBUTES``
|
|
20
|
+
Edge-specific attributes (extends ``NODE_EDGE_ATTRIBUTES``).
|
|
21
|
+
``CLUSTER_GRAPH_ATTRIBUTES``
|
|
22
|
+
Attributes valid for both clusters and graphs (extends
|
|
23
|
+
``COMMON_ATTRIBUTES``).
|
|
24
|
+
``CLUSTER_ATTRIBUTES``
|
|
25
|
+
Cluster-specific attributes (extends ``CLUSTER_GRAPH_ATTRIBUTES``).
|
|
26
|
+
``GRAPH_ATTRIBUTES``
|
|
27
|
+
Graph-specific attributes (extends ``CLUSTER_GRAPH_ATTRIBUTES``).
|
|
28
|
+
|
|
29
|
+
Reference: https://graphviz.org/doc/info/attrs.html
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
# Attributes shared across all Graphviz element types.
|
|
33
|
+
COMMON_ATTRIBUTES = [
|
|
34
|
+
"URL",
|
|
35
|
+
"class",
|
|
36
|
+
"colorscheme",
|
|
37
|
+
"fontcolor",
|
|
38
|
+
"fontname",
|
|
39
|
+
"fontsize",
|
|
40
|
+
"href",
|
|
41
|
+
"id",
|
|
42
|
+
"label",
|
|
43
|
+
"nojustify",
|
|
44
|
+
"style",
|
|
45
|
+
"target",
|
|
46
|
+
"tooltip",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
# Attributes valid for both nodes and edges (but not graphs/clusters).
|
|
50
|
+
NODE_EDGE_ATTRIBUTES = COMMON_ATTRIBUTES + [
|
|
51
|
+
"color",
|
|
52
|
+
"comment",
|
|
53
|
+
"fillcolor",
|
|
54
|
+
"layer",
|
|
55
|
+
"penwidth",
|
|
56
|
+
"pos",
|
|
57
|
+
"showboxes",
|
|
58
|
+
"xlabel",
|
|
59
|
+
"xlp",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
# Attributes valid only for nodes.
|
|
63
|
+
NODE_ATTRIBUTES = NODE_EDGE_ATTRIBUTES + [
|
|
64
|
+
"area",
|
|
65
|
+
"distortion",
|
|
66
|
+
"fixedsize",
|
|
67
|
+
"gradientangle",
|
|
68
|
+
"group",
|
|
69
|
+
"height",
|
|
70
|
+
"image",
|
|
71
|
+
"imagepos",
|
|
72
|
+
"imagescale",
|
|
73
|
+
"labelloc",
|
|
74
|
+
"margin",
|
|
75
|
+
"ordering",
|
|
76
|
+
"orientation",
|
|
77
|
+
"peripheries",
|
|
78
|
+
"pin",
|
|
79
|
+
"rects",
|
|
80
|
+
"regular",
|
|
81
|
+
"root",
|
|
82
|
+
"samplepoints",
|
|
83
|
+
"shape",
|
|
84
|
+
"shapefile",
|
|
85
|
+
"sides",
|
|
86
|
+
"skew",
|
|
87
|
+
"sortv",
|
|
88
|
+
"vertices",
|
|
89
|
+
"width",
|
|
90
|
+
"z",
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
# Attributes valid only for edges.
|
|
94
|
+
EDGE_ATTRIBUTES = NODE_EDGE_ATTRIBUTES + [
|
|
95
|
+
"arrowhead",
|
|
96
|
+
"arrowsize",
|
|
97
|
+
"arrowtail",
|
|
98
|
+
"constraint",
|
|
99
|
+
"decorate",
|
|
100
|
+
"dir",
|
|
101
|
+
"edgeURL",
|
|
102
|
+
"edgehref",
|
|
103
|
+
"edgetarget",
|
|
104
|
+
"edgetooltip",
|
|
105
|
+
"headURL",
|
|
106
|
+
"head_lp",
|
|
107
|
+
"headclip",
|
|
108
|
+
"headhref",
|
|
109
|
+
"headlabel",
|
|
110
|
+
"headport",
|
|
111
|
+
"headtarget",
|
|
112
|
+
"headtooltip",
|
|
113
|
+
"labelURL",
|
|
114
|
+
"labelangle",
|
|
115
|
+
"labeldistance",
|
|
116
|
+
"labelfloat",
|
|
117
|
+
"labelfontcolor",
|
|
118
|
+
"labelfontname",
|
|
119
|
+
"labelfontsize",
|
|
120
|
+
"labelhref",
|
|
121
|
+
"labeltarget",
|
|
122
|
+
"labeltooltip",
|
|
123
|
+
"len",
|
|
124
|
+
"lhead",
|
|
125
|
+
"lp",
|
|
126
|
+
"ltail",
|
|
127
|
+
"minlen",
|
|
128
|
+
"samehead",
|
|
129
|
+
"sametail",
|
|
130
|
+
"tailURL",
|
|
131
|
+
"tail_lp",
|
|
132
|
+
"tailclip",
|
|
133
|
+
"tailhref",
|
|
134
|
+
"taillabel",
|
|
135
|
+
"tailport",
|
|
136
|
+
"tailtarget",
|
|
137
|
+
"tailtooltip",
|
|
138
|
+
"weight",
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
# Attributes valid for both clusters and top-level graphs.
|
|
142
|
+
CLUSTER_GRAPH_ATTRIBUTES = COMMON_ATTRIBUTES + [
|
|
143
|
+
"K",
|
|
144
|
+
"bb",
|
|
145
|
+
"bgcolor",
|
|
146
|
+
"gradientangle",
|
|
147
|
+
"labeljust",
|
|
148
|
+
"labelloc",
|
|
149
|
+
"lheight",
|
|
150
|
+
"lp",
|
|
151
|
+
"lwidth",
|
|
152
|
+
"margin",
|
|
153
|
+
"sortv",
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
# Attributes valid only for clusters (subgraphs whose name begins with
|
|
157
|
+
# ``cluster_``).
|
|
158
|
+
CLUSTER_ATTRIBUTES = CLUSTER_GRAPH_ATTRIBUTES + [
|
|
159
|
+
"area",
|
|
160
|
+
"cluster",
|
|
161
|
+
"color",
|
|
162
|
+
"fillcolor",
|
|
163
|
+
"layer",
|
|
164
|
+
"pencolor",
|
|
165
|
+
"penwidth",
|
|
166
|
+
"peripheries",
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
# Attributes valid only for the top-level graph.
|
|
170
|
+
GRAPH_ATTRIBUTES = CLUSTER_GRAPH_ATTRIBUTES + [
|
|
171
|
+
"Damping",
|
|
172
|
+
"TBbalance",
|
|
173
|
+
"_background",
|
|
174
|
+
"beautify",
|
|
175
|
+
"center",
|
|
176
|
+
"charset",
|
|
177
|
+
"clusterrank",
|
|
178
|
+
"comment",
|
|
179
|
+
"compound",
|
|
180
|
+
"concentrate",
|
|
181
|
+
"defaultdist",
|
|
182
|
+
"dim",
|
|
183
|
+
"dimen",
|
|
184
|
+
"diredgeconstraints",
|
|
185
|
+
"dpi",
|
|
186
|
+
"epsilon",
|
|
187
|
+
"esep",
|
|
188
|
+
"fontnames",
|
|
189
|
+
"fontpath",
|
|
190
|
+
"forcelabels",
|
|
191
|
+
"imagepath",
|
|
192
|
+
"inputscale",
|
|
193
|
+
"label_scheme",
|
|
194
|
+
"landscape",
|
|
195
|
+
"layerlistsep",
|
|
196
|
+
"layers",
|
|
197
|
+
"layerselect",
|
|
198
|
+
"layersep",
|
|
199
|
+
"layout",
|
|
200
|
+
"levels",
|
|
201
|
+
"levelsgap",
|
|
202
|
+
"linelength",
|
|
203
|
+
"maxiter",
|
|
204
|
+
"mclimit",
|
|
205
|
+
"mindist",
|
|
206
|
+
"mode",
|
|
207
|
+
"model",
|
|
208
|
+
"newrank",
|
|
209
|
+
"nodesep",
|
|
210
|
+
"normalize",
|
|
211
|
+
"notranslate",
|
|
212
|
+
"nslimit",
|
|
213
|
+
"nslimit1",
|
|
214
|
+
"oneblock",
|
|
215
|
+
"ordering",
|
|
216
|
+
"orientation",
|
|
217
|
+
"outputorder",
|
|
218
|
+
"overlap",
|
|
219
|
+
"overlap_scaling",
|
|
220
|
+
"overlap_shrink",
|
|
221
|
+
"pack",
|
|
222
|
+
"packmode",
|
|
223
|
+
"pad",
|
|
224
|
+
"page",
|
|
225
|
+
"pagedir",
|
|
226
|
+
"quadtree",
|
|
227
|
+
"quantum",
|
|
228
|
+
"rankdir",
|
|
229
|
+
"ranksep",
|
|
230
|
+
"ratio",
|
|
231
|
+
"remincross",
|
|
232
|
+
"repulsiveforce",
|
|
233
|
+
"resolution",
|
|
234
|
+
"root",
|
|
235
|
+
"rotate",
|
|
236
|
+
"rotation",
|
|
237
|
+
"scale",
|
|
238
|
+
"searchsize",
|
|
239
|
+
"sep",
|
|
240
|
+
"showboxes",
|
|
241
|
+
"size",
|
|
242
|
+
"smoothing",
|
|
243
|
+
"splines",
|
|
244
|
+
"start",
|
|
245
|
+
"stylesheet",
|
|
246
|
+
"truecolor",
|
|
247
|
+
"viewport",
|
|
248
|
+
"voro_margin",
|
|
249
|
+
"xdotversion",
|
|
250
|
+
]
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
if __name__ == "__main__":
|
|
254
|
+
# Helper script to verify that the attribute group boundaries are correct.
|
|
255
|
+
common = (set(CLUSTER_ATTRIBUTES).intersection(GRAPH_ATTRIBUTES)) - set(COMMON_ATTRIBUTES)
|
|
256
|
+
print(f"CLUSTER_GRAPH_ATTRIBUTES = COMMON_ATTRIBUTES + {repr(sorted(common- set(COMMON_ATTRIBUTES)))}")
|
|
257
|
+
print(
|
|
258
|
+
f"CLUSTER_ATTRIBUTES = CLUSTER_GRAPH_ATTRIBUTES+{repr(sorted(set(CLUSTER_ATTRIBUTES)-common- set(COMMON_ATTRIBUTES)))}"
|
|
259
|
+
)
|
|
260
|
+
print(
|
|
261
|
+
f"GRAPH_ATTRIBUTES = CLUSTER_GRAPH_ATTRIBUTES + {repr(sorted(set(GRAPH_ATTRIBUTES)-common- set(COMMON_ATTRIBUTES)))}"
|
|
262
|
+
)
|
torch_flowgraph/cli.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 European Centre for Medium-Range Weather Forecasts (ECMWF)
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
"""Command-line interface for torch-flowgraph.
|
|
5
|
+
|
|
6
|
+
Usage::
|
|
7
|
+
|
|
8
|
+
torch-flowgraph validate path/to/graph.json
|
|
9
|
+
python -m torch_flowgraph validate path/to/graph.json
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import sys
|
|
16
|
+
|
|
17
|
+
from .graph import GraphBase
|
|
18
|
+
from .validation import validate_graph
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _cmd_validate(args) -> int:
|
|
22
|
+
graph = GraphBase.from_json_file(args.path)
|
|
23
|
+
ok = validate_graph(graph, verbose=not args.quiet)
|
|
24
|
+
return 0 if ok else 1
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def main(argv=None) -> int:
|
|
28
|
+
parser = argparse.ArgumentParser(
|
|
29
|
+
prog="torch-flowgraph",
|
|
30
|
+
description="Inspect and validate torch-flowgraph JSON graphs.",
|
|
31
|
+
)
|
|
32
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
33
|
+
|
|
34
|
+
validate = sub.add_parser("validate", help="Check a graph JSON file for inconsistencies.")
|
|
35
|
+
validate.add_argument("path", help="Path to a graph JSON file.")
|
|
36
|
+
validate.add_argument("-q", "--quiet", action="store_true", help="Suppress the report; only set the exit code.")
|
|
37
|
+
validate.set_defaults(func=_cmd_validate)
|
|
38
|
+
|
|
39
|
+
args = parser.parse_args(argv)
|
|
40
|
+
return args.func(args)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if __name__ == "__main__":
|
|
44
|
+
sys.exit(main())
|