tnfr 3.0.3__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 +375 -56
- 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 +723 -0
- 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 +171 -0
- tnfr/cache.pyi +13 -0
- tnfr/cli/__init__.py +110 -0
- tnfr/cli/__init__.pyi +26 -0
- tnfr/cli/arguments.py +489 -0
- tnfr/cli/arguments.pyi +29 -0
- tnfr/cli/execution.py +914 -0
- tnfr/cli/execution.pyi +70 -0
- tnfr/cli/interactive_validator.py +614 -0
- tnfr/cli/utils.py +51 -0
- 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/config/constants.py +104 -0
- tnfr/config/constants.pyi +12 -0
- tnfr/config/defaults.py +54 -0
- tnfr/config/defaults_core.py +212 -0
- 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 +92 -0
- tnfr/constants/__init__.pyi +92 -0
- tnfr/constants/aliases.py +33 -0
- tnfr/constants/aliases.pyi +27 -0
- tnfr/constants/init.py +33 -0
- tnfr/constants/init.pyi +12 -0
- tnfr/constants/metric.py +104 -0
- 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 +238 -0
- 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 +3034 -0
- 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 +661 -0
- 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 +36 -0
- 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 +223 -0
- 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 +262 -0
- tnfr/flatten.pyi +21 -0
- tnfr/gamma.py +354 -0
- tnfr/gamma.pyi +36 -0
- tnfr/glyph_history.py +377 -0
- tnfr/glyph_history.pyi +35 -0
- tnfr/glyph_runtime.py +19 -0
- tnfr/glyph_runtime.pyi +8 -0
- tnfr/immutable.py +218 -0
- tnfr/immutable.pyi +36 -0
- tnfr/initialization.py +203 -0
- tnfr/initialization.pyi +65 -0
- tnfr/io.py +10 -0
- tnfr/io.pyi +13 -0
- tnfr/locking.py +37 -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 +79 -0
- 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 +2009 -0
- tnfr/metrics/coherence.pyi +129 -0
- tnfr/metrics/common.py +158 -0
- tnfr/metrics/common.pyi +35 -0
- tnfr/metrics/core.py +316 -0
- tnfr/metrics/core.pyi +13 -0
- tnfr/metrics/diagnosis.py +833 -0
- tnfr/metrics/diagnosis.pyi +86 -0
- tnfr/metrics/emergence.py +245 -0
- tnfr/metrics/export.py +179 -0
- tnfr/metrics/export.pyi +7 -0
- tnfr/metrics/glyph_timing.py +379 -0
- 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 +183 -0
- tnfr/metrics/reporting.pyi +25 -0
- tnfr/metrics/sense_index.py +1203 -0
- tnfr/metrics/sense_index.pyi +9 -0
- tnfr/metrics/trig.py +373 -0
- tnfr/metrics/trig.pyi +13 -0
- tnfr/metrics/trig_cache.py +233 -0
- tnfr/metrics/trig_cache.pyi +10 -0
- tnfr/multiscale/__init__.py +32 -0
- tnfr/multiscale/hierarchical.py +517 -0
- tnfr/node.py +763 -0
- tnfr/node.pyi +139 -0
- tnfr/observers.py +255 -130
- tnfr/observers.pyi +31 -0
- tnfr/ontosim.py +144 -137
- tnfr/ontosim.pyi +28 -0
- tnfr/operators/__init__.py +1672 -0
- 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 +272 -0
- 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 +1809 -0
- 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 +178 -0
- 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 +247 -0
- tnfr/selector.pyi +19 -0
- tnfr/sense.py +378 -0
- 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 +705 -0
- 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 +58 -0
- 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 +543 -0
- 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 +775 -0
- 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/utils/callbacks.py +375 -0
- 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/utils/numeric.py +114 -0
- 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-8.5.0.dist-info/entry_points.txt +3 -0
- tnfr-3.0.3.dist-info/licenses/LICENSE.txt → tnfr-8.5.0.dist-info/licenses/LICENSE.md +1 -1
- tnfr/constants.py +0 -183
- tnfr/dynamics.py +0 -543
- tnfr/helpers.py +0 -198
- tnfr/main.py +0 -37
- tnfr/operators.py +0 -296
- tnfr-3.0.3.dist-info/METADATA +0 -35
- tnfr-3.0.3.dist-info/RECORD +0 -13
- {tnfr-3.0.3.dist-info → tnfr-8.5.0.dist-info}/WHEEL +0 -0
- {tnfr-3.0.3.dist-info → tnfr-8.5.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
"""Glyph timing utilities and advanced metrics."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
from collections import Counter, defaultdict
|
|
7
|
+
from concurrent.futures import ProcessPoolExecutor
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from types import ModuleType
|
|
10
|
+
from typing import (
|
|
11
|
+
Any,
|
|
12
|
+
Callable,
|
|
13
|
+
Mapping,
|
|
14
|
+
MutableMapping,
|
|
15
|
+
Sequence,
|
|
16
|
+
cast,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from ..alias import get_attr
|
|
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
|
|
39
|
+
|
|
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
|
|
44
|
+
|
|
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
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass
|
|
71
|
+
class GlyphTiming:
|
|
72
|
+
"""Mutable accumulator tracking the active glyph and its dwell time."""
|
|
73
|
+
|
|
74
|
+
curr: str | None = None
|
|
75
|
+
run: float = 0.0
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
__all__ = [
|
|
79
|
+
"LATENT_GLYPH",
|
|
80
|
+
"GlyphTiming",
|
|
81
|
+
"SigmaTrace",
|
|
82
|
+
"GlyphogramRow",
|
|
83
|
+
"GlyphTimingTotals",
|
|
84
|
+
"GlyphTimingByNode",
|
|
85
|
+
"_tg_state",
|
|
86
|
+
"for_each_glyph",
|
|
87
|
+
"_update_tg_node",
|
|
88
|
+
"_update_tg",
|
|
89
|
+
"_update_glyphogram",
|
|
90
|
+
"_update_latency_index",
|
|
91
|
+
"_update_epi_support",
|
|
92
|
+
"_update_morph_metrics",
|
|
93
|
+
"_compute_advanced_metrics",
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
# ---------------------------------------------------------------------------
|
|
97
|
+
# Internal utilities
|
|
98
|
+
# ---------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
|
|
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:
|
|
123
|
+
"""Expose per-node glyph timing state."""
|
|
124
|
+
|
|
125
|
+
return nd.setdefault("_Tg", GlyphTiming())
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def for_each_glyph(fn: Callable[[str], None]) -> None:
|
|
129
|
+
"""Apply ``fn`` to each canonical structural operator."""
|
|
130
|
+
|
|
131
|
+
for g in GLYPHS_CANONICAL:
|
|
132
|
+
fn(g)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# ---------------------------------------------------------------------------
|
|
136
|
+
# Glyph timing helpers
|
|
137
|
+
# ---------------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
|
|
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]:
|
|
147
|
+
"""Track a node's glyph transition and accumulate run time."""
|
|
148
|
+
|
|
149
|
+
g = last_glyph(nd)
|
|
150
|
+
if not g:
|
|
151
|
+
return None, False
|
|
152
|
+
st = _tg_state(nd)
|
|
153
|
+
curr = st.curr
|
|
154
|
+
if curr is None:
|
|
155
|
+
st.curr = g
|
|
156
|
+
st.run = dt
|
|
157
|
+
elif g == curr:
|
|
158
|
+
st.run += dt
|
|
159
|
+
else:
|
|
160
|
+
dur = st.run
|
|
161
|
+
tg_total[curr] += dur
|
|
162
|
+
if tg_by_node is not None:
|
|
163
|
+
tg_by_node[n][curr].append(dur)
|
|
164
|
+
st.curr = g
|
|
165
|
+
st.run = dt
|
|
166
|
+
return g, g == LATENT_GLYPH
|
|
167
|
+
|
|
168
|
+
|
|
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]:
|
|
176
|
+
"""Accumulate glyph dwell times for the entire graph."""
|
|
177
|
+
|
|
178
|
+
tg_total = cast(GlyphTimingTotals, hist.setdefault("Tg_total", defaultdict(float)))
|
|
179
|
+
tg_by_node = (
|
|
180
|
+
cast(
|
|
181
|
+
GlyphTimingByNode,
|
|
182
|
+
hist.setdefault(
|
|
183
|
+
"Tg_by_node",
|
|
184
|
+
defaultdict(lambda: defaultdict(list)),
|
|
185
|
+
),
|
|
186
|
+
)
|
|
187
|
+
if save_by_node
|
|
188
|
+
else None
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
n_total = 0
|
|
192
|
+
n_latent = 0
|
|
193
|
+
glyph_sequence: list[str] = []
|
|
194
|
+
for n, nd in G.nodes(data=True):
|
|
195
|
+
g, is_latent = _update_tg_node(n, nd, dt, tg_total, tg_by_node)
|
|
196
|
+
if g is None:
|
|
197
|
+
continue
|
|
198
|
+
n_total += 1
|
|
199
|
+
if is_latent:
|
|
200
|
+
n_latent += 1
|
|
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
|
+
|
|
238
|
+
return counts, n_total, n_latent
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _update_glyphogram(
|
|
242
|
+
G: GraphLike,
|
|
243
|
+
hist: GlyphMetricsHistory,
|
|
244
|
+
counts: GlyphCounts,
|
|
245
|
+
t: float,
|
|
246
|
+
n_total: int,
|
|
247
|
+
) -> None:
|
|
248
|
+
"""Record glyphogram row from glyph counts."""
|
|
249
|
+
|
|
250
|
+
normalize_series = bool(get_param(G, "METRICS").get("normalize_series", False))
|
|
251
|
+
row: GlyphogramRow = {"t": t}
|
|
252
|
+
total = max(1, n_total)
|
|
253
|
+
for g in GLYPHS_CANONICAL:
|
|
254
|
+
c = counts.get(g, 0)
|
|
255
|
+
row[g] = (c / total) if normalize_series else c
|
|
256
|
+
append_metric(cast(MetricsListHistory, hist), "glyphogram", row)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _update_latency_index(
|
|
260
|
+
G: GraphLike,
|
|
261
|
+
hist: GlyphMetricsHistory,
|
|
262
|
+
n_total: int,
|
|
263
|
+
n_latent: int,
|
|
264
|
+
t: float,
|
|
265
|
+
) -> None:
|
|
266
|
+
"""Record latency index for the current step."""
|
|
267
|
+
|
|
268
|
+
li = n_latent / max(1, n_total)
|
|
269
|
+
append_metric(
|
|
270
|
+
cast(MetricsListHistory, hist),
|
|
271
|
+
"latency_index",
|
|
272
|
+
{"t": t, "value": li},
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _update_epi_support(
|
|
277
|
+
G: GraphLike,
|
|
278
|
+
hist: GlyphMetricsHistory,
|
|
279
|
+
t: float,
|
|
280
|
+
threshold: float = DEFAULT_EPI_SUPPORT_LIMIT,
|
|
281
|
+
n_jobs: int | None = None,
|
|
282
|
+
) -> None:
|
|
283
|
+
"""Measure EPI support and normalized magnitude."""
|
|
284
|
+
|
|
285
|
+
node_count = G.number_of_nodes()
|
|
286
|
+
total = 0.0
|
|
287
|
+
count = 0
|
|
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
|
|
330
|
+
epi_norm = (total / count) if count else 0.0
|
|
331
|
+
append_metric(
|
|
332
|
+
cast(MetricsListHistory, hist),
|
|
333
|
+
"EPI_support",
|
|
334
|
+
{"t": t, "size": count, "epi_norm": float(epi_norm)},
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def _update_morph_metrics(
|
|
339
|
+
G: GraphLike,
|
|
340
|
+
hist: GlyphMetricsHistory,
|
|
341
|
+
counts: GlyphCounts,
|
|
342
|
+
t: float,
|
|
343
|
+
) -> None:
|
|
344
|
+
"""Capture morphosyntactic distribution of glyphs."""
|
|
345
|
+
|
|
346
|
+
def get_count(keys: Sequence[str]) -> int:
|
|
347
|
+
return sum(counts.get(k, 0) for k in keys)
|
|
348
|
+
|
|
349
|
+
total = max(1, sum(counts.values()))
|
|
350
|
+
id_val = get_count(GLYPH_GROUPS.get("ID", ())) / total
|
|
351
|
+
cm_val = get_count(GLYPH_GROUPS.get("CM", ())) / total
|
|
352
|
+
ne_val = get_count(GLYPH_GROUPS.get("NE", ())) / total
|
|
353
|
+
num = get_count(GLYPH_GROUPS.get("PP_num", ()))
|
|
354
|
+
den = get_count(GLYPH_GROUPS.get("PP_den", ()))
|
|
355
|
+
pp_val = 0.0 if den == 0 else num / den
|
|
356
|
+
append_metric(
|
|
357
|
+
cast(MetricsListHistory, hist),
|
|
358
|
+
"morph",
|
|
359
|
+
{"t": t, "ID": id_val, "CM": cm_val, "NE": ne_val, "PP": pp_val},
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def _compute_advanced_metrics(
|
|
364
|
+
G: GraphLike,
|
|
365
|
+
hist: GlyphMetricsHistory,
|
|
366
|
+
t: float,
|
|
367
|
+
dt: float,
|
|
368
|
+
cfg: Mapping[str, Any],
|
|
369
|
+
threshold: float = DEFAULT_EPI_SUPPORT_LIMIT,
|
|
370
|
+
n_jobs: int | None = None,
|
|
371
|
+
) -> None:
|
|
372
|
+
"""Compute glyph timing derived metrics."""
|
|
373
|
+
|
|
374
|
+
save_by_node = bool(cfg.get("save_by_node", True))
|
|
375
|
+
counts, n_total, n_latent = _update_tg(G, hist, dt, save_by_node, n_jobs=n_jobs)
|
|
376
|
+
_update_glyphogram(G, hist, counts, t, n_total)
|
|
377
|
+
_update_latency_index(G, hist, n_total, n_latent, t)
|
|
378
|
+
_update_epi_support(G, hist, t, threshold, n_jobs=n_jobs)
|
|
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: ...
|