tnfr 4.5.2__py3-none-any.whl → 8.5.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 +334 -50
- 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 +214 -37
- tnfr/alias.pyi +108 -0
- tnfr/backends/__init__.py +354 -0
- tnfr/backends/jax_backend.py +173 -0
- tnfr/backends/numpy_backend.py +238 -0
- tnfr/backends/optimized_numpy.py +420 -0
- tnfr/backends/torch_backend.py +408 -0
- tnfr/cache.py +149 -556
- tnfr/cache.pyi +13 -0
- tnfr/cli/__init__.py +51 -16
- tnfr/cli/__init__.pyi +26 -0
- tnfr/cli/arguments.py +344 -32
- tnfr/cli/arguments.pyi +29 -0
- tnfr/cli/execution.py +676 -50
- tnfr/cli/execution.pyi +70 -0
- tnfr/cli/interactive_validator.py +614 -0
- tnfr/cli/utils.py +18 -3
- tnfr/cli/utils.pyi +7 -0
- tnfr/cli/validate.py +236 -0
- tnfr/compat/__init__.py +85 -0
- tnfr/compat/dataclass.py +136 -0
- tnfr/compat/jsonschema_stub.py +61 -0
- tnfr/compat/matplotlib_stub.py +73 -0
- tnfr/compat/numpy_stub.py +155 -0
- tnfr/config/__init__.py +224 -0
- tnfr/config/__init__.pyi +10 -0
- tnfr/{constants_glyphs.py → config/constants.py} +26 -20
- tnfr/config/constants.pyi +12 -0
- tnfr/config/defaults.py +54 -0
- tnfr/{constants/core.py → config/defaults_core.py} +59 -6
- tnfr/config/defaults_init.py +33 -0
- tnfr/config/defaults_metric.py +104 -0
- tnfr/config/feature_flags.py +81 -0
- tnfr/config/feature_flags.pyi +16 -0
- tnfr/config/glyph_constants.py +31 -0
- tnfr/config/init.py +77 -0
- tnfr/config/init.pyi +8 -0
- tnfr/config/operator_names.py +254 -0
- tnfr/config/operator_names.pyi +36 -0
- tnfr/config/physics_derivation.py +354 -0
- tnfr/config/presets.py +83 -0
- tnfr/config/presets.pyi +7 -0
- tnfr/config/security.py +927 -0
- tnfr/config/thresholds.py +114 -0
- tnfr/config/tnfr_config.py +498 -0
- tnfr/constants/__init__.py +51 -133
- tnfr/constants/__init__.pyi +92 -0
- tnfr/constants/aliases.py +33 -0
- tnfr/constants/aliases.pyi +27 -0
- tnfr/constants/init.py +3 -1
- tnfr/constants/init.pyi +12 -0
- tnfr/constants/metric.py +9 -15
- tnfr/constants/metric.pyi +19 -0
- tnfr/core/__init__.py +33 -0
- tnfr/core/container.py +226 -0
- tnfr/core/default_implementations.py +329 -0
- tnfr/core/interfaces.py +279 -0
- tnfr/dynamics/__init__.py +213 -633
- tnfr/dynamics/__init__.pyi +83 -0
- tnfr/dynamics/adaptation.py +267 -0
- tnfr/dynamics/adaptation.pyi +7 -0
- tnfr/dynamics/adaptive_sequences.py +189 -0
- tnfr/dynamics/adaptive_sequences.pyi +14 -0
- tnfr/dynamics/aliases.py +23 -0
- tnfr/dynamics/aliases.pyi +19 -0
- tnfr/dynamics/bifurcation.py +232 -0
- tnfr/dynamics/canonical.py +229 -0
- tnfr/dynamics/canonical.pyi +48 -0
- tnfr/dynamics/coordination.py +385 -0
- tnfr/dynamics/coordination.pyi +25 -0
- tnfr/dynamics/dnfr.py +2699 -398
- tnfr/dynamics/dnfr.pyi +26 -0
- tnfr/dynamics/dynamic_limits.py +225 -0
- tnfr/dynamics/feedback.py +252 -0
- tnfr/dynamics/feedback.pyi +24 -0
- tnfr/dynamics/fused_dnfr.py +454 -0
- tnfr/dynamics/homeostasis.py +157 -0
- tnfr/dynamics/homeostasis.pyi +14 -0
- tnfr/dynamics/integrators.py +496 -102
- tnfr/dynamics/integrators.pyi +36 -0
- tnfr/dynamics/learning.py +310 -0
- tnfr/dynamics/learning.pyi +33 -0
- tnfr/dynamics/metabolism.py +254 -0
- tnfr/dynamics/nbody.py +796 -0
- tnfr/dynamics/nbody_tnfr.py +783 -0
- tnfr/dynamics/propagation.py +326 -0
- tnfr/dynamics/runtime.py +908 -0
- tnfr/dynamics/runtime.pyi +77 -0
- tnfr/dynamics/sampling.py +10 -5
- tnfr/dynamics/sampling.pyi +7 -0
- tnfr/dynamics/selectors.py +711 -0
- tnfr/dynamics/selectors.pyi +85 -0
- tnfr/dynamics/structural_clip.py +207 -0
- tnfr/errors/__init__.py +37 -0
- tnfr/errors/contextual.py +492 -0
- tnfr/execution.py +77 -55
- tnfr/execution.pyi +45 -0
- tnfr/extensions/__init__.py +205 -0
- tnfr/extensions/__init__.pyi +18 -0
- tnfr/extensions/base.py +173 -0
- tnfr/extensions/base.pyi +35 -0
- tnfr/extensions/business/__init__.py +71 -0
- tnfr/extensions/business/__init__.pyi +11 -0
- tnfr/extensions/business/cookbook.py +88 -0
- tnfr/extensions/business/cookbook.pyi +8 -0
- tnfr/extensions/business/health_analyzers.py +202 -0
- tnfr/extensions/business/health_analyzers.pyi +9 -0
- tnfr/extensions/business/patterns.py +183 -0
- tnfr/extensions/business/patterns.pyi +8 -0
- tnfr/extensions/medical/__init__.py +73 -0
- tnfr/extensions/medical/__init__.pyi +11 -0
- tnfr/extensions/medical/cookbook.py +88 -0
- tnfr/extensions/medical/cookbook.pyi +8 -0
- tnfr/extensions/medical/health_analyzers.py +181 -0
- tnfr/extensions/medical/health_analyzers.pyi +9 -0
- tnfr/extensions/medical/patterns.py +163 -0
- tnfr/extensions/medical/patterns.pyi +8 -0
- tnfr/flatten.py +29 -50
- tnfr/flatten.pyi +21 -0
- tnfr/gamma.py +66 -53
- tnfr/gamma.pyi +36 -0
- tnfr/glyph_history.py +144 -57
- tnfr/glyph_history.pyi +35 -0
- tnfr/glyph_runtime.py +19 -0
- tnfr/glyph_runtime.pyi +8 -0
- tnfr/immutable.py +70 -30
- tnfr/immutable.pyi +36 -0
- tnfr/initialization.py +22 -16
- tnfr/initialization.pyi +65 -0
- tnfr/io.py +5 -241
- tnfr/io.pyi +13 -0
- tnfr/locking.pyi +7 -0
- tnfr/mathematics/__init__.py +79 -0
- tnfr/mathematics/backend.py +453 -0
- tnfr/mathematics/backend.pyi +99 -0
- tnfr/mathematics/dynamics.py +408 -0
- tnfr/mathematics/dynamics.pyi +90 -0
- tnfr/mathematics/epi.py +391 -0
- tnfr/mathematics/epi.pyi +65 -0
- tnfr/mathematics/generators.py +242 -0
- tnfr/mathematics/generators.pyi +29 -0
- tnfr/mathematics/metrics.py +119 -0
- tnfr/mathematics/metrics.pyi +16 -0
- tnfr/mathematics/operators.py +239 -0
- tnfr/mathematics/operators.pyi +59 -0
- tnfr/mathematics/operators_factory.py +124 -0
- tnfr/mathematics/operators_factory.pyi +11 -0
- tnfr/mathematics/projection.py +87 -0
- tnfr/mathematics/projection.pyi +33 -0
- tnfr/mathematics/runtime.py +182 -0
- tnfr/mathematics/runtime.pyi +64 -0
- tnfr/mathematics/spaces.py +256 -0
- tnfr/mathematics/spaces.pyi +83 -0
- tnfr/mathematics/transforms.py +305 -0
- tnfr/mathematics/transforms.pyi +62 -0
- tnfr/metrics/__init__.py +47 -9
- tnfr/metrics/__init__.pyi +20 -0
- tnfr/metrics/buffer_cache.py +163 -0
- tnfr/metrics/buffer_cache.pyi +24 -0
- tnfr/metrics/cache_utils.py +214 -0
- tnfr/metrics/coherence.py +1510 -330
- tnfr/metrics/coherence.pyi +129 -0
- tnfr/metrics/common.py +23 -16
- tnfr/metrics/common.pyi +35 -0
- tnfr/metrics/core.py +251 -36
- tnfr/metrics/core.pyi +13 -0
- tnfr/metrics/diagnosis.py +709 -110
- tnfr/metrics/diagnosis.pyi +86 -0
- tnfr/metrics/emergence.py +245 -0
- tnfr/metrics/export.py +60 -18
- tnfr/metrics/export.pyi +7 -0
- tnfr/metrics/glyph_timing.py +233 -43
- tnfr/metrics/glyph_timing.pyi +81 -0
- tnfr/metrics/learning_metrics.py +280 -0
- tnfr/metrics/learning_metrics.pyi +21 -0
- tnfr/metrics/phase_coherence.py +351 -0
- tnfr/metrics/phase_compatibility.py +349 -0
- tnfr/metrics/reporting.py +63 -28
- tnfr/metrics/reporting.pyi +25 -0
- tnfr/metrics/sense_index.py +1126 -43
- tnfr/metrics/sense_index.pyi +9 -0
- tnfr/metrics/trig.py +215 -23
- tnfr/metrics/trig.pyi +13 -0
- tnfr/metrics/trig_cache.py +148 -24
- tnfr/metrics/trig_cache.pyi +10 -0
- tnfr/multiscale/__init__.py +32 -0
- tnfr/multiscale/hierarchical.py +517 -0
- tnfr/node.py +646 -140
- tnfr/node.pyi +139 -0
- tnfr/observers.py +160 -45
- tnfr/observers.pyi +31 -0
- tnfr/ontosim.py +23 -19
- tnfr/ontosim.pyi +28 -0
- tnfr/operators/__init__.py +1358 -106
- tnfr/operators/__init__.pyi +31 -0
- tnfr/operators/algebra.py +277 -0
- tnfr/operators/canonical_patterns.py +420 -0
- tnfr/operators/cascade.py +267 -0
- tnfr/operators/cycle_detection.py +358 -0
- tnfr/operators/definitions.py +4108 -0
- tnfr/operators/definitions.pyi +78 -0
- tnfr/operators/grammar.py +1164 -0
- tnfr/operators/grammar.pyi +140 -0
- tnfr/operators/hamiltonian.py +710 -0
- tnfr/operators/health_analyzer.py +809 -0
- tnfr/operators/jitter.py +107 -38
- tnfr/operators/jitter.pyi +11 -0
- tnfr/operators/lifecycle.py +314 -0
- tnfr/operators/metabolism.py +618 -0
- tnfr/operators/metrics.py +2138 -0
- tnfr/operators/network_analysis/__init__.py +27 -0
- tnfr/operators/network_analysis/source_detection.py +186 -0
- tnfr/operators/nodal_equation.py +395 -0
- tnfr/operators/pattern_detection.py +660 -0
- tnfr/operators/patterns.py +669 -0
- tnfr/operators/postconditions/__init__.py +38 -0
- tnfr/operators/postconditions/mutation.py +236 -0
- tnfr/operators/preconditions/__init__.py +1226 -0
- tnfr/operators/preconditions/coherence.py +305 -0
- tnfr/operators/preconditions/dissonance.py +236 -0
- tnfr/operators/preconditions/emission.py +128 -0
- tnfr/operators/preconditions/mutation.py +580 -0
- tnfr/operators/preconditions/reception.py +125 -0
- tnfr/operators/preconditions/resonance.py +364 -0
- tnfr/operators/registry.py +74 -0
- tnfr/operators/registry.pyi +9 -0
- tnfr/operators/remesh.py +1415 -91
- tnfr/operators/remesh.pyi +26 -0
- tnfr/operators/structural_units.py +268 -0
- tnfr/operators/unified_grammar.py +105 -0
- tnfr/parallel/__init__.py +54 -0
- tnfr/parallel/auto_scaler.py +234 -0
- tnfr/parallel/distributed.py +384 -0
- tnfr/parallel/engine.py +238 -0
- tnfr/parallel/gpu_engine.py +420 -0
- tnfr/parallel/monitoring.py +248 -0
- tnfr/parallel/partitioner.py +459 -0
- tnfr/py.typed +0 -0
- tnfr/recipes/__init__.py +22 -0
- tnfr/recipes/cookbook.py +743 -0
- tnfr/rng.py +75 -151
- tnfr/rng.pyi +26 -0
- tnfr/schemas/__init__.py +8 -0
- tnfr/schemas/grammar.json +94 -0
- tnfr/sdk/__init__.py +107 -0
- tnfr/sdk/__init__.pyi +19 -0
- tnfr/sdk/adaptive_system.py +173 -0
- tnfr/sdk/adaptive_system.pyi +21 -0
- tnfr/sdk/builders.py +370 -0
- tnfr/sdk/builders.pyi +51 -0
- tnfr/sdk/fluent.py +1121 -0
- tnfr/sdk/fluent.pyi +74 -0
- tnfr/sdk/templates.py +342 -0
- tnfr/sdk/templates.pyi +41 -0
- tnfr/sdk/utils.py +341 -0
- tnfr/secure_config.py +46 -0
- tnfr/security/__init__.py +70 -0
- tnfr/security/database.py +514 -0
- tnfr/security/subprocess.py +503 -0
- tnfr/security/validation.py +290 -0
- tnfr/selector.py +59 -22
- tnfr/selector.pyi +19 -0
- tnfr/sense.py +92 -67
- tnfr/sense.pyi +23 -0
- tnfr/services/__init__.py +17 -0
- tnfr/services/orchestrator.py +325 -0
- tnfr/sparse/__init__.py +39 -0
- tnfr/sparse/representations.py +492 -0
- tnfr/structural.py +639 -263
- tnfr/structural.pyi +83 -0
- tnfr/telemetry/__init__.py +35 -0
- tnfr/telemetry/cache_metrics.py +226 -0
- tnfr/telemetry/cache_metrics.pyi +64 -0
- tnfr/telemetry/nu_f.py +422 -0
- tnfr/telemetry/nu_f.pyi +108 -0
- tnfr/telemetry/verbosity.py +36 -0
- tnfr/telemetry/verbosity.pyi +15 -0
- tnfr/tokens.py +2 -4
- tnfr/tokens.pyi +36 -0
- tnfr/tools/__init__.py +20 -0
- tnfr/tools/domain_templates.py +478 -0
- tnfr/tools/sequence_generator.py +846 -0
- tnfr/topology/__init__.py +13 -0
- tnfr/topology/asymmetry.py +151 -0
- tnfr/trace.py +300 -126
- tnfr/trace.pyi +42 -0
- tnfr/tutorials/__init__.py +38 -0
- tnfr/tutorials/autonomous_evolution.py +285 -0
- tnfr/tutorials/interactive.py +1576 -0
- tnfr/tutorials/structural_metabolism.py +238 -0
- tnfr/types.py +743 -12
- tnfr/types.pyi +357 -0
- tnfr/units.py +68 -0
- tnfr/units.pyi +13 -0
- tnfr/utils/__init__.py +282 -0
- tnfr/utils/__init__.pyi +215 -0
- tnfr/utils/cache.py +4223 -0
- tnfr/utils/cache.pyi +470 -0
- tnfr/{callback_utils.py → utils/callbacks.py} +26 -39
- tnfr/utils/callbacks.pyi +49 -0
- tnfr/utils/chunks.py +108 -0
- tnfr/utils/chunks.pyi +22 -0
- tnfr/utils/data.py +428 -0
- tnfr/utils/data.pyi +74 -0
- tnfr/utils/graph.py +85 -0
- tnfr/utils/graph.pyi +10 -0
- tnfr/utils/init.py +821 -0
- tnfr/utils/init.pyi +80 -0
- tnfr/utils/io.py +559 -0
- tnfr/utils/io.pyi +66 -0
- tnfr/{helpers → utils}/numeric.py +51 -24
- tnfr/utils/numeric.pyi +21 -0
- tnfr/validation/__init__.py +257 -0
- tnfr/validation/__init__.pyi +85 -0
- tnfr/validation/compatibility.py +460 -0
- tnfr/validation/compatibility.pyi +6 -0
- tnfr/validation/config.py +73 -0
- tnfr/validation/graph.py +139 -0
- tnfr/validation/graph.pyi +18 -0
- tnfr/validation/input_validation.py +755 -0
- tnfr/validation/invariants.py +712 -0
- tnfr/validation/rules.py +253 -0
- tnfr/validation/rules.pyi +44 -0
- tnfr/validation/runtime.py +279 -0
- tnfr/validation/runtime.pyi +28 -0
- tnfr/validation/sequence_validator.py +162 -0
- tnfr/validation/soft_filters.py +170 -0
- tnfr/validation/soft_filters.pyi +32 -0
- tnfr/validation/spectral.py +164 -0
- tnfr/validation/spectral.pyi +42 -0
- tnfr/validation/validator.py +1266 -0
- tnfr/validation/window.py +39 -0
- tnfr/validation/window.pyi +1 -0
- tnfr/visualization/__init__.py +98 -0
- tnfr/visualization/cascade_viz.py +256 -0
- tnfr/visualization/hierarchy.py +284 -0
- tnfr/visualization/sequence_plotter.py +784 -0
- tnfr/viz/__init__.py +60 -0
- tnfr/viz/matplotlib.py +278 -0
- tnfr/viz/matplotlib.pyi +35 -0
- tnfr-8.5.0.dist-info/METADATA +573 -0
- tnfr-8.5.0.dist-info/RECORD +353 -0
- {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/entry_points.txt +1 -0
- {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/licenses/LICENSE.md +1 -1
- tnfr/collections_utils.py +0 -300
- tnfr/config.py +0 -32
- 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-8.5.0.dist-info}/WHEEL +0 -0
- {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/top_level.txt +0 -0
tnfr/metrics/glyph_timing.py
CHANGED
|
@@ -2,24 +2,75 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import math
|
|
5
6
|
from collections import Counter, defaultdict
|
|
7
|
+
from concurrent.futures import ProcessPoolExecutor
|
|
6
8
|
from dataclasses import dataclass
|
|
7
|
-
from
|
|
9
|
+
from types import ModuleType
|
|
10
|
+
from typing import (
|
|
11
|
+
Any,
|
|
12
|
+
Callable,
|
|
13
|
+
Mapping,
|
|
14
|
+
MutableMapping,
|
|
15
|
+
Sequence,
|
|
16
|
+
cast,
|
|
17
|
+
)
|
|
8
18
|
|
|
9
19
|
from ..alias import get_attr
|
|
10
|
-
from ..constants import
|
|
11
|
-
from ..
|
|
12
|
-
from ..
|
|
13
|
-
from ..
|
|
20
|
+
from ..config.constants import GLYPH_GROUPS, GLYPHS_CANONICAL
|
|
21
|
+
from ..constants import get_param
|
|
22
|
+
from ..constants.aliases import ALIAS_EPI
|
|
23
|
+
from ..glyph_history import append_metric
|
|
24
|
+
from ..glyph_runtime import last_glyph
|
|
25
|
+
from ..utils import resolve_chunk_size
|
|
26
|
+
from ..types import (
|
|
27
|
+
GlyphCounts,
|
|
28
|
+
GlyphMetricsHistory,
|
|
29
|
+
GlyphTimingByNode,
|
|
30
|
+
GlyphTimingTotals,
|
|
31
|
+
GlyphogramRow,
|
|
32
|
+
GraphLike,
|
|
33
|
+
MetricsListHistory,
|
|
34
|
+
SigmaTrace,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
LATENT_GLYPH: str = "SHA"
|
|
38
|
+
DEFAULT_EPI_SUPPORT_LIMIT = 0.05
|
|
14
39
|
|
|
15
|
-
|
|
40
|
+
try: # pragma: no cover - import guard exercised via tests
|
|
41
|
+
import numpy as _np # type: ignore[import-not-found]
|
|
42
|
+
except Exception: # pragma: no cover - numpy optional dependency
|
|
43
|
+
_np = None
|
|
16
44
|
|
|
17
|
-
|
|
18
|
-
|
|
45
|
+
np: ModuleType | None = cast(ModuleType | None, _np)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _has_numpy_support(np_obj: object) -> bool:
|
|
49
|
+
"""Return ``True`` when ``np_obj`` exposes the required NumPy API."""
|
|
50
|
+
|
|
51
|
+
return isinstance(np_obj, ModuleType) or (
|
|
52
|
+
np_obj is not None
|
|
53
|
+
and hasattr(np_obj, "fromiter")
|
|
54
|
+
and hasattr(np_obj, "bincount")
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
_GLYPH_TO_INDEX = {glyph: idx for idx, glyph in enumerate(GLYPHS_CANONICAL)}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _coerce_float(value: Any) -> float:
|
|
62
|
+
"""Attempt to coerce ``value`` to ``float`` returning ``0.0`` on failure."""
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
return float(value)
|
|
66
|
+
except (TypeError, ValueError):
|
|
67
|
+
return 0.0
|
|
19
68
|
|
|
20
69
|
|
|
21
70
|
@dataclass
|
|
22
71
|
class GlyphTiming:
|
|
72
|
+
"""Mutable accumulator tracking the active glyph and its dwell time."""
|
|
73
|
+
|
|
23
74
|
curr: str | None = None
|
|
24
75
|
run: float = 0.0
|
|
25
76
|
|
|
@@ -27,6 +78,10 @@ class GlyphTiming:
|
|
|
27
78
|
__all__ = [
|
|
28
79
|
"LATENT_GLYPH",
|
|
29
80
|
"GlyphTiming",
|
|
81
|
+
"SigmaTrace",
|
|
82
|
+
"GlyphogramRow",
|
|
83
|
+
"GlyphTimingTotals",
|
|
84
|
+
"GlyphTimingByNode",
|
|
30
85
|
"_tg_state",
|
|
31
86
|
"for_each_glyph",
|
|
32
87
|
"_update_tg_node",
|
|
@@ -38,19 +93,39 @@ __all__ = [
|
|
|
38
93
|
"_compute_advanced_metrics",
|
|
39
94
|
]
|
|
40
95
|
|
|
41
|
-
|
|
42
96
|
# ---------------------------------------------------------------------------
|
|
43
97
|
# Internal utilities
|
|
44
98
|
# ---------------------------------------------------------------------------
|
|
45
99
|
|
|
46
100
|
|
|
47
|
-
def
|
|
101
|
+
def _count_glyphs_chunk(chunk: Sequence[str]) -> Counter[str]:
|
|
102
|
+
"""Count glyph occurrences within a chunk (multiprocessing helper)."""
|
|
103
|
+
|
|
104
|
+
counter: Counter[str] = Counter()
|
|
105
|
+
for glyph in chunk:
|
|
106
|
+
counter[glyph] += 1
|
|
107
|
+
return counter
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _epi_support_chunk(values: Sequence[float], threshold: float) -> tuple[float, int]:
|
|
111
|
+
"""Compute EPI support contribution for a chunk."""
|
|
112
|
+
|
|
113
|
+
total = 0.0
|
|
114
|
+
count = 0
|
|
115
|
+
for value in values:
|
|
116
|
+
if value >= threshold:
|
|
117
|
+
total += value
|
|
118
|
+
count += 1
|
|
119
|
+
return total, count
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _tg_state(nd: MutableMapping[str, Any]) -> GlyphTiming:
|
|
48
123
|
"""Expose per-node glyph timing state."""
|
|
49
124
|
|
|
50
125
|
return nd.setdefault("_Tg", GlyphTiming())
|
|
51
126
|
|
|
52
127
|
|
|
53
|
-
def for_each_glyph(fn: Callable[[str],
|
|
128
|
+
def for_each_glyph(fn: Callable[[str], None]) -> None:
|
|
54
129
|
"""Apply ``fn`` to each canonical structural operator."""
|
|
55
130
|
|
|
56
131
|
for g in GLYPHS_CANONICAL:
|
|
@@ -62,7 +137,13 @@ def for_each_glyph(fn: Callable[[str], Any]) -> None:
|
|
|
62
137
|
# ---------------------------------------------------------------------------
|
|
63
138
|
|
|
64
139
|
|
|
65
|
-
def _update_tg_node(
|
|
140
|
+
def _update_tg_node(
|
|
141
|
+
n: Any,
|
|
142
|
+
nd: MutableMapping[str, Any],
|
|
143
|
+
dt: float,
|
|
144
|
+
tg_total: GlyphTimingTotals,
|
|
145
|
+
tg_by_node: GlyphTimingByNode | None,
|
|
146
|
+
) -> tuple[str | None, bool]:
|
|
66
147
|
"""Track a node's glyph transition and accumulate run time."""
|
|
67
148
|
|
|
68
149
|
g = last_glyph(nd)
|
|
@@ -85,19 +166,31 @@ def _update_tg_node(n, nd, dt, tg_total, tg_by_node):
|
|
|
85
166
|
return g, g == LATENT_GLYPH
|
|
86
167
|
|
|
87
168
|
|
|
88
|
-
def _update_tg(
|
|
169
|
+
def _update_tg(
|
|
170
|
+
G: GraphLike,
|
|
171
|
+
hist: GlyphMetricsHistory,
|
|
172
|
+
dt: float,
|
|
173
|
+
save_by_node: bool,
|
|
174
|
+
n_jobs: int | None = None,
|
|
175
|
+
) -> tuple[Counter[str], int, int]:
|
|
89
176
|
"""Accumulate glyph dwell times for the entire graph."""
|
|
90
177
|
|
|
91
|
-
|
|
92
|
-
tg_total = hist.setdefault("Tg_total", defaultdict(float))
|
|
178
|
+
tg_total = cast(GlyphTimingTotals, hist.setdefault("Tg_total", defaultdict(float)))
|
|
93
179
|
tg_by_node = (
|
|
94
|
-
|
|
180
|
+
cast(
|
|
181
|
+
GlyphTimingByNode,
|
|
182
|
+
hist.setdefault(
|
|
183
|
+
"Tg_by_node",
|
|
184
|
+
defaultdict(lambda: defaultdict(list)),
|
|
185
|
+
),
|
|
186
|
+
)
|
|
95
187
|
if save_by_node
|
|
96
188
|
else None
|
|
97
189
|
)
|
|
98
190
|
|
|
99
191
|
n_total = 0
|
|
100
192
|
n_latent = 0
|
|
193
|
+
glyph_sequence: list[str] = []
|
|
101
194
|
for n, nd in G.nodes(data=True):
|
|
102
195
|
g, is_latent = _update_tg_node(n, nd, dt, tg_total, tg_by_node)
|
|
103
196
|
if g is None:
|
|
@@ -105,56 +198,152 @@ def _update_tg(G, hist, dt, save_by_node: bool):
|
|
|
105
198
|
n_total += 1
|
|
106
199
|
if is_latent:
|
|
107
200
|
n_latent += 1
|
|
108
|
-
|
|
201
|
+
glyph_sequence.append(g)
|
|
202
|
+
|
|
203
|
+
counts: Counter[str] = Counter()
|
|
204
|
+
if not glyph_sequence:
|
|
205
|
+
return counts, n_total, n_latent
|
|
206
|
+
|
|
207
|
+
if _has_numpy_support(np):
|
|
208
|
+
glyph_idx = np.fromiter(
|
|
209
|
+
(_GLYPH_TO_INDEX[glyph] for glyph in glyph_sequence),
|
|
210
|
+
dtype=np.int64,
|
|
211
|
+
count=len(glyph_sequence),
|
|
212
|
+
)
|
|
213
|
+
freq = np.bincount(glyph_idx, minlength=len(GLYPHS_CANONICAL))
|
|
214
|
+
counts.update(
|
|
215
|
+
{
|
|
216
|
+
glyph: int(freq[_GLYPH_TO_INDEX[glyph]])
|
|
217
|
+
for glyph in GLYPHS_CANONICAL
|
|
218
|
+
if freq[_GLYPH_TO_INDEX[glyph]]
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
elif n_jobs is not None and n_jobs > 1 and len(glyph_sequence) > 1:
|
|
222
|
+
approx_chunk = math.ceil(len(glyph_sequence) / n_jobs) if n_jobs else None
|
|
223
|
+
chunk_size = resolve_chunk_size(
|
|
224
|
+
approx_chunk,
|
|
225
|
+
len(glyph_sequence),
|
|
226
|
+
minimum=1,
|
|
227
|
+
)
|
|
228
|
+
futures = []
|
|
229
|
+
with ProcessPoolExecutor(max_workers=n_jobs) as executor:
|
|
230
|
+
for start in range(0, len(glyph_sequence), chunk_size):
|
|
231
|
+
chunk = glyph_sequence[start : start + chunk_size]
|
|
232
|
+
futures.append(executor.submit(_count_glyphs_chunk, chunk))
|
|
233
|
+
for future in futures:
|
|
234
|
+
counts.update(future.result())
|
|
235
|
+
else:
|
|
236
|
+
counts.update(glyph_sequence)
|
|
237
|
+
|
|
109
238
|
return counts, n_total, n_latent
|
|
110
239
|
|
|
111
240
|
|
|
112
|
-
def _update_glyphogram(
|
|
241
|
+
def _update_glyphogram(
|
|
242
|
+
G: GraphLike,
|
|
243
|
+
hist: GlyphMetricsHistory,
|
|
244
|
+
counts: GlyphCounts,
|
|
245
|
+
t: float,
|
|
246
|
+
n_total: int,
|
|
247
|
+
) -> None:
|
|
113
248
|
"""Record glyphogram row from glyph counts."""
|
|
114
249
|
|
|
115
250
|
normalize_series = bool(get_param(G, "METRICS").get("normalize_series", False))
|
|
116
|
-
row = {"t": t}
|
|
251
|
+
row: GlyphogramRow = {"t": t}
|
|
117
252
|
total = max(1, n_total)
|
|
118
253
|
for g in GLYPHS_CANONICAL:
|
|
119
254
|
c = counts.get(g, 0)
|
|
120
255
|
row[g] = (c / total) if normalize_series else c
|
|
121
|
-
append_metric(hist, "glyphogram", row)
|
|
256
|
+
append_metric(cast(MetricsListHistory, hist), "glyphogram", row)
|
|
122
257
|
|
|
123
258
|
|
|
124
|
-
def _update_latency_index(
|
|
259
|
+
def _update_latency_index(
|
|
260
|
+
G: GraphLike,
|
|
261
|
+
hist: GlyphMetricsHistory,
|
|
262
|
+
n_total: int,
|
|
263
|
+
n_latent: int,
|
|
264
|
+
t: float,
|
|
265
|
+
) -> None:
|
|
125
266
|
"""Record latency index for the current step."""
|
|
126
267
|
|
|
127
268
|
li = n_latent / max(1, n_total)
|
|
128
|
-
append_metric(
|
|
269
|
+
append_metric(
|
|
270
|
+
cast(MetricsListHistory, hist),
|
|
271
|
+
"latency_index",
|
|
272
|
+
{"t": t, "value": li},
|
|
273
|
+
)
|
|
129
274
|
|
|
130
275
|
|
|
131
276
|
def _update_epi_support(
|
|
132
|
-
G,
|
|
133
|
-
hist,
|
|
134
|
-
t,
|
|
277
|
+
G: GraphLike,
|
|
278
|
+
hist: GlyphMetricsHistory,
|
|
279
|
+
t: float,
|
|
135
280
|
threshold: float = DEFAULT_EPI_SUPPORT_LIMIT,
|
|
136
|
-
|
|
281
|
+
n_jobs: int | None = None,
|
|
282
|
+
) -> None:
|
|
137
283
|
"""Measure EPI support and normalized magnitude."""
|
|
138
284
|
|
|
285
|
+
node_count = G.number_of_nodes()
|
|
139
286
|
total = 0.0
|
|
140
287
|
count = 0
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
288
|
+
|
|
289
|
+
if _has_numpy_support(np) and node_count:
|
|
290
|
+
epi_values = np.fromiter(
|
|
291
|
+
(
|
|
292
|
+
abs(_coerce_float(get_attr(nd, ALIAS_EPI, 0.0)))
|
|
293
|
+
for _, nd in G.nodes(data=True)
|
|
294
|
+
),
|
|
295
|
+
dtype=float,
|
|
296
|
+
count=node_count,
|
|
297
|
+
)
|
|
298
|
+
mask = epi_values >= threshold
|
|
299
|
+
count = int(mask.sum())
|
|
300
|
+
if count:
|
|
301
|
+
total = float(epi_values[mask].sum())
|
|
302
|
+
elif n_jobs is not None and n_jobs > 1 and node_count > 1:
|
|
303
|
+
values = [
|
|
304
|
+
abs(_coerce_float(get_attr(nd, ALIAS_EPI, 0.0)))
|
|
305
|
+
for _, nd in G.nodes(data=True)
|
|
306
|
+
]
|
|
307
|
+
approx_chunk = math.ceil(len(values) / n_jobs) if n_jobs else None
|
|
308
|
+
chunk_size = resolve_chunk_size(
|
|
309
|
+
approx_chunk,
|
|
310
|
+
len(values),
|
|
311
|
+
minimum=1,
|
|
312
|
+
)
|
|
313
|
+
totals: list[tuple[float, int]] = []
|
|
314
|
+
with ProcessPoolExecutor(max_workers=n_jobs) as executor:
|
|
315
|
+
futures = []
|
|
316
|
+
for start in range(0, len(values), chunk_size):
|
|
317
|
+
chunk = values[start : start + chunk_size]
|
|
318
|
+
futures.append(executor.submit(_epi_support_chunk, chunk, threshold))
|
|
319
|
+
for future in futures:
|
|
320
|
+
totals.append(future.result())
|
|
321
|
+
for part_total, part_count in totals:
|
|
322
|
+
total += part_total
|
|
323
|
+
count += part_count
|
|
324
|
+
else:
|
|
325
|
+
for _, nd in G.nodes(data=True):
|
|
326
|
+
epi_val = abs(_coerce_float(get_attr(nd, ALIAS_EPI, 0.0)))
|
|
327
|
+
if epi_val >= threshold:
|
|
328
|
+
total += epi_val
|
|
329
|
+
count += 1
|
|
146
330
|
epi_norm = (total / count) if count else 0.0
|
|
147
331
|
append_metric(
|
|
148
|
-
hist,
|
|
332
|
+
cast(MetricsListHistory, hist),
|
|
149
333
|
"EPI_support",
|
|
150
334
|
{"t": t, "size": count, "epi_norm": float(epi_norm)},
|
|
151
335
|
)
|
|
152
336
|
|
|
153
337
|
|
|
154
|
-
def _update_morph_metrics(
|
|
338
|
+
def _update_morph_metrics(
|
|
339
|
+
G: GraphLike,
|
|
340
|
+
hist: GlyphMetricsHistory,
|
|
341
|
+
counts: GlyphCounts,
|
|
342
|
+
t: float,
|
|
343
|
+
) -> None:
|
|
155
344
|
"""Capture morphosyntactic distribution of glyphs."""
|
|
156
345
|
|
|
157
|
-
def get_count(keys):
|
|
346
|
+
def get_count(keys: Sequence[str]) -> int:
|
|
158
347
|
return sum(counts.get(k, 0) for k in keys)
|
|
159
348
|
|
|
160
349
|
total = max(1, sum(counts.values()))
|
|
@@ -165,25 +354,26 @@ def _update_morph_metrics(G, hist, counts, t):
|
|
|
165
354
|
den = get_count(GLYPH_GROUPS.get("PP_den", ()))
|
|
166
355
|
pp_val = 0.0 if den == 0 else num / den
|
|
167
356
|
append_metric(
|
|
168
|
-
hist,
|
|
357
|
+
cast(MetricsListHistory, hist),
|
|
169
358
|
"morph",
|
|
170
359
|
{"t": t, "ID": id_val, "CM": cm_val, "NE": ne_val, "PP": pp_val},
|
|
171
360
|
)
|
|
172
361
|
|
|
173
362
|
|
|
174
363
|
def _compute_advanced_metrics(
|
|
175
|
-
G,
|
|
176
|
-
hist,
|
|
177
|
-
t,
|
|
178
|
-
dt,
|
|
179
|
-
cfg,
|
|
364
|
+
G: GraphLike,
|
|
365
|
+
hist: GlyphMetricsHistory,
|
|
366
|
+
t: float,
|
|
367
|
+
dt: float,
|
|
368
|
+
cfg: Mapping[str, Any],
|
|
180
369
|
threshold: float = DEFAULT_EPI_SUPPORT_LIMIT,
|
|
181
|
-
|
|
370
|
+
n_jobs: int | None = None,
|
|
371
|
+
) -> None:
|
|
182
372
|
"""Compute glyph timing derived metrics."""
|
|
183
373
|
|
|
184
374
|
save_by_node = bool(cfg.get("save_by_node", True))
|
|
185
|
-
counts, n_total, n_latent = _update_tg(G, hist, dt, save_by_node)
|
|
375
|
+
counts, n_total, n_latent = _update_tg(G, hist, dt, save_by_node, n_jobs=n_jobs)
|
|
186
376
|
_update_glyphogram(G, hist, counts, t, n_total)
|
|
187
377
|
_update_latency_index(G, hist, n_total, n_latent, t)
|
|
188
|
-
_update_epi_support(G, hist, t, threshold)
|
|
378
|
+
_update_epi_support(G, hist, t, threshold, n_jobs=n_jobs)
|
|
189
379
|
_update_morph_metrics(G, hist, counts, t)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from ..types import (
|
|
4
|
+
GlyphCounts,
|
|
5
|
+
GlyphMetricsHistory,
|
|
6
|
+
GlyphTimingByNode as GlyphTimingByNode,
|
|
7
|
+
GlyphTimingTotals as GlyphTimingTotals,
|
|
8
|
+
GlyphogramRow as GlyphogramRow,
|
|
9
|
+
GraphLike,
|
|
10
|
+
SigmaTrace as SigmaTrace,
|
|
11
|
+
)
|
|
12
|
+
from collections import Counter
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from typing import Any, Callable, Mapping, MutableMapping
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"LATENT_GLYPH",
|
|
18
|
+
"GlyphTiming",
|
|
19
|
+
"SigmaTrace",
|
|
20
|
+
"GlyphogramRow",
|
|
21
|
+
"GlyphTimingTotals",
|
|
22
|
+
"GlyphTimingByNode",
|
|
23
|
+
"_tg_state",
|
|
24
|
+
"for_each_glyph",
|
|
25
|
+
"_update_tg_node",
|
|
26
|
+
"_update_tg",
|
|
27
|
+
"_update_glyphogram",
|
|
28
|
+
"_update_latency_index",
|
|
29
|
+
"_update_epi_support",
|
|
30
|
+
"_update_morph_metrics",
|
|
31
|
+
"_compute_advanced_metrics",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
LATENT_GLYPH: str
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class GlyphTiming:
|
|
38
|
+
curr: str | None = ...
|
|
39
|
+
run: float = ...
|
|
40
|
+
|
|
41
|
+
def _tg_state(nd: MutableMapping[str, Any]) -> GlyphTiming: ...
|
|
42
|
+
def for_each_glyph(fn: Callable[[str], None]) -> None: ...
|
|
43
|
+
def _update_tg_node(
|
|
44
|
+
n: Any,
|
|
45
|
+
nd: MutableMapping[str, Any],
|
|
46
|
+
dt: float,
|
|
47
|
+
tg_total: GlyphTimingTotals,
|
|
48
|
+
tg_by_node: GlyphTimingByNode | None,
|
|
49
|
+
) -> tuple[str | None, bool]: ...
|
|
50
|
+
def _update_tg(
|
|
51
|
+
G: GraphLike,
|
|
52
|
+
hist: GlyphMetricsHistory,
|
|
53
|
+
dt: float,
|
|
54
|
+
save_by_node: bool,
|
|
55
|
+
n_jobs: int | None = None,
|
|
56
|
+
) -> tuple[Counter[str], int, int]: ...
|
|
57
|
+
def _update_glyphogram(
|
|
58
|
+
G: GraphLike, hist: GlyphMetricsHistory, counts: GlyphCounts, t: float, n_total: int
|
|
59
|
+
) -> None: ...
|
|
60
|
+
def _update_latency_index(
|
|
61
|
+
G: GraphLike, hist: GlyphMetricsHistory, n_total: int, n_latent: int, t: float
|
|
62
|
+
) -> None: ...
|
|
63
|
+
def _update_epi_support(
|
|
64
|
+
G: GraphLike,
|
|
65
|
+
hist: GlyphMetricsHistory,
|
|
66
|
+
t: float,
|
|
67
|
+
threshold: float = ...,
|
|
68
|
+
n_jobs: int | None = None,
|
|
69
|
+
) -> None: ...
|
|
70
|
+
def _update_morph_metrics(
|
|
71
|
+
G: GraphLike, hist: GlyphMetricsHistory, counts: GlyphCounts, t: float
|
|
72
|
+
) -> None: ...
|
|
73
|
+
def _compute_advanced_metrics(
|
|
74
|
+
G: GraphLike,
|
|
75
|
+
hist: GlyphMetricsHistory,
|
|
76
|
+
t: float,
|
|
77
|
+
dt: float,
|
|
78
|
+
cfg: Mapping[str, Any],
|
|
79
|
+
threshold: float = ...,
|
|
80
|
+
n_jobs: int | None = None,
|
|
81
|
+
) -> None: ...
|