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.
- tnfr/__init__.py +270 -90
- tnfr/__init__.pyi +40 -0
- tnfr/_compat.py +11 -0
- tnfr/_version.py +7 -0
- tnfr/_version.pyi +7 -0
- tnfr/alias.py +631 -0
- tnfr/alias.pyi +140 -0
- tnfr/cache.py +732 -0
- tnfr/cache.pyi +232 -0
- tnfr/callback_utils.py +381 -0
- tnfr/callback_utils.pyi +105 -0
- tnfr/cli/__init__.py +89 -0
- tnfr/cli/__init__.pyi +47 -0
- tnfr/cli/arguments.py +199 -0
- tnfr/cli/arguments.pyi +33 -0
- tnfr/cli/execution.py +322 -0
- tnfr/cli/execution.pyi +80 -0
- tnfr/cli/utils.py +34 -0
- tnfr/cli/utils.pyi +8 -0
- tnfr/config/__init__.py +12 -0
- tnfr/config/__init__.pyi +8 -0
- tnfr/config/constants.py +104 -0
- tnfr/config/constants.pyi +12 -0
- tnfr/config/init.py +36 -0
- tnfr/config/init.pyi +8 -0
- tnfr/config/operator_names.py +106 -0
- tnfr/config/operator_names.pyi +28 -0
- tnfr/config/presets.py +104 -0
- tnfr/config/presets.pyi +7 -0
- tnfr/constants/__init__.py +228 -0
- tnfr/constants/__init__.pyi +104 -0
- tnfr/constants/core.py +158 -0
- tnfr/constants/core.pyi +17 -0
- tnfr/constants/init.py +31 -0
- tnfr/constants/init.pyi +12 -0
- tnfr/constants/metric.py +102 -0
- tnfr/constants/metric.pyi +19 -0
- tnfr/constants_glyphs.py +16 -0
- tnfr/constants_glyphs.pyi +12 -0
- tnfr/dynamics/__init__.py +136 -0
- tnfr/dynamics/__init__.pyi +83 -0
- tnfr/dynamics/adaptation.py +201 -0
- tnfr/dynamics/aliases.py +22 -0
- tnfr/dynamics/coordination.py +343 -0
- tnfr/dynamics/dnfr.py +2315 -0
- tnfr/dynamics/dnfr.pyi +33 -0
- tnfr/dynamics/integrators.py +561 -0
- tnfr/dynamics/integrators.pyi +35 -0
- tnfr/dynamics/runtime.py +521 -0
- tnfr/dynamics/sampling.py +34 -0
- tnfr/dynamics/sampling.pyi +7 -0
- tnfr/dynamics/selectors.py +680 -0
- tnfr/execution.py +216 -0
- tnfr/execution.pyi +65 -0
- tnfr/flatten.py +283 -0
- tnfr/flatten.pyi +28 -0
- tnfr/gamma.py +320 -89
- tnfr/gamma.pyi +40 -0
- tnfr/glyph_history.py +337 -0
- tnfr/glyph_history.pyi +53 -0
- tnfr/grammar.py +23 -153
- tnfr/grammar.pyi +13 -0
- tnfr/helpers/__init__.py +151 -0
- tnfr/helpers/__init__.pyi +66 -0
- tnfr/helpers/numeric.py +88 -0
- tnfr/helpers/numeric.pyi +12 -0
- tnfr/immutable.py +214 -0
- tnfr/immutable.pyi +37 -0
- tnfr/initialization.py +199 -0
- tnfr/initialization.pyi +73 -0
- tnfr/io.py +311 -0
- tnfr/io.pyi +11 -0
- tnfr/locking.py +37 -0
- tnfr/locking.pyi +7 -0
- tnfr/metrics/__init__.py +41 -0
- tnfr/metrics/__init__.pyi +20 -0
- tnfr/metrics/coherence.py +1469 -0
- tnfr/metrics/common.py +149 -0
- tnfr/metrics/common.pyi +15 -0
- tnfr/metrics/core.py +259 -0
- tnfr/metrics/core.pyi +13 -0
- tnfr/metrics/diagnosis.py +840 -0
- tnfr/metrics/diagnosis.pyi +89 -0
- tnfr/metrics/export.py +151 -0
- tnfr/metrics/glyph_timing.py +369 -0
- tnfr/metrics/reporting.py +152 -0
- tnfr/metrics/reporting.pyi +12 -0
- tnfr/metrics/sense_index.py +294 -0
- tnfr/metrics/sense_index.pyi +9 -0
- tnfr/metrics/trig.py +216 -0
- tnfr/metrics/trig.pyi +12 -0
- tnfr/metrics/trig_cache.py +105 -0
- tnfr/metrics/trig_cache.pyi +10 -0
- tnfr/node.py +255 -177
- tnfr/node.pyi +161 -0
- tnfr/observers.py +154 -150
- tnfr/observers.pyi +46 -0
- tnfr/ontosim.py +135 -134
- tnfr/ontosim.pyi +33 -0
- tnfr/operators/__init__.py +452 -0
- tnfr/operators/__init__.pyi +31 -0
- tnfr/operators/definitions.py +181 -0
- tnfr/operators/definitions.pyi +92 -0
- tnfr/operators/jitter.py +266 -0
- tnfr/operators/jitter.pyi +11 -0
- tnfr/operators/registry.py +80 -0
- tnfr/operators/registry.pyi +15 -0
- tnfr/operators/remesh.py +569 -0
- tnfr/presets.py +10 -23
- tnfr/presets.pyi +7 -0
- tnfr/py.typed +0 -0
- tnfr/rng.py +440 -0
- tnfr/rng.pyi +14 -0
- tnfr/selector.py +217 -0
- tnfr/selector.pyi +19 -0
- tnfr/sense.py +307 -142
- tnfr/sense.pyi +30 -0
- tnfr/structural.py +69 -164
- tnfr/structural.pyi +46 -0
- tnfr/telemetry/__init__.py +13 -0
- tnfr/telemetry/verbosity.py +37 -0
- tnfr/tokens.py +61 -0
- tnfr/tokens.pyi +41 -0
- tnfr/trace.py +520 -95
- tnfr/trace.pyi +68 -0
- tnfr/types.py +382 -17
- tnfr/types.pyi +145 -0
- tnfr/utils/__init__.py +158 -0
- tnfr/utils/__init__.pyi +133 -0
- tnfr/utils/cache.py +755 -0
- tnfr/utils/cache.pyi +156 -0
- tnfr/utils/data.py +267 -0
- tnfr/utils/data.pyi +73 -0
- tnfr/utils/graph.py +87 -0
- tnfr/utils/graph.pyi +10 -0
- tnfr/utils/init.py +746 -0
- tnfr/utils/init.pyi +85 -0
- tnfr/utils/io.py +157 -0
- tnfr/utils/io.pyi +10 -0
- tnfr/utils/validators.py +130 -0
- tnfr/utils/validators.pyi +19 -0
- tnfr/validation/__init__.py +25 -0
- tnfr/validation/__init__.pyi +17 -0
- tnfr/validation/compatibility.py +59 -0
- tnfr/validation/compatibility.pyi +8 -0
- tnfr/validation/grammar.py +149 -0
- tnfr/validation/grammar.pyi +11 -0
- tnfr/validation/rules.py +194 -0
- tnfr/validation/rules.pyi +18 -0
- tnfr/validation/syntax.py +151 -0
- tnfr/validation/syntax.pyi +7 -0
- tnfr-6.0.0.dist-info/METADATA +135 -0
- tnfr-6.0.0.dist-info/RECORD +157 -0
- tnfr/cli.py +0 -322
- tnfr/config.py +0 -41
- tnfr/constants.py +0 -277
- tnfr/dynamics.py +0 -814
- tnfr/helpers.py +0 -264
- tnfr/main.py +0 -47
- tnfr/metrics.py +0 -597
- tnfr/operators.py +0 -525
- tnfr/program.py +0 -176
- tnfr/scenarios.py +0 -34
- tnfr/validators.py +0 -38
- tnfr-4.5.1.dist-info/METADATA +0 -221
- tnfr-4.5.1.dist-info/RECORD +0 -28
- {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/WHEEL +0 -0
- {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/entry_points.txt +0 -0
- {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/licenses/LICENSE.md +0 -0
- {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)
|
tnfr/metrics/common.pyi
ADDED
|
@@ -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