tnfr 4.5.2__py3-none-any.whl → 7.0.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.
Potentially problematic release.
This version of tnfr might be problematic. Click here for more details.
- tnfr/__init__.py +275 -51
- tnfr/__init__.pyi +33 -0
- tnfr/_compat.py +10 -0
- tnfr/_generated_version.py +34 -0
- tnfr/_version.py +49 -0
- tnfr/_version.pyi +7 -0
- tnfr/alias.py +117 -31
- tnfr/alias.pyi +108 -0
- tnfr/cache.py +6 -572
- tnfr/cache.pyi +16 -0
- tnfr/callback_utils.py +16 -38
- tnfr/callback_utils.pyi +79 -0
- tnfr/cli/__init__.py +34 -14
- tnfr/cli/__init__.pyi +26 -0
- tnfr/cli/arguments.py +211 -28
- tnfr/cli/arguments.pyi +27 -0
- tnfr/cli/execution.py +470 -50
- tnfr/cli/execution.pyi +70 -0
- tnfr/cli/utils.py +18 -3
- tnfr/cli/utils.pyi +8 -0
- tnfr/config/__init__.py +13 -0
- tnfr/config/__init__.pyi +10 -0
- tnfr/{constants_glyphs.py → config/constants.py} +26 -20
- tnfr/config/constants.pyi +12 -0
- tnfr/config/feature_flags.py +83 -0
- tnfr/{config.py → config/init.py} +11 -7
- tnfr/config/init.pyi +8 -0
- tnfr/config/operator_names.py +93 -0
- tnfr/config/operator_names.pyi +28 -0
- tnfr/config/presets.py +84 -0
- tnfr/config/presets.pyi +7 -0
- tnfr/constants/__init__.py +80 -29
- tnfr/constants/__init__.pyi +92 -0
- tnfr/constants/aliases.py +31 -0
- tnfr/constants/core.py +4 -4
- tnfr/constants/core.pyi +17 -0
- tnfr/constants/init.py +1 -1
- tnfr/constants/init.pyi +12 -0
- tnfr/constants/metric.py +7 -15
- tnfr/constants/metric.pyi +19 -0
- tnfr/dynamics/__init__.py +165 -633
- tnfr/dynamics/__init__.pyi +82 -0
- tnfr/dynamics/adaptation.py +267 -0
- tnfr/dynamics/aliases.py +23 -0
- tnfr/dynamics/coordination.py +385 -0
- tnfr/dynamics/dnfr.py +2283 -400
- tnfr/dynamics/dnfr.pyi +24 -0
- tnfr/dynamics/integrators.py +406 -98
- tnfr/dynamics/integrators.pyi +34 -0
- tnfr/dynamics/runtime.py +881 -0
- tnfr/dynamics/sampling.py +10 -5
- tnfr/dynamics/sampling.pyi +7 -0
- tnfr/dynamics/selectors.py +719 -0
- tnfr/execution.py +70 -48
- tnfr/execution.pyi +45 -0
- tnfr/flatten.py +13 -9
- tnfr/flatten.pyi +21 -0
- tnfr/gamma.py +66 -53
- tnfr/gamma.pyi +34 -0
- tnfr/glyph_history.py +110 -52
- tnfr/glyph_history.pyi +35 -0
- tnfr/glyph_runtime.py +16 -0
- tnfr/glyph_runtime.pyi +9 -0
- tnfr/immutable.py +69 -28
- tnfr/immutable.pyi +34 -0
- tnfr/initialization.py +16 -16
- tnfr/initialization.pyi +65 -0
- tnfr/io.py +6 -240
- tnfr/io.pyi +16 -0
- tnfr/locking.pyi +7 -0
- tnfr/mathematics/__init__.py +81 -0
- tnfr/mathematics/backend.py +426 -0
- tnfr/mathematics/dynamics.py +398 -0
- tnfr/mathematics/epi.py +254 -0
- tnfr/mathematics/generators.py +222 -0
- tnfr/mathematics/metrics.py +119 -0
- tnfr/mathematics/operators.py +233 -0
- tnfr/mathematics/operators_factory.py +71 -0
- tnfr/mathematics/projection.py +78 -0
- tnfr/mathematics/runtime.py +173 -0
- tnfr/mathematics/spaces.py +247 -0
- tnfr/mathematics/transforms.py +292 -0
- tnfr/metrics/__init__.py +10 -10
- tnfr/metrics/__init__.pyi +20 -0
- tnfr/metrics/coherence.py +993 -324
- tnfr/metrics/common.py +23 -16
- tnfr/metrics/common.pyi +46 -0
- tnfr/metrics/core.py +251 -35
- tnfr/metrics/core.pyi +13 -0
- tnfr/metrics/diagnosis.py +708 -111
- tnfr/metrics/diagnosis.pyi +85 -0
- tnfr/metrics/export.py +27 -15
- tnfr/metrics/glyph_timing.py +232 -42
- tnfr/metrics/reporting.py +33 -22
- tnfr/metrics/reporting.pyi +12 -0
- tnfr/metrics/sense_index.py +987 -43
- tnfr/metrics/sense_index.pyi +9 -0
- tnfr/metrics/trig.py +214 -23
- tnfr/metrics/trig.pyi +13 -0
- tnfr/metrics/trig_cache.py +115 -22
- tnfr/metrics/trig_cache.pyi +10 -0
- tnfr/node.py +542 -136
- tnfr/node.pyi +178 -0
- tnfr/observers.py +152 -35
- tnfr/observers.pyi +31 -0
- tnfr/ontosim.py +23 -19
- tnfr/ontosim.pyi +28 -0
- tnfr/operators/__init__.py +601 -82
- tnfr/operators/__init__.pyi +45 -0
- tnfr/operators/definitions.py +513 -0
- tnfr/operators/definitions.pyi +78 -0
- tnfr/operators/grammar.py +760 -0
- tnfr/operators/jitter.py +107 -38
- tnfr/operators/jitter.pyi +11 -0
- tnfr/operators/registry.py +75 -0
- tnfr/operators/registry.pyi +13 -0
- tnfr/operators/remesh.py +149 -88
- tnfr/py.typed +0 -0
- tnfr/rng.py +46 -143
- tnfr/rng.pyi +14 -0
- tnfr/schemas/__init__.py +8 -0
- tnfr/schemas/grammar.json +94 -0
- tnfr/selector.py +25 -19
- tnfr/selector.pyi +19 -0
- tnfr/sense.py +72 -62
- tnfr/sense.pyi +23 -0
- tnfr/structural.py +522 -262
- tnfr/structural.pyi +69 -0
- tnfr/telemetry/__init__.py +35 -0
- tnfr/telemetry/cache_metrics.py +226 -0
- tnfr/telemetry/nu_f.py +423 -0
- tnfr/telemetry/nu_f.pyi +123 -0
- tnfr/telemetry/verbosity.py +37 -0
- tnfr/tokens.py +1 -3
- tnfr/tokens.pyi +36 -0
- tnfr/trace.py +270 -113
- tnfr/trace.pyi +40 -0
- tnfr/types.py +574 -6
- tnfr/types.pyi +331 -0
- tnfr/units.py +69 -0
- tnfr/units.pyi +16 -0
- tnfr/utils/__init__.py +217 -0
- tnfr/utils/__init__.pyi +202 -0
- tnfr/utils/cache.py +2395 -0
- tnfr/utils/cache.pyi +468 -0
- tnfr/utils/chunks.py +104 -0
- tnfr/utils/chunks.pyi +21 -0
- tnfr/{collections_utils.py → utils/data.py} +147 -90
- tnfr/utils/data.pyi +64 -0
- tnfr/utils/graph.py +85 -0
- tnfr/utils/graph.pyi +10 -0
- tnfr/utils/init.py +770 -0
- tnfr/utils/init.pyi +78 -0
- tnfr/utils/io.py +456 -0
- tnfr/{helpers → utils}/numeric.py +51 -24
- tnfr/utils/numeric.pyi +21 -0
- tnfr/validation/__init__.py +113 -0
- tnfr/validation/__init__.pyi +77 -0
- tnfr/validation/compatibility.py +95 -0
- tnfr/validation/compatibility.pyi +6 -0
- tnfr/validation/grammar.py +71 -0
- tnfr/validation/grammar.pyi +40 -0
- tnfr/validation/graph.py +138 -0
- tnfr/validation/graph.pyi +17 -0
- tnfr/validation/rules.py +281 -0
- tnfr/validation/rules.pyi +55 -0
- tnfr/validation/runtime.py +263 -0
- tnfr/validation/runtime.pyi +31 -0
- tnfr/validation/soft_filters.py +170 -0
- tnfr/validation/soft_filters.pyi +37 -0
- tnfr/validation/spectral.py +159 -0
- tnfr/validation/spectral.pyi +46 -0
- tnfr/validation/syntax.py +40 -0
- tnfr/validation/syntax.pyi +10 -0
- tnfr/validation/window.py +39 -0
- tnfr/validation/window.pyi +1 -0
- tnfr/viz/__init__.py +9 -0
- tnfr/viz/matplotlib.py +246 -0
- tnfr-7.0.0.dist-info/METADATA +179 -0
- tnfr-7.0.0.dist-info/RECORD +185 -0
- {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/licenses/LICENSE.md +1 -1
- tnfr/grammar.py +0 -344
- tnfr/graph_utils.py +0 -84
- tnfr/helpers/__init__.py +0 -71
- tnfr/import_utils.py +0 -228
- tnfr/json_utils.py +0 -162
- tnfr/logging_utils.py +0 -116
- tnfr/presets.py +0 -60
- tnfr/validators.py +0 -84
- tnfr/value_utils.py +0 -59
- tnfr-4.5.2.dist-info/METADATA +0 -379
- tnfr-4.5.2.dist-info/RECORD +0 -67
- {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/WHEEL +0 -0
- {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/entry_points.txt +0 -0
- {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/top_level.txt +0 -0
tnfr/trace.py
CHANGED
|
@@ -7,25 +7,37 @@ structures as immutable snapshots.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
from typing import Any, Callable, Optional, Protocol, NamedTuple, TypedDict, cast
|
|
10
|
+
import warnings
|
|
12
11
|
from collections.abc import Iterable, Mapping
|
|
12
|
+
from types import MappingProxyType
|
|
13
|
+
from typing import Any, Callable, NamedTuple, Protocol, cast
|
|
13
14
|
|
|
14
15
|
from .constants import TRACE
|
|
15
|
-
from .glyph_history import
|
|
16
|
-
from .
|
|
17
|
-
from .
|
|
18
|
-
|
|
16
|
+
from .glyph_history import append_metric, count_glyphs, ensure_history
|
|
17
|
+
from .metrics.sense_index import _normalise_si_sensitivity_mapping
|
|
18
|
+
from .telemetry.verbosity import (
|
|
19
|
+
TELEMETRY_VERBOSITY_DEFAULT,
|
|
20
|
+
TelemetryVerbosity,
|
|
21
|
+
)
|
|
22
|
+
from .types import (
|
|
23
|
+
SigmaVector,
|
|
24
|
+
TNFRGraph,
|
|
25
|
+
TraceCallback,
|
|
26
|
+
TraceFieldFn,
|
|
27
|
+
TraceFieldMap,
|
|
28
|
+
TraceFieldRegistry,
|
|
29
|
+
TraceMetadata,
|
|
30
|
+
TraceSnapshot,
|
|
31
|
+
)
|
|
32
|
+
from .utils import cached_import, get_graph_mapping, is_non_string_sequence
|
|
19
33
|
|
|
20
34
|
|
|
21
35
|
class _KuramotoFn(Protocol):
|
|
22
|
-
def __call__(self, G:
|
|
36
|
+
def __call__(self, G: TNFRGraph) -> tuple[float, float]: ...
|
|
23
37
|
|
|
24
38
|
|
|
25
39
|
class _SigmaVectorFn(Protocol):
|
|
26
|
-
def __call__(
|
|
27
|
-
self, G: Any, weight_mode: str | None = None
|
|
28
|
-
) -> dict[str, float]: ...
|
|
40
|
+
def __call__(self, G: TNFRGraph, weight_mode: str | None = None) -> SigmaVector: ...
|
|
29
41
|
|
|
30
42
|
|
|
31
43
|
class CallbackSpec(NamedTuple):
|
|
@@ -35,30 +47,77 @@ class CallbackSpec(NamedTuple):
|
|
|
35
47
|
func: Callable[..., Any]
|
|
36
48
|
|
|
37
49
|
|
|
38
|
-
class
|
|
39
|
-
"""
|
|
50
|
+
class TraceFieldSpec(NamedTuple):
|
|
51
|
+
"""Declarative specification for a trace field producer."""
|
|
52
|
+
|
|
53
|
+
name: str
|
|
54
|
+
phase: str
|
|
55
|
+
producer: TraceFieldFn
|
|
56
|
+
tiers: tuple[TelemetryVerbosity, ...]
|
|
40
57
|
|
|
41
|
-
gamma: Mapping[str, Any]
|
|
42
|
-
grammar: Mapping[str, Any]
|
|
43
|
-
selector: str | None
|
|
44
|
-
dnfr_weights: Mapping[str, Any]
|
|
45
|
-
si_weights: Mapping[str, Any]
|
|
46
|
-
si_sensitivity: Mapping[str, Any]
|
|
47
|
-
callbacks: Mapping[str, list[str] | None]
|
|
48
|
-
thol_open_nodes: int
|
|
49
|
-
kuramoto: Mapping[str, float]
|
|
50
|
-
sigma: Mapping[str, float]
|
|
51
|
-
glyphs: Mapping[str, int]
|
|
52
58
|
|
|
59
|
+
TRACE_VERBOSITY_DEFAULT = TELEMETRY_VERBOSITY_DEFAULT
|
|
60
|
+
TRACE_VERBOSITY_PRESETS: dict[str, tuple[str, ...]] = {}
|
|
61
|
+
_TRACE_CAPTURE_ALIASES: Mapping[str, str] = MappingProxyType(
|
|
62
|
+
{
|
|
63
|
+
"glyphs": "glyph_counts",
|
|
64
|
+
}
|
|
65
|
+
)
|
|
53
66
|
|
|
54
|
-
class TraceSnapshot(TraceMetadata, total=False):
|
|
55
|
-
"""Trace snapshot stored in the history."""
|
|
56
67
|
|
|
57
|
-
|
|
58
|
-
|
|
68
|
+
def _canonical_capture_name(name: str) -> str:
|
|
69
|
+
"""Return the canonical capture field name for ``name``."""
|
|
70
|
+
|
|
71
|
+
stripped = name.strip()
|
|
72
|
+
alias = _TRACE_CAPTURE_ALIASES.get(stripped)
|
|
73
|
+
if alias is not None:
|
|
74
|
+
return alias
|
|
75
|
+
|
|
76
|
+
lowered = stripped.lower()
|
|
77
|
+
alias = _TRACE_CAPTURE_ALIASES.get(lowered)
|
|
78
|
+
if alias is not None:
|
|
79
|
+
return alias
|
|
80
|
+
|
|
81
|
+
return stripped
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _normalise_capture_spec(raw: Any) -> set[str]:
|
|
85
|
+
"""Coerce custom capture payloads to a ``set`` of field names."""
|
|
59
86
|
|
|
87
|
+
if raw is None:
|
|
88
|
+
return set()
|
|
89
|
+
if isinstance(raw, Mapping):
|
|
90
|
+
return {_canonical_capture_name(str(name)) for name in raw.keys()}
|
|
91
|
+
if isinstance(raw, str):
|
|
92
|
+
return {_canonical_capture_name(raw)}
|
|
93
|
+
if isinstance(raw, Iterable):
|
|
94
|
+
return {_canonical_capture_name(str(name)) for name in raw}
|
|
95
|
+
return {_canonical_capture_name(str(raw))}
|
|
60
96
|
|
|
61
|
-
|
|
97
|
+
|
|
98
|
+
def _resolve_trace_capture(cfg: Mapping[str, Any]) -> set[str]:
|
|
99
|
+
"""Return the capture set declared by ``cfg`` respecting verbosity."""
|
|
100
|
+
|
|
101
|
+
if "capture" in cfg:
|
|
102
|
+
return _normalise_capture_spec(cfg.get("capture"))
|
|
103
|
+
|
|
104
|
+
raw_verbosity = cfg.get("verbosity", TRACE_VERBOSITY_DEFAULT)
|
|
105
|
+
verbosity = str(raw_verbosity).lower()
|
|
106
|
+
fields = TRACE_VERBOSITY_PRESETS.get(verbosity)
|
|
107
|
+
if fields is None:
|
|
108
|
+
warnings.warn(
|
|
109
|
+
(
|
|
110
|
+
"Unknown TRACE verbosity %r; falling back to %s"
|
|
111
|
+
% (raw_verbosity, TRACE_VERBOSITY_DEFAULT)
|
|
112
|
+
),
|
|
113
|
+
UserWarning,
|
|
114
|
+
stacklevel=3,
|
|
115
|
+
)
|
|
116
|
+
fields = TRACE_VERBOSITY_PRESETS[TRACE_VERBOSITY_DEFAULT]
|
|
117
|
+
return set(fields)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _kuramoto_fallback(G: TNFRGraph) -> tuple[float, float]:
|
|
62
121
|
return 0.0, 0.0
|
|
63
122
|
|
|
64
123
|
|
|
@@ -68,9 +127,7 @@ kuramoto_R_psi: _KuramotoFn = cast(
|
|
|
68
127
|
)
|
|
69
128
|
|
|
70
129
|
|
|
71
|
-
def _sigma_fallback(
|
|
72
|
-
G: Any, _weight_mode: str | None = None
|
|
73
|
-
) -> dict[str, float]:
|
|
130
|
+
def _sigma_fallback(G: TNFRGraph, _weight_mode: str | None = None) -> SigmaVector:
|
|
74
131
|
"""Return a null sigma vector regardless of ``_weight_mode``."""
|
|
75
132
|
|
|
76
133
|
return {"x": 0.0, "y": 0.0, "mag": 0.0, "angle": 0.0, "n": 0}
|
|
@@ -79,6 +136,7 @@ def _sigma_fallback(
|
|
|
79
136
|
# Public exports for this module
|
|
80
137
|
__all__ = (
|
|
81
138
|
"CallbackSpec",
|
|
139
|
+
"TraceFieldSpec",
|
|
82
140
|
"TraceMetadata",
|
|
83
141
|
"TraceSnapshot",
|
|
84
142
|
"register_trace",
|
|
@@ -94,24 +152,28 @@ __all__ = (
|
|
|
94
152
|
|
|
95
153
|
|
|
96
154
|
def _trace_setup(
|
|
97
|
-
G,
|
|
155
|
+
G: TNFRGraph,
|
|
98
156
|
) -> tuple[
|
|
99
|
-
|
|
157
|
+
Mapping[str, Any] | None,
|
|
158
|
+
set[str],
|
|
159
|
+
dict[str, Any] | None,
|
|
160
|
+
str | None,
|
|
100
161
|
]:
|
|
101
|
-
"""
|
|
162
|
+
"""Prepare common configuration for trace snapshots.
|
|
102
163
|
|
|
103
164
|
Returns the active configuration, capture set, history and key under
|
|
104
165
|
which metadata will be stored. If tracing is disabled returns
|
|
105
166
|
``(None, set(), None, None)``.
|
|
106
167
|
"""
|
|
107
168
|
|
|
108
|
-
|
|
169
|
+
cfg_raw = G.graph.get("TRACE", TRACE)
|
|
170
|
+
cfg = cfg_raw if isinstance(cfg_raw, Mapping) else TRACE
|
|
109
171
|
if not cfg.get("enabled", True):
|
|
110
172
|
return None, set(), None, None
|
|
111
173
|
|
|
112
|
-
capture
|
|
174
|
+
capture = _resolve_trace_capture(cfg)
|
|
113
175
|
hist = ensure_history(G)
|
|
114
|
-
key = cfg.get("history_key", "trace_meta")
|
|
176
|
+
key = cast(str | None, cfg.get("history_key", "trace_meta"))
|
|
115
177
|
return cfg, capture, hist, key
|
|
116
178
|
|
|
117
179
|
|
|
@@ -122,21 +184,22 @@ def _callback_names(
|
|
|
122
184
|
if isinstance(callbacks, Mapping):
|
|
123
185
|
callbacks = callbacks.values()
|
|
124
186
|
return [
|
|
125
|
-
cb.name
|
|
126
|
-
if cb.name is not None
|
|
127
|
-
else str(getattr(cb.func, "__name__", "fn"))
|
|
187
|
+
cb.name if cb.name is not None else str(getattr(cb.func, "__name__", "fn"))
|
|
128
188
|
for cb in callbacks
|
|
129
189
|
]
|
|
130
190
|
|
|
131
191
|
|
|
132
|
-
|
|
133
|
-
|
|
192
|
+
EMPTY_MAPPING: Mapping[str, Any] = MappingProxyType({})
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def mapping_field(G: TNFRGraph, graph_key: str, out_key: str) -> TraceMetadata:
|
|
196
|
+
"""Copy mappings from ``G.graph`` into trace output."""
|
|
134
197
|
mapping = get_graph_mapping(
|
|
135
|
-
G, graph_key, f"G.graph[{graph_key!r}]
|
|
198
|
+
G, graph_key, f"G.graph[{graph_key!r}] is not a mapping; ignoring"
|
|
136
199
|
)
|
|
137
200
|
if mapping is None:
|
|
138
201
|
return {}
|
|
139
|
-
return
|
|
202
|
+
return {out_key: mapping}
|
|
140
203
|
|
|
141
204
|
|
|
142
205
|
# -------------------------
|
|
@@ -145,10 +208,8 @@ def mapping_field(G: Any, graph_key: str, out_key: str) -> TraceMetadata:
|
|
|
145
208
|
|
|
146
209
|
|
|
147
210
|
def _new_trace_meta(
|
|
148
|
-
G, phase: str
|
|
149
|
-
) ->
|
|
150
|
-
tuple[TraceSnapshot, set[str], Optional[dict[str, Any]], Optional[str]]
|
|
151
|
-
]:
|
|
211
|
+
G: TNFRGraph, phase: str
|
|
212
|
+
) -> tuple[TraceSnapshot, set[str], dict[str, Any] | None, str | None] | None:
|
|
152
213
|
"""Initialise trace metadata for a ``phase``.
|
|
153
214
|
|
|
154
215
|
Wraps :func:`_trace_setup` and creates the base structure with timestamp
|
|
@@ -168,9 +229,7 @@ def _new_trace_meta(
|
|
|
168
229
|
# -------------------------
|
|
169
230
|
|
|
170
231
|
|
|
171
|
-
def _trace_capture(
|
|
172
|
-
G, phase: str, fields: Mapping[str, Callable[[Any], TraceMetadata]]
|
|
173
|
-
) -> None:
|
|
232
|
+
def _trace_capture(G: TNFRGraph, phase: str, fields: TraceFieldMap) -> None:
|
|
174
233
|
"""Capture ``fields`` for ``phase`` and store the snapshot.
|
|
175
234
|
|
|
176
235
|
A :class:`TraceSnapshot` is appended to the configured history when
|
|
@@ -187,7 +246,7 @@ def _trace_capture(
|
|
|
187
246
|
return
|
|
188
247
|
for name, getter in fields.items():
|
|
189
248
|
if name in capture:
|
|
190
|
-
meta.update(
|
|
249
|
+
meta.update(getter(G))
|
|
191
250
|
if hist is None or key is None:
|
|
192
251
|
return
|
|
193
252
|
append_metric(hist, key, meta)
|
|
@@ -198,54 +257,70 @@ def _trace_capture(
|
|
|
198
257
|
# -------------------------
|
|
199
258
|
|
|
200
259
|
|
|
201
|
-
TRACE_FIELDS:
|
|
260
|
+
TRACE_FIELDS: TraceFieldRegistry = {}
|
|
202
261
|
|
|
203
262
|
|
|
204
|
-
def register_trace_field(
|
|
205
|
-
phase: str, name: str, func: Callable[[Any], TraceMetadata]
|
|
206
|
-
) -> None:
|
|
263
|
+
def register_trace_field(phase: str, name: str, func: TraceFieldFn) -> None:
|
|
207
264
|
"""Register ``func`` to populate trace field ``name`` during ``phase``."""
|
|
208
265
|
|
|
209
266
|
TRACE_FIELDS.setdefault(phase, {})[name] = func
|
|
210
267
|
|
|
211
268
|
|
|
212
|
-
gamma_field
|
|
269
|
+
def gamma_field(G: TNFRGraph) -> TraceMetadata:
|
|
270
|
+
"""Expose γ-field metadata stored under ``G.graph['GAMMA']``."""
|
|
213
271
|
|
|
272
|
+
return mapping_field(G, "GAMMA", "gamma")
|
|
214
273
|
|
|
215
|
-
grammar_field = partial(mapping_field, graph_key="GRAMMAR_CANON", out_key="grammar")
|
|
216
274
|
|
|
275
|
+
def grammar_field(G: TNFRGraph) -> TraceMetadata:
|
|
276
|
+
"""Expose canonical grammar metadata for trace emission."""
|
|
217
277
|
|
|
218
|
-
|
|
219
|
-
mapping_field, graph_key="DNFR_WEIGHTS", out_key="dnfr_weights"
|
|
220
|
-
)
|
|
278
|
+
return mapping_field(G, "GRAMMAR_CANON", "grammar")
|
|
221
279
|
|
|
222
280
|
|
|
223
|
-
def
|
|
281
|
+
def dnfr_weights_field(G: TNFRGraph) -> TraceMetadata:
|
|
282
|
+
return mapping_field(G, "DNFR_WEIGHTS", "dnfr_weights")
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def selector_field(G: TNFRGraph) -> TraceMetadata:
|
|
224
286
|
sel = G.graph.get("glyph_selector")
|
|
225
|
-
|
|
287
|
+
selector_name = getattr(sel, "__name__", str(sel)) if sel else None
|
|
288
|
+
return {"selector": selector_name}
|
|
226
289
|
|
|
227
290
|
|
|
228
|
-
_si_weights_field
|
|
291
|
+
def _si_weights_field(G: TNFRGraph) -> TraceMetadata:
|
|
292
|
+
weights = mapping_field(G, "_Si_weights", "si_weights")
|
|
293
|
+
if weights:
|
|
294
|
+
return weights
|
|
295
|
+
return {"si_weights": EMPTY_MAPPING}
|
|
229
296
|
|
|
230
297
|
|
|
231
|
-
_si_sensitivity_field
|
|
232
|
-
|
|
233
|
-
|
|
298
|
+
def _si_sensitivity_field(G: TNFRGraph) -> TraceMetadata:
|
|
299
|
+
mapping = get_graph_mapping(
|
|
300
|
+
G,
|
|
301
|
+
"_Si_sensitivity",
|
|
302
|
+
"G.graph['_Si_sensitivity'] is not a mapping; ignoring",
|
|
303
|
+
)
|
|
304
|
+
if mapping is None:
|
|
305
|
+
return {"si_sensitivity": EMPTY_MAPPING}
|
|
306
|
+
|
|
307
|
+
normalised = _normalise_si_sensitivity_mapping(mapping, warn=True)
|
|
308
|
+
|
|
309
|
+
if normalised != mapping:
|
|
310
|
+
G.graph["_Si_sensitivity"] = normalised
|
|
234
311
|
|
|
312
|
+
return {"si_sensitivity": MappingProxyType(normalised)}
|
|
235
313
|
|
|
236
|
-
|
|
314
|
+
|
|
315
|
+
def si_weights_field(G: TNFRGraph) -> TraceMetadata:
|
|
237
316
|
"""Return sense-plane weights and sensitivity."""
|
|
238
317
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
**(_si_weights_field(G) or {"si_weights": {}}),
|
|
243
|
-
**(_si_sensitivity_field(G) or {"si_sensitivity": {}}),
|
|
244
|
-
},
|
|
245
|
-
)
|
|
318
|
+
weights = _si_weights_field(G)
|
|
319
|
+
sensitivity = _si_sensitivity_field(G)
|
|
320
|
+
return {**weights, **sensitivity}
|
|
246
321
|
|
|
247
322
|
|
|
248
|
-
def callbacks_field(G:
|
|
323
|
+
def callbacks_field(G: TNFRGraph) -> TraceMetadata:
|
|
249
324
|
cb = G.graph.get("callbacks")
|
|
250
325
|
if not isinstance(cb, Mapping):
|
|
251
326
|
return {}
|
|
@@ -255,24 +330,24 @@ def callbacks_field(G: Any) -> TraceMetadata:
|
|
|
255
330
|
out[phase] = _callback_names(cb_map)
|
|
256
331
|
else:
|
|
257
332
|
out[phase] = None
|
|
258
|
-
return
|
|
333
|
+
return {"callbacks": out}
|
|
259
334
|
|
|
260
335
|
|
|
261
|
-
def thol_state_field(G:
|
|
336
|
+
def thol_state_field(G: TNFRGraph) -> TraceMetadata:
|
|
262
337
|
th_open = 0
|
|
263
338
|
for _, nd in G.nodes(data=True):
|
|
264
339
|
st = nd.get("_GRAM", {})
|
|
265
340
|
if st.get("thol_open", False):
|
|
266
341
|
th_open += 1
|
|
267
|
-
return
|
|
342
|
+
return {"thol_open_nodes": th_open}
|
|
268
343
|
|
|
269
344
|
|
|
270
|
-
def kuramoto_field(G:
|
|
345
|
+
def kuramoto_field(G: TNFRGraph) -> TraceMetadata:
|
|
271
346
|
R, psi = kuramoto_R_psi(G)
|
|
272
|
-
return
|
|
347
|
+
return {"kuramoto": {"R": float(R), "psi": float(psi)}}
|
|
273
348
|
|
|
274
349
|
|
|
275
|
-
def sigma_field(G:
|
|
350
|
+
def sigma_field(G: TNFRGraph) -> TraceMetadata:
|
|
276
351
|
sigma_vector_from_graph: _SigmaVectorFn = cast(
|
|
277
352
|
_SigmaVectorFn,
|
|
278
353
|
cached_import(
|
|
@@ -282,20 +357,17 @@ def sigma_field(G: Any) -> TraceMetadata:
|
|
|
282
357
|
),
|
|
283
358
|
)
|
|
284
359
|
sv = sigma_vector_from_graph(G)
|
|
285
|
-
return
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
"
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
},
|
|
295
|
-
)
|
|
360
|
+
return {
|
|
361
|
+
"sigma": {
|
|
362
|
+
"x": float(sv.get("x", 0.0)),
|
|
363
|
+
"y": float(sv.get("y", 0.0)),
|
|
364
|
+
"mag": float(sv.get("mag", 0.0)),
|
|
365
|
+
"angle": float(sv.get("angle", 0.0)),
|
|
366
|
+
}
|
|
367
|
+
}
|
|
296
368
|
|
|
297
369
|
|
|
298
|
-
def glyph_counts_field(G:
|
|
370
|
+
def glyph_counts_field(G: TNFRGraph) -> TraceMetadata:
|
|
299
371
|
"""Return glyph count snapshot.
|
|
300
372
|
|
|
301
373
|
``count_glyphs`` already produces a fresh mapping so no additional copy
|
|
@@ -303,21 +375,107 @@ def glyph_counts_field(G: Any) -> TraceMetadata:
|
|
|
303
375
|
"""
|
|
304
376
|
|
|
305
377
|
cnt = count_glyphs(G, window=1)
|
|
306
|
-
return
|
|
307
|
-
|
|
378
|
+
return {"glyphs": cnt}
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
TRACE_FIELD_SPECS: tuple[TraceFieldSpec, ...] = (
|
|
382
|
+
TraceFieldSpec(
|
|
383
|
+
name="gamma",
|
|
384
|
+
phase="before",
|
|
385
|
+
producer=gamma_field,
|
|
386
|
+
tiers=(
|
|
387
|
+
TelemetryVerbosity.BASIC,
|
|
388
|
+
TelemetryVerbosity.DETAILED,
|
|
389
|
+
TelemetryVerbosity.DEBUG,
|
|
390
|
+
),
|
|
391
|
+
),
|
|
392
|
+
TraceFieldSpec(
|
|
393
|
+
name="grammar",
|
|
394
|
+
phase="before",
|
|
395
|
+
producer=grammar_field,
|
|
396
|
+
tiers=(
|
|
397
|
+
TelemetryVerbosity.BASIC,
|
|
398
|
+
TelemetryVerbosity.DETAILED,
|
|
399
|
+
TelemetryVerbosity.DEBUG,
|
|
400
|
+
),
|
|
401
|
+
),
|
|
402
|
+
TraceFieldSpec(
|
|
403
|
+
name="selector",
|
|
404
|
+
phase="before",
|
|
405
|
+
producer=selector_field,
|
|
406
|
+
tiers=(
|
|
407
|
+
TelemetryVerbosity.BASIC,
|
|
408
|
+
TelemetryVerbosity.DETAILED,
|
|
409
|
+
TelemetryVerbosity.DEBUG,
|
|
410
|
+
),
|
|
411
|
+
),
|
|
412
|
+
TraceFieldSpec(
|
|
413
|
+
name="dnfr_weights",
|
|
414
|
+
phase="before",
|
|
415
|
+
producer=dnfr_weights_field,
|
|
416
|
+
tiers=(
|
|
417
|
+
TelemetryVerbosity.BASIC,
|
|
418
|
+
TelemetryVerbosity.DETAILED,
|
|
419
|
+
TelemetryVerbosity.DEBUG,
|
|
420
|
+
),
|
|
421
|
+
),
|
|
422
|
+
TraceFieldSpec(
|
|
423
|
+
name="si_weights",
|
|
424
|
+
phase="before",
|
|
425
|
+
producer=si_weights_field,
|
|
426
|
+
tiers=(
|
|
427
|
+
TelemetryVerbosity.BASIC,
|
|
428
|
+
TelemetryVerbosity.DETAILED,
|
|
429
|
+
TelemetryVerbosity.DEBUG,
|
|
430
|
+
),
|
|
431
|
+
),
|
|
432
|
+
TraceFieldSpec(
|
|
433
|
+
name="callbacks",
|
|
434
|
+
phase="before",
|
|
435
|
+
producer=callbacks_field,
|
|
436
|
+
tiers=(
|
|
437
|
+
TelemetryVerbosity.BASIC,
|
|
438
|
+
TelemetryVerbosity.DETAILED,
|
|
439
|
+
TelemetryVerbosity.DEBUG,
|
|
440
|
+
),
|
|
441
|
+
),
|
|
442
|
+
TraceFieldSpec(
|
|
443
|
+
name="thol_open_nodes",
|
|
444
|
+
phase="before",
|
|
445
|
+
producer=thol_state_field,
|
|
446
|
+
tiers=(
|
|
447
|
+
TelemetryVerbosity.BASIC,
|
|
448
|
+
TelemetryVerbosity.DETAILED,
|
|
449
|
+
TelemetryVerbosity.DEBUG,
|
|
450
|
+
),
|
|
451
|
+
),
|
|
452
|
+
TraceFieldSpec(
|
|
453
|
+
name="kuramoto",
|
|
454
|
+
phase="after",
|
|
455
|
+
producer=kuramoto_field,
|
|
456
|
+
tiers=(TelemetryVerbosity.DETAILED, TelemetryVerbosity.DEBUG),
|
|
457
|
+
),
|
|
458
|
+
TraceFieldSpec(
|
|
459
|
+
name="sigma",
|
|
460
|
+
phase="after",
|
|
461
|
+
producer=sigma_field,
|
|
462
|
+
tiers=(TelemetryVerbosity.DETAILED, TelemetryVerbosity.DEBUG),
|
|
463
|
+
),
|
|
464
|
+
TraceFieldSpec(
|
|
465
|
+
name="glyph_counts",
|
|
466
|
+
phase="after",
|
|
467
|
+
producer=glyph_counts_field,
|
|
468
|
+
tiers=(TelemetryVerbosity.DEBUG,),
|
|
469
|
+
),
|
|
470
|
+
)
|
|
308
471
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
register_trace_field("before", "dnfr_weights", dnfr_weights_field)
|
|
314
|
-
register_trace_field("before", "si_weights", si_weights_field)
|
|
315
|
-
register_trace_field("before", "callbacks", callbacks_field)
|
|
316
|
-
register_trace_field("before", "thol_open_nodes", thol_state_field)
|
|
472
|
+
TRACE_VERBOSITY_PRESETS = {
|
|
473
|
+
level.value: tuple(spec.name for spec in TRACE_FIELD_SPECS if level in spec.tiers)
|
|
474
|
+
for level in TelemetryVerbosity
|
|
475
|
+
}
|
|
317
476
|
|
|
318
|
-
|
|
319
|
-
register_trace_field(
|
|
320
|
-
register_trace_field("after", "glyph_counts", glyph_counts_field)
|
|
477
|
+
for spec in TRACE_FIELD_SPECS:
|
|
478
|
+
register_trace_field(spec.phase, spec.name, spec.producer)
|
|
321
479
|
|
|
322
480
|
|
|
323
481
|
# -------------------------
|
|
@@ -325,9 +483,8 @@ register_trace_field("after", "glyph_counts", glyph_counts_field)
|
|
|
325
483
|
# -------------------------
|
|
326
484
|
|
|
327
485
|
|
|
328
|
-
def register_trace(G) -> None:
|
|
329
|
-
"""Enable before/after-step snapshots and dump operational metadata
|
|
330
|
-
to history.
|
|
486
|
+
def register_trace(G: TNFRGraph) -> None:
|
|
487
|
+
"""Enable before/after-step snapshots and dump operational metadata to history.
|
|
331
488
|
|
|
332
489
|
Trace snapshots are stored as :class:`TraceSnapshot` entries in
|
|
333
490
|
``G.graph['history'][TRACE.history_key]`` with:
|
|
@@ -354,11 +511,11 @@ def register_trace(G) -> None:
|
|
|
354
511
|
for phase in TRACE_FIELDS.keys():
|
|
355
512
|
event = f"{phase}_step"
|
|
356
513
|
|
|
357
|
-
def _make_cb(ph):
|
|
358
|
-
def _cb(
|
|
514
|
+
def _make_cb(ph: str) -> TraceCallback:
|
|
515
|
+
def _cb(graph: TNFRGraph, ctx: dict[str, Any]) -> None:
|
|
359
516
|
del ctx
|
|
360
517
|
|
|
361
|
-
_trace_capture(
|
|
518
|
+
_trace_capture(graph, ph, TRACE_FIELDS.get(ph, {}))
|
|
362
519
|
|
|
363
520
|
return _cb
|
|
364
521
|
|
tnfr/trace.pyi
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from collections.abc import Iterable, Mapping
|
|
2
|
+
from typing import Any, Callable, NamedTuple
|
|
3
|
+
|
|
4
|
+
from .types import (
|
|
5
|
+
TNFRGraph,
|
|
6
|
+
TraceFieldFn,
|
|
7
|
+
TraceFieldMap,
|
|
8
|
+
TraceFieldRegistry,
|
|
9
|
+
TraceMetadata,
|
|
10
|
+
TraceSnapshot,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__: tuple[str, ...]
|
|
14
|
+
|
|
15
|
+
def __getattr__(name: str) -> Any: ...
|
|
16
|
+
|
|
17
|
+
class CallbackSpec(NamedTuple):
|
|
18
|
+
name: str | None
|
|
19
|
+
func: Callable[..., Any]
|
|
20
|
+
|
|
21
|
+
kuramoto_R_psi: Callable[[TNFRGraph], tuple[float, float]]
|
|
22
|
+
TRACE_FIELDS: TraceFieldRegistry
|
|
23
|
+
|
|
24
|
+
def _callback_names(
|
|
25
|
+
callbacks: Mapping[str, CallbackSpec] | Iterable[CallbackSpec],
|
|
26
|
+
) -> list[str]: ...
|
|
27
|
+
def mapping_field(G: TNFRGraph, graph_key: str, out_key: str) -> TraceMetadata: ...
|
|
28
|
+
def _trace_capture(G: TNFRGraph, phase: str, fields: TraceFieldMap) -> None: ...
|
|
29
|
+
def register_trace_field(phase: str, name: str, func: TraceFieldFn) -> None: ...
|
|
30
|
+
def gamma_field(G: TNFRGraph) -> TraceMetadata: ...
|
|
31
|
+
def grammar_field(G: TNFRGraph) -> TraceMetadata: ...
|
|
32
|
+
def dnfr_weights_field(G: TNFRGraph) -> TraceMetadata: ...
|
|
33
|
+
def selector_field(G: TNFRGraph) -> TraceMetadata: ...
|
|
34
|
+
def si_weights_field(G: TNFRGraph) -> TraceMetadata: ...
|
|
35
|
+
def callbacks_field(G: TNFRGraph) -> TraceMetadata: ...
|
|
36
|
+
def thol_state_field(G: TNFRGraph) -> TraceMetadata: ...
|
|
37
|
+
def kuramoto_field(G: TNFRGraph) -> TraceMetadata: ...
|
|
38
|
+
def sigma_field(G: TNFRGraph) -> TraceMetadata: ...
|
|
39
|
+
def glyph_counts_field(G: TNFRGraph) -> TraceMetadata: ...
|
|
40
|
+
def register_trace(G: TNFRGraph) -> None: ...
|