tnfr 4.5.1__py3-none-any.whl → 6.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.
Files changed (170) hide show
  1. tnfr/__init__.py +270 -90
  2. tnfr/__init__.pyi +40 -0
  3. tnfr/_compat.py +11 -0
  4. tnfr/_version.py +7 -0
  5. tnfr/_version.pyi +7 -0
  6. tnfr/alias.py +631 -0
  7. tnfr/alias.pyi +140 -0
  8. tnfr/cache.py +732 -0
  9. tnfr/cache.pyi +232 -0
  10. tnfr/callback_utils.py +381 -0
  11. tnfr/callback_utils.pyi +105 -0
  12. tnfr/cli/__init__.py +89 -0
  13. tnfr/cli/__init__.pyi +47 -0
  14. tnfr/cli/arguments.py +199 -0
  15. tnfr/cli/arguments.pyi +33 -0
  16. tnfr/cli/execution.py +322 -0
  17. tnfr/cli/execution.pyi +80 -0
  18. tnfr/cli/utils.py +34 -0
  19. tnfr/cli/utils.pyi +8 -0
  20. tnfr/config/__init__.py +12 -0
  21. tnfr/config/__init__.pyi +8 -0
  22. tnfr/config/constants.py +104 -0
  23. tnfr/config/constants.pyi +12 -0
  24. tnfr/config/init.py +36 -0
  25. tnfr/config/init.pyi +8 -0
  26. tnfr/config/operator_names.py +106 -0
  27. tnfr/config/operator_names.pyi +28 -0
  28. tnfr/config/presets.py +104 -0
  29. tnfr/config/presets.pyi +7 -0
  30. tnfr/constants/__init__.py +228 -0
  31. tnfr/constants/__init__.pyi +104 -0
  32. tnfr/constants/core.py +158 -0
  33. tnfr/constants/core.pyi +17 -0
  34. tnfr/constants/init.py +31 -0
  35. tnfr/constants/init.pyi +12 -0
  36. tnfr/constants/metric.py +102 -0
  37. tnfr/constants/metric.pyi +19 -0
  38. tnfr/constants_glyphs.py +16 -0
  39. tnfr/constants_glyphs.pyi +12 -0
  40. tnfr/dynamics/__init__.py +136 -0
  41. tnfr/dynamics/__init__.pyi +83 -0
  42. tnfr/dynamics/adaptation.py +201 -0
  43. tnfr/dynamics/aliases.py +22 -0
  44. tnfr/dynamics/coordination.py +343 -0
  45. tnfr/dynamics/dnfr.py +2315 -0
  46. tnfr/dynamics/dnfr.pyi +33 -0
  47. tnfr/dynamics/integrators.py +561 -0
  48. tnfr/dynamics/integrators.pyi +35 -0
  49. tnfr/dynamics/runtime.py +521 -0
  50. tnfr/dynamics/sampling.py +34 -0
  51. tnfr/dynamics/sampling.pyi +7 -0
  52. tnfr/dynamics/selectors.py +680 -0
  53. tnfr/execution.py +216 -0
  54. tnfr/execution.pyi +65 -0
  55. tnfr/flatten.py +283 -0
  56. tnfr/flatten.pyi +28 -0
  57. tnfr/gamma.py +320 -89
  58. tnfr/gamma.pyi +40 -0
  59. tnfr/glyph_history.py +337 -0
  60. tnfr/glyph_history.pyi +53 -0
  61. tnfr/grammar.py +23 -153
  62. tnfr/grammar.pyi +13 -0
  63. tnfr/helpers/__init__.py +151 -0
  64. tnfr/helpers/__init__.pyi +66 -0
  65. tnfr/helpers/numeric.py +88 -0
  66. tnfr/helpers/numeric.pyi +12 -0
  67. tnfr/immutable.py +214 -0
  68. tnfr/immutable.pyi +37 -0
  69. tnfr/initialization.py +199 -0
  70. tnfr/initialization.pyi +73 -0
  71. tnfr/io.py +311 -0
  72. tnfr/io.pyi +11 -0
  73. tnfr/locking.py +37 -0
  74. tnfr/locking.pyi +7 -0
  75. tnfr/metrics/__init__.py +41 -0
  76. tnfr/metrics/__init__.pyi +20 -0
  77. tnfr/metrics/coherence.py +1469 -0
  78. tnfr/metrics/common.py +149 -0
  79. tnfr/metrics/common.pyi +15 -0
  80. tnfr/metrics/core.py +259 -0
  81. tnfr/metrics/core.pyi +13 -0
  82. tnfr/metrics/diagnosis.py +840 -0
  83. tnfr/metrics/diagnosis.pyi +89 -0
  84. tnfr/metrics/export.py +151 -0
  85. tnfr/metrics/glyph_timing.py +369 -0
  86. tnfr/metrics/reporting.py +152 -0
  87. tnfr/metrics/reporting.pyi +12 -0
  88. tnfr/metrics/sense_index.py +294 -0
  89. tnfr/metrics/sense_index.pyi +9 -0
  90. tnfr/metrics/trig.py +216 -0
  91. tnfr/metrics/trig.pyi +12 -0
  92. tnfr/metrics/trig_cache.py +105 -0
  93. tnfr/metrics/trig_cache.pyi +10 -0
  94. tnfr/node.py +255 -177
  95. tnfr/node.pyi +161 -0
  96. tnfr/observers.py +154 -150
  97. tnfr/observers.pyi +46 -0
  98. tnfr/ontosim.py +135 -134
  99. tnfr/ontosim.pyi +33 -0
  100. tnfr/operators/__init__.py +452 -0
  101. tnfr/operators/__init__.pyi +31 -0
  102. tnfr/operators/definitions.py +181 -0
  103. tnfr/operators/definitions.pyi +92 -0
  104. tnfr/operators/jitter.py +266 -0
  105. tnfr/operators/jitter.pyi +11 -0
  106. tnfr/operators/registry.py +80 -0
  107. tnfr/operators/registry.pyi +15 -0
  108. tnfr/operators/remesh.py +569 -0
  109. tnfr/presets.py +10 -23
  110. tnfr/presets.pyi +7 -0
  111. tnfr/py.typed +0 -0
  112. tnfr/rng.py +440 -0
  113. tnfr/rng.pyi +14 -0
  114. tnfr/selector.py +217 -0
  115. tnfr/selector.pyi +19 -0
  116. tnfr/sense.py +307 -142
  117. tnfr/sense.pyi +30 -0
  118. tnfr/structural.py +69 -164
  119. tnfr/structural.pyi +46 -0
  120. tnfr/telemetry/__init__.py +13 -0
  121. tnfr/telemetry/verbosity.py +37 -0
  122. tnfr/tokens.py +61 -0
  123. tnfr/tokens.pyi +41 -0
  124. tnfr/trace.py +520 -95
  125. tnfr/trace.pyi +68 -0
  126. tnfr/types.py +382 -17
  127. tnfr/types.pyi +145 -0
  128. tnfr/utils/__init__.py +158 -0
  129. tnfr/utils/__init__.pyi +133 -0
  130. tnfr/utils/cache.py +755 -0
  131. tnfr/utils/cache.pyi +156 -0
  132. tnfr/utils/data.py +267 -0
  133. tnfr/utils/data.pyi +73 -0
  134. tnfr/utils/graph.py +87 -0
  135. tnfr/utils/graph.pyi +10 -0
  136. tnfr/utils/init.py +746 -0
  137. tnfr/utils/init.pyi +85 -0
  138. tnfr/utils/io.py +157 -0
  139. tnfr/utils/io.pyi +10 -0
  140. tnfr/utils/validators.py +130 -0
  141. tnfr/utils/validators.pyi +19 -0
  142. tnfr/validation/__init__.py +25 -0
  143. tnfr/validation/__init__.pyi +17 -0
  144. tnfr/validation/compatibility.py +59 -0
  145. tnfr/validation/compatibility.pyi +8 -0
  146. tnfr/validation/grammar.py +149 -0
  147. tnfr/validation/grammar.pyi +11 -0
  148. tnfr/validation/rules.py +194 -0
  149. tnfr/validation/rules.pyi +18 -0
  150. tnfr/validation/syntax.py +151 -0
  151. tnfr/validation/syntax.pyi +7 -0
  152. tnfr-6.0.0.dist-info/METADATA +135 -0
  153. tnfr-6.0.0.dist-info/RECORD +157 -0
  154. tnfr/cli.py +0 -322
  155. tnfr/config.py +0 -41
  156. tnfr/constants.py +0 -277
  157. tnfr/dynamics.py +0 -814
  158. tnfr/helpers.py +0 -264
  159. tnfr/main.py +0 -47
  160. tnfr/metrics.py +0 -597
  161. tnfr/operators.py +0 -525
  162. tnfr/program.py +0 -176
  163. tnfr/scenarios.py +0 -34
  164. tnfr/validators.py +0 -38
  165. tnfr-4.5.1.dist-info/METADATA +0 -221
  166. tnfr-4.5.1.dist-info/RECORD +0 -28
  167. {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/WHEEL +0 -0
  168. {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/entry_points.txt +0 -0
  169. {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/licenses/LICENSE.md +0 -0
  170. {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/top_level.txt +0 -0
tnfr/metrics/common.py ADDED
@@ -0,0 +1,149 @@
1
+ """Shared helpers for TNFR metrics."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from types import MappingProxyType
6
+ from typing import Any, Iterable, Mapping, Sequence
7
+
8
+ from ..alias import collect_attr, get_attr, multi_recompute_abs_max
9
+ from ..constants import DEFAULTS, get_aliases
10
+ from ..helpers.numeric import clamp01, kahan_sum_nd
11
+ from ..types import GraphLike
12
+ from ..utils import edge_version_cache, get_numpy, normalize_weights
13
+
14
+ ALIAS_DNFR = get_aliases("DNFR")
15
+ ALIAS_D2EPI = get_aliases("D2EPI")
16
+ ALIAS_DEPI = get_aliases("DEPI")
17
+ ALIAS_VF = get_aliases("VF")
18
+
19
+ __all__ = (
20
+ "GraphLike",
21
+ "compute_coherence",
22
+ "compute_dnfr_accel_max",
23
+ "normalize_dnfr",
24
+ "ensure_neighbors_map",
25
+ "merge_graph_weights",
26
+ "merge_and_normalize_weights",
27
+ "min_max_range",
28
+ "_get_vf_dnfr_max",
29
+ )
30
+
31
+
32
+ def compute_coherence(
33
+ G: GraphLike, *, return_means: bool = False
34
+ ) -> float | tuple[float, float, float]:
35
+ """Compute global coherence ``C`` from ``ΔNFR`` and ``dEPI``."""
36
+
37
+ count = G.number_of_nodes()
38
+ if count == 0:
39
+ return (0.0, 0.0, 0.0) if return_means else 0.0
40
+
41
+ nodes = G.nodes
42
+ np = get_numpy()
43
+ dnfr_values = collect_attr(G, nodes, ALIAS_DNFR, 0.0, np=np)
44
+ depi_values = collect_attr(G, nodes, ALIAS_DEPI, 0.0, np=np)
45
+
46
+ if np is not None:
47
+ dnfr_mean = float(np.mean(np.abs(dnfr_values)))
48
+ depi_mean = float(np.mean(np.abs(depi_values)))
49
+ else:
50
+ dnfr_sum, depi_sum = kahan_sum_nd(
51
+ ((abs(d), abs(e)) for d, e in zip(dnfr_values, depi_values)),
52
+ dims=2,
53
+ )
54
+ dnfr_mean = dnfr_sum / count
55
+ depi_mean = depi_sum / count
56
+
57
+ coherence = 1.0 / (1.0 + dnfr_mean + depi_mean)
58
+ return (coherence, dnfr_mean, depi_mean) if return_means else coherence
59
+
60
+
61
+ def ensure_neighbors_map(G: GraphLike) -> Mapping[Any, Sequence[Any]]:
62
+ """Return cached neighbors list keyed by node as a read-only mapping."""
63
+
64
+ def builder() -> Mapping[Any, Sequence[Any]]:
65
+ return MappingProxyType({n: tuple(G.neighbors(n)) for n in G})
66
+
67
+ return edge_version_cache(G, "_neighbors", builder)
68
+
69
+
70
+ def merge_graph_weights(G: GraphLike, key: str) -> dict[str, float]:
71
+ """Merge default weights for ``key`` with any graph overrides."""
72
+
73
+ return {**DEFAULTS[key], **G.graph.get(key, {})}
74
+
75
+
76
+ def merge_and_normalize_weights(
77
+ G: GraphLike,
78
+ key: str,
79
+ fields: Sequence[str],
80
+ *,
81
+ default: float = 0.0,
82
+ ) -> dict[str, float]:
83
+ """Merge defaults for ``key`` and normalise ``fields``."""
84
+
85
+ w = merge_graph_weights(G, key)
86
+ return normalize_weights(
87
+ w,
88
+ fields,
89
+ default=default,
90
+ error_on_conversion=False,
91
+ error_on_negative=False,
92
+ warn_once=True,
93
+ )
94
+
95
+
96
+ def compute_dnfr_accel_max(G: GraphLike) -> dict[str, float]:
97
+ """Compute absolute maxima of |ΔNFR| and |d²EPI/dt²|."""
98
+
99
+ return multi_recompute_abs_max(
100
+ G, {"dnfr_max": ALIAS_DNFR, "accel_max": ALIAS_D2EPI}
101
+ )
102
+
103
+
104
+ def normalize_dnfr(nd: Mapping[str, Any], max_val: float) -> float:
105
+ """Normalise ``|ΔNFR|`` using ``max_val``."""
106
+
107
+ if max_val <= 0:
108
+ return 0.0
109
+ val = abs(get_attr(nd, ALIAS_DNFR, 0.0))
110
+ return clamp01(val / max_val)
111
+
112
+
113
+ def min_max_range(
114
+ values: Iterable[float], *, default: tuple[float, float] = (0.0, 0.0)
115
+ ) -> tuple[float, float]:
116
+ """Return the minimum and maximum values observed in ``values``."""
117
+
118
+ it = iter(values)
119
+ try:
120
+ first = next(it)
121
+ except StopIteration:
122
+ return default
123
+ min_val = max_val = first
124
+ for val in it:
125
+ if val < min_val:
126
+ min_val = val
127
+ elif val > max_val:
128
+ max_val = val
129
+ return min_val, max_val
130
+
131
+
132
+ def _get_vf_dnfr_max(G: GraphLike) -> tuple[float, float]:
133
+ """Ensure and return absolute maxima for ``νf`` and ``ΔNFR``."""
134
+
135
+ vfmax = G.graph.get("_vfmax")
136
+ dnfrmax = G.graph.get("_dnfrmax")
137
+ if vfmax is None or dnfrmax is None:
138
+ maxes = multi_recompute_abs_max(
139
+ G, {"_vfmax": ALIAS_VF, "_dnfrmax": ALIAS_DNFR}
140
+ )
141
+ if vfmax is None:
142
+ vfmax = maxes["_vfmax"]
143
+ if dnfrmax is None:
144
+ dnfrmax = maxes["_dnfrmax"]
145
+ G.graph["_vfmax"] = vfmax
146
+ G.graph["_dnfrmax"] = dnfrmax
147
+ vfmax = 1.0 if vfmax == 0 else vfmax
148
+ dnfrmax = 1.0 if dnfrmax == 0 else dnfrmax
149
+ return float(vfmax), float(dnfrmax)
@@ -0,0 +1,15 @@
1
+ from typing import Any
2
+
3
+ __all__: Any
4
+
5
+ def __getattr__(name: str) -> Any: ...
6
+
7
+ GraphLike: Any
8
+ _get_vf_dnfr_max: Any
9
+ compute_coherence: Any
10
+ compute_dnfr_accel_max: Any
11
+ ensure_neighbors_map: Any
12
+ merge_and_normalize_weights: Any
13
+ merge_graph_weights: Any
14
+ min_max_range: Any
15
+ normalize_dnfr: Any
tnfr/metrics/core.py ADDED
@@ -0,0 +1,259 @@
1
+ """Basic metrics orchestrator."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Mapping
6
+ from typing import Any, NamedTuple, cast
7
+
8
+ from ..types import (
9
+ GlyphSelector,
10
+ NodeId,
11
+ SelectorPreselectionChoices,
12
+ SelectorPreselectionMetrics,
13
+ SelectorPreselectionPayload,
14
+ TNFRGraph,
15
+ TraceCallback,
16
+ TraceFieldFn,
17
+ TraceFieldMap,
18
+ TraceFieldRegistry,
19
+ )
20
+
21
+ from ..callback_utils import CallbackEvent, callback_manager
22
+ from ..constants import get_param
23
+ from ..glyph_history import append_metric, ensure_history
24
+ from ..utils import get_logger
25
+ from ..telemetry.verbosity import (
26
+ TelemetryVerbosity,
27
+ TELEMETRY_VERBOSITY_DEFAULT,
28
+ TELEMETRY_VERBOSITY_LEVELS,
29
+ )
30
+ from .coherence import (
31
+ _aggregate_si,
32
+ _track_stability,
33
+ _update_coherence,
34
+ _update_phase_sync,
35
+ _update_sigma,
36
+ register_coherence_callbacks,
37
+ GLYPH_LOAD_STABILIZERS_KEY,
38
+ )
39
+ from .diagnosis import register_diagnosis_callbacks
40
+ from .glyph_timing import _compute_advanced_metrics, GlyphMetricsHistory
41
+ from .reporting import (
42
+ Tg_by_node,
43
+ Tg_global,
44
+ glyphogram_series,
45
+ glyph_top,
46
+ latency_series,
47
+ )
48
+
49
+ logger = get_logger(__name__)
50
+
51
+ __all__ = [
52
+ "TNFRGraph",
53
+ "NodeId",
54
+ "GlyphSelector",
55
+ "SelectorPreselectionMetrics",
56
+ "SelectorPreselectionChoices",
57
+ "SelectorPreselectionPayload",
58
+ "TraceCallback",
59
+ "TraceFieldFn",
60
+ "TraceFieldMap",
61
+ "TraceFieldRegistry",
62
+ "_metrics_step",
63
+ "register_metrics_callbacks",
64
+ "Tg_global",
65
+ "Tg_by_node",
66
+ "latency_series",
67
+ "glyphogram_series",
68
+ "glyph_top",
69
+ ]
70
+
71
+
72
+ class MetricsVerbositySpec(NamedTuple):
73
+ """Runtime configuration for metrics verbosity tiers."""
74
+
75
+ name: str
76
+ enable_phase_sync: bool
77
+ enable_sigma: bool
78
+ enable_aggregate_si: bool
79
+ enable_advanced: bool
80
+ attach_coherence_hooks: bool
81
+ attach_diagnosis_hooks: bool
82
+
83
+
84
+ METRICS_VERBOSITY_DEFAULT = TELEMETRY_VERBOSITY_DEFAULT
85
+
86
+ _METRICS_VERBOSITY_PRESETS: dict[str, MetricsVerbositySpec] = {}
87
+
88
+
89
+ def _register_metrics_preset(spec: MetricsVerbositySpec) -> None:
90
+ if spec.name not in TELEMETRY_VERBOSITY_LEVELS:
91
+ raise ValueError(
92
+ "Unknown metrics verbosity '%s'; use %s" % (
93
+ spec.name,
94
+ ", ".join(TELEMETRY_VERBOSITY_LEVELS),
95
+ )
96
+ )
97
+ _METRICS_VERBOSITY_PRESETS[spec.name] = spec
98
+
99
+
100
+ _register_metrics_preset(
101
+ MetricsVerbositySpec(
102
+ name=TelemetryVerbosity.BASIC.value,
103
+ enable_phase_sync=False,
104
+ enable_sigma=False,
105
+ enable_aggregate_si=False,
106
+ enable_advanced=False,
107
+ attach_coherence_hooks=False,
108
+ attach_diagnosis_hooks=False,
109
+ )
110
+ )
111
+
112
+ _detailed_spec = MetricsVerbositySpec(
113
+ name=TelemetryVerbosity.DETAILED.value,
114
+ enable_phase_sync=True,
115
+ enable_sigma=True,
116
+ enable_aggregate_si=True,
117
+ enable_advanced=False,
118
+ attach_coherence_hooks=True,
119
+ attach_diagnosis_hooks=False,
120
+ )
121
+ _register_metrics_preset(_detailed_spec)
122
+ _register_metrics_preset(
123
+ _detailed_spec._replace(
124
+ name=TelemetryVerbosity.DEBUG.value,
125
+ enable_advanced=True,
126
+ attach_diagnosis_hooks=True,
127
+ )
128
+ )
129
+
130
+
131
+ _METRICS_BASE_HISTORY_KEYS = ("C_steps", "stable_frac", "delta_Si", "B")
132
+ _METRICS_PHASE_HISTORY_KEYS = ("phase_sync", "kuramoto_R")
133
+ _METRICS_SIGMA_HISTORY_KEYS = (
134
+ GLYPH_LOAD_STABILIZERS_KEY,
135
+ "glyph_load_disr",
136
+ "sense_sigma_x",
137
+ "sense_sigma_y",
138
+ "sense_sigma_mag",
139
+ "sense_sigma_angle",
140
+ )
141
+ _METRICS_SI_HISTORY_KEYS = ("Si_mean", "Si_hi_frac", "Si_lo_frac")
142
+
143
+
144
+ def _resolve_metrics_verbosity(cfg: Mapping[str, Any]) -> MetricsVerbositySpec:
145
+ """Return the preset matching ``cfg['verbosity']``."""
146
+
147
+ raw_value = cfg.get("verbosity", METRICS_VERBOSITY_DEFAULT)
148
+ key = str(raw_value).lower()
149
+ spec = _METRICS_VERBOSITY_PRESETS.get(key)
150
+ if spec is not None:
151
+ return spec
152
+ logger.warning(
153
+ "Unknown METRICS verbosity '%s'; falling back to '%s'",
154
+ raw_value,
155
+ METRICS_VERBOSITY_DEFAULT,
156
+ )
157
+ return _METRICS_VERBOSITY_PRESETS[METRICS_VERBOSITY_DEFAULT]
158
+
159
+
160
+ def _metrics_step(G: TNFRGraph, ctx: dict[str, Any] | None = None) -> None:
161
+ """Update operational TNFR metrics per step."""
162
+
163
+ del ctx
164
+
165
+ cfg = cast(Mapping[str, Any], get_param(G, "METRICS"))
166
+ if not cfg.get("enabled", True):
167
+ return
168
+
169
+ spec = _resolve_metrics_verbosity(cfg)
170
+ hist = ensure_history(G)
171
+ if "glyph_load_estab" in hist:
172
+ raise ValueError(
173
+ "History payloads using 'glyph_load_estab' are no longer supported. "
174
+ "Rename the series to 'glyph_load_stabilizers' before loading the graph."
175
+ )
176
+ metrics_sentinel_key = "_metrics_history_id"
177
+ history_id = id(hist)
178
+ if G.graph.get(metrics_sentinel_key) != history_id:
179
+ for key in _METRICS_BASE_HISTORY_KEYS:
180
+ hist.setdefault(key, [])
181
+ if spec.enable_phase_sync:
182
+ for key in _METRICS_PHASE_HISTORY_KEYS:
183
+ hist.setdefault(key, [])
184
+ if spec.enable_sigma:
185
+ for key in _METRICS_SIGMA_HISTORY_KEYS:
186
+ hist.setdefault(key, [])
187
+ if spec.enable_aggregate_si:
188
+ for key in _METRICS_SI_HISTORY_KEYS:
189
+ hist.setdefault(key, [])
190
+ G.graph[metrics_sentinel_key] = history_id
191
+
192
+ dt = float(get_param(G, "DT"))
193
+ eps_dnfr = float(get_param(G, "EPS_DNFR_STABLE"))
194
+ eps_depi = float(get_param(G, "EPS_DEPI_STABLE"))
195
+ t = float(G.graph.get("_t", 0.0))
196
+
197
+ _update_coherence(G, hist)
198
+
199
+ raw_jobs = cfg.get("n_jobs")
200
+ metrics_jobs: int | None
201
+ try:
202
+ metrics_jobs = None if raw_jobs is None else int(raw_jobs)
203
+ except (TypeError, ValueError):
204
+ metrics_jobs = None
205
+ else:
206
+ if metrics_jobs <= 0:
207
+ metrics_jobs = None
208
+
209
+ _track_stability(
210
+ G,
211
+ hist,
212
+ dt,
213
+ eps_dnfr,
214
+ eps_depi,
215
+ n_jobs=metrics_jobs,
216
+ )
217
+ if spec.enable_phase_sync or spec.enable_sigma:
218
+ try:
219
+ if spec.enable_phase_sync:
220
+ _update_phase_sync(G, hist)
221
+ if spec.enable_sigma:
222
+ _update_sigma(G, hist)
223
+ except (KeyError, AttributeError, TypeError) as exc:
224
+ logger.debug("observer update failed: %s", exc)
225
+
226
+ if hist.get("C_steps") and hist.get("stable_frac"):
227
+ append_metric(
228
+ hist,
229
+ "iota",
230
+ hist["C_steps"][-1] * hist["stable_frac"][-1],
231
+ )
232
+
233
+ if spec.enable_aggregate_si:
234
+ _aggregate_si(G, hist, n_jobs=metrics_jobs)
235
+
236
+ if spec.enable_advanced:
237
+ _compute_advanced_metrics(
238
+ G,
239
+ cast(GlyphMetricsHistory, hist),
240
+ t,
241
+ dt,
242
+ cfg,
243
+ n_jobs=metrics_jobs,
244
+ )
245
+
246
+
247
+ def register_metrics_callbacks(G: TNFRGraph) -> None:
248
+ cfg = cast(Mapping[str, Any], get_param(G, "METRICS"))
249
+ spec = _resolve_metrics_verbosity(cfg)
250
+ callback_manager.register_callback(
251
+ G,
252
+ event=CallbackEvent.AFTER_STEP.value,
253
+ func=_metrics_step,
254
+ name="metrics_step",
255
+ )
256
+ if spec.attach_coherence_hooks:
257
+ register_coherence_callbacks(G)
258
+ if spec.attach_diagnosis_hooks:
259
+ register_diagnosis_callbacks(G)
tnfr/metrics/core.pyi ADDED
@@ -0,0 +1,13 @@
1
+ from typing import Any
2
+
3
+ __all__: Any
4
+
5
+ def __getattr__(name: str) -> Any: ...
6
+
7
+ Tg_by_node: Any
8
+ Tg_global: Any
9
+ _metrics_step: Any
10
+ glyph_top: Any
11
+ glyphogram_series: Any
12
+ latency_series: Any
13
+ register_metrics_callbacks: Any