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,280 @@
|
|
|
1
|
+
"""Learning metrics for adaptive TNFR dynamics.
|
|
2
|
+
|
|
3
|
+
This module provides metrics specific to adaptive learning processes,
|
|
4
|
+
measuring plasticity, consolidation, and learning efficiency using
|
|
5
|
+
existing TNFR infrastructure (glyph history, EPI, νf, ΔNFR).
|
|
6
|
+
|
|
7
|
+
All functions reuse canonical utilities from glyph_history and alias modules.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Any, Mapping, Sequence
|
|
13
|
+
|
|
14
|
+
from ..alias import get_attr
|
|
15
|
+
from ..constants.aliases import ALIAS_EPI, ALIAS_VF, ALIAS_DNFR
|
|
16
|
+
from ..glyph_history import ensure_history, count_glyphs
|
|
17
|
+
from ..types import TNFRGraph, Glyph
|
|
18
|
+
from ..config.operator_names import (
|
|
19
|
+
COHERENCE,
|
|
20
|
+
DISSONANCE,
|
|
21
|
+
EMISSION,
|
|
22
|
+
MUTATION,
|
|
23
|
+
RECEPTION,
|
|
24
|
+
RECURSIVITY,
|
|
25
|
+
SELF_ORGANIZATION,
|
|
26
|
+
SILENCE,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"compute_learning_plasticity",
|
|
31
|
+
"compute_consolidation_index",
|
|
32
|
+
"compute_learning_efficiency",
|
|
33
|
+
"glyph_history_to_operator_names",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def glyph_history_to_operator_names(glyph_history: Sequence[str]) -> list[str]:
|
|
38
|
+
"""Convert glyph history to operator names for comparison.
|
|
39
|
+
|
|
40
|
+
This is a lightweight helper that converts glyphs (e.g., 'AL', 'EN') to
|
|
41
|
+
canonical operator names (e.g., 'emission', 'reception') using the
|
|
42
|
+
existing GLYPH_TO_FUNCTION mapping.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
glyph_history : Sequence[str]
|
|
47
|
+
Sequence of glyph codes from node's glyph_history.
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
list[str]
|
|
52
|
+
List of canonical operator names.
|
|
53
|
+
|
|
54
|
+
Notes
|
|
55
|
+
-----
|
|
56
|
+
Reuses existing GLYPH_TO_FUNCTION mapping from grammar module.
|
|
57
|
+
Computational cost is O(n) with n = len(glyph_history), just dict lookups.
|
|
58
|
+
|
|
59
|
+
Examples
|
|
60
|
+
--------
|
|
61
|
+
>>> from tnfr.metrics.learning_metrics import glyph_history_to_operator_names
|
|
62
|
+
>>> glyphs = ['AL', 'EN', 'IL']
|
|
63
|
+
>>> names = glyph_history_to_operator_names(glyphs)
|
|
64
|
+
>>> names
|
|
65
|
+
['emission', 'reception', 'coherence']
|
|
66
|
+
"""
|
|
67
|
+
from ..operators.grammar import GLYPH_TO_FUNCTION
|
|
68
|
+
|
|
69
|
+
result = []
|
|
70
|
+
for glyph_str in glyph_history:
|
|
71
|
+
# Convert string to Glyph enum if needed
|
|
72
|
+
try:
|
|
73
|
+
glyph = Glyph(glyph_str)
|
|
74
|
+
operator_name = GLYPH_TO_FUNCTION.get(glyph, glyph_str.lower())
|
|
75
|
+
result.append(operator_name)
|
|
76
|
+
except (ValueError, KeyError):
|
|
77
|
+
# If glyph is not recognized, keep as-is lowercased
|
|
78
|
+
result.append(glyph_str.lower())
|
|
79
|
+
|
|
80
|
+
return result
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def compute_learning_plasticity(
|
|
84
|
+
G: TNFRGraph,
|
|
85
|
+
node: Any,
|
|
86
|
+
window: int = 10,
|
|
87
|
+
) -> float:
|
|
88
|
+
"""Measure capacity for structural reorganization (learning plasticity).
|
|
89
|
+
|
|
90
|
+
High plasticity indicates high capacity for learning and adaptation.
|
|
91
|
+
Plasticity is measured by the frequency of reorganization operators
|
|
92
|
+
(OZ, THOL, ZHIR) in the recent glyph history.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
G : TNFRGraph
|
|
97
|
+
Graph storing TNFR nodes and their structural operator history.
|
|
98
|
+
node : Any
|
|
99
|
+
Node identifier within G.
|
|
100
|
+
window : int, default=10
|
|
101
|
+
Number of recent glyphs to analyze.
|
|
102
|
+
|
|
103
|
+
Returns
|
|
104
|
+
-------
|
|
105
|
+
float
|
|
106
|
+
Plasticity index in range [0.0, 1.0] where higher values indicate
|
|
107
|
+
greater reorganization capacity.
|
|
108
|
+
|
|
109
|
+
Notes
|
|
110
|
+
-----
|
|
111
|
+
Reuses canonical glyph_history functions for tracking operator emissions.
|
|
112
|
+
Plasticity operators: OZ (Dissonance), THOL (Self-organization),
|
|
113
|
+
ZHIR (Mutation) - these represent structural flexibility.
|
|
114
|
+
|
|
115
|
+
Examples
|
|
116
|
+
--------
|
|
117
|
+
>>> from tnfr.structural import create_nfr, run_sequence
|
|
118
|
+
>>> from tnfr.operators.definitions import Dissonance, SelfOrganization
|
|
119
|
+
>>> from tnfr.metrics.learning_metrics import compute_learning_plasticity
|
|
120
|
+
>>> G, node = create_nfr("learner", epi=0.3, vf=1.0)
|
|
121
|
+
>>> # Apply reorganization operators
|
|
122
|
+
>>> run_sequence(G, node, [Dissonance(), SelfOrganization()])
|
|
123
|
+
>>> plasticity = compute_learning_plasticity(G, node, window=10)
|
|
124
|
+
>>> plasticity > 0.0 # Should show some plasticity
|
|
125
|
+
True
|
|
126
|
+
"""
|
|
127
|
+
# Get glyph history directly from node
|
|
128
|
+
history = G.nodes[node].get("glyph_history", [])
|
|
129
|
+
if not history:
|
|
130
|
+
return 0.0
|
|
131
|
+
|
|
132
|
+
# Convert deque to list if needed
|
|
133
|
+
if hasattr(history, "__iter__"):
|
|
134
|
+
history = list(history)
|
|
135
|
+
else:
|
|
136
|
+
return 0.0
|
|
137
|
+
|
|
138
|
+
# Limit to window size
|
|
139
|
+
if window > 0 and len(history) > window:
|
|
140
|
+
history = history[-window:]
|
|
141
|
+
|
|
142
|
+
# Convert glyphs to operator names using canonical function
|
|
143
|
+
operator_names = glyph_history_to_operator_names(history)
|
|
144
|
+
|
|
145
|
+
# Count plastic operators: those that enable reorganization
|
|
146
|
+
plastic_ops = {DISSONANCE, SELF_ORGANIZATION, MUTATION}
|
|
147
|
+
plastic_count = sum(1 for op_name in operator_names if op_name in plastic_ops)
|
|
148
|
+
|
|
149
|
+
# Normalize by history length
|
|
150
|
+
return plastic_count / max(len(operator_names), 1)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def compute_consolidation_index(
|
|
154
|
+
G: TNFRGraph,
|
|
155
|
+
node: Any,
|
|
156
|
+
window: int = 10,
|
|
157
|
+
) -> float:
|
|
158
|
+
"""Measure structural stabilization after learning (consolidation).
|
|
159
|
+
|
|
160
|
+
High consolidation indicates that learning has been stabilized and
|
|
161
|
+
integrated. Consolidation is measured by the frequency of stabilization
|
|
162
|
+
operators (IL, SHA) in the recent glyph history.
|
|
163
|
+
|
|
164
|
+
Parameters
|
|
165
|
+
----------
|
|
166
|
+
G : TNFRGraph
|
|
167
|
+
Graph storing TNFR nodes and their structural operator history.
|
|
168
|
+
node : Any
|
|
169
|
+
Node identifier within G.
|
|
170
|
+
window : int, default=10
|
|
171
|
+
Number of recent glyphs to analyze.
|
|
172
|
+
|
|
173
|
+
Returns
|
|
174
|
+
-------
|
|
175
|
+
float
|
|
176
|
+
Consolidation index in range [0.0, 1.0] where higher values indicate
|
|
177
|
+
greater stabilization of learned patterns.
|
|
178
|
+
|
|
179
|
+
Notes
|
|
180
|
+
-----
|
|
181
|
+
Reuses canonical glyph_history functions for tracking operator emissions.
|
|
182
|
+
Consolidation operators: IL (Coherence), SHA (Silence) - these represent
|
|
183
|
+
structural stabilization.
|
|
184
|
+
|
|
185
|
+
Examples
|
|
186
|
+
--------
|
|
187
|
+
>>> from tnfr.structural import create_nfr, run_sequence
|
|
188
|
+
>>> from tnfr.operators.definitions import Coherence, Silence
|
|
189
|
+
>>> from tnfr.metrics.learning_metrics import compute_consolidation_index
|
|
190
|
+
>>> G, node = create_nfr("learner", epi=0.5, vf=1.0)
|
|
191
|
+
>>> # Apply consolidation operators
|
|
192
|
+
>>> run_sequence(G, node, [Coherence(), Silence()])
|
|
193
|
+
>>> consolidation = compute_consolidation_index(G, node, window=10)
|
|
194
|
+
>>> consolidation > 0.0 # Should show some consolidation
|
|
195
|
+
True
|
|
196
|
+
"""
|
|
197
|
+
# Get glyph history directly from node
|
|
198
|
+
history = G.nodes[node].get("glyph_history", [])
|
|
199
|
+
if not history:
|
|
200
|
+
return 0.0
|
|
201
|
+
|
|
202
|
+
# Convert deque to list if needed
|
|
203
|
+
if hasattr(history, "__iter__"):
|
|
204
|
+
history = list(history)
|
|
205
|
+
else:
|
|
206
|
+
return 0.0
|
|
207
|
+
|
|
208
|
+
# Limit to window size
|
|
209
|
+
if window > 0 and len(history) > window:
|
|
210
|
+
history = history[-window:]
|
|
211
|
+
|
|
212
|
+
# Convert glyphs to operator names using canonical function
|
|
213
|
+
operator_names = glyph_history_to_operator_names(history)
|
|
214
|
+
|
|
215
|
+
# Count stable operators: those that consolidate structure
|
|
216
|
+
stable_ops = {COHERENCE, SILENCE, RECURSIVITY}
|
|
217
|
+
stable_count = sum(1 for op_name in operator_names if op_name in stable_ops)
|
|
218
|
+
|
|
219
|
+
# Normalize by history length
|
|
220
|
+
return stable_count / max(len(operator_names), 1)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def compute_learning_efficiency(
|
|
224
|
+
G: TNFRGraph,
|
|
225
|
+
node: Any,
|
|
226
|
+
) -> float:
|
|
227
|
+
"""Measure learning efficiency (EPI change per operator applied).
|
|
228
|
+
|
|
229
|
+
Efficiency represents how much structural change (ΔEPI) occurs per
|
|
230
|
+
operator application, indicating effective learning without excessive
|
|
231
|
+
reorganization.
|
|
232
|
+
|
|
233
|
+
Parameters
|
|
234
|
+
----------
|
|
235
|
+
G : TNFRGraph
|
|
236
|
+
Graph storing TNFR nodes and their structural state.
|
|
237
|
+
node : Any
|
|
238
|
+
Node identifier within G.
|
|
239
|
+
|
|
240
|
+
Returns
|
|
241
|
+
-------
|
|
242
|
+
float
|
|
243
|
+
Learning efficiency as ΔEPI / number_of_operators. Higher values
|
|
244
|
+
indicate more efficient learning (more change with fewer operations).
|
|
245
|
+
|
|
246
|
+
Notes
|
|
247
|
+
-----
|
|
248
|
+
Reuses canonical alias functions (get_attr) for accessing node attributes.
|
|
249
|
+
Requires node to have 'epi_initial' attribute set at creation time to
|
|
250
|
+
measure total EPI change.
|
|
251
|
+
|
|
252
|
+
Examples
|
|
253
|
+
--------
|
|
254
|
+
>>> from tnfr.structural import create_nfr, run_sequence
|
|
255
|
+
>>> from tnfr.operators.definitions import Emission, Reception
|
|
256
|
+
>>> from tnfr.metrics.learning_metrics import compute_learning_efficiency
|
|
257
|
+
>>> G, node = create_nfr("learner", epi=0.2, vf=1.0)
|
|
258
|
+
>>> G.nodes[node]["epi_initial"] = 0.2 # Track initial state
|
|
259
|
+
>>> # Apply learning operators
|
|
260
|
+
>>> run_sequence(G, node, [Emission(), Reception()])
|
|
261
|
+
>>> efficiency = compute_learning_efficiency(G, node)
|
|
262
|
+
>>> efficiency >= 0.0 # Should be non-negative
|
|
263
|
+
True
|
|
264
|
+
"""
|
|
265
|
+
# Get current EPI using canonical get_attr
|
|
266
|
+
epi_current = float(get_attr(G.nodes[node], ALIAS_EPI, 0.0))
|
|
267
|
+
|
|
268
|
+
# Get initial EPI (should be set at node creation)
|
|
269
|
+
epi_initial = G.nodes[node].get("epi_initial", 0.0)
|
|
270
|
+
|
|
271
|
+
# Get number of operators applied from glyph history
|
|
272
|
+
history = G.nodes[node].get("glyph_history", [])
|
|
273
|
+
num_ops = len(history) if history else 0
|
|
274
|
+
|
|
275
|
+
if num_ops == 0:
|
|
276
|
+
return 0.0
|
|
277
|
+
|
|
278
|
+
# Calculate efficiency: total EPI change per operator
|
|
279
|
+
delta_epi = abs(epi_current - epi_initial)
|
|
280
|
+
return delta_epi / num_ops
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Type stubs for learning metrics module."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Sequence
|
|
4
|
+
|
|
5
|
+
from ..types import TNFRGraph
|
|
6
|
+
|
|
7
|
+
def glyph_history_to_operator_names(glyph_history: Sequence[str]) -> list[str]: ...
|
|
8
|
+
def compute_learning_plasticity(
|
|
9
|
+
G: TNFRGraph,
|
|
10
|
+
node: Any,
|
|
11
|
+
window: int = ...,
|
|
12
|
+
) -> float: ...
|
|
13
|
+
def compute_consolidation_index(
|
|
14
|
+
G: TNFRGraph,
|
|
15
|
+
node: Any,
|
|
16
|
+
window: int = ...,
|
|
17
|
+
) -> float: ...
|
|
18
|
+
def compute_learning_efficiency(
|
|
19
|
+
G: TNFRGraph,
|
|
20
|
+
node: Any,
|
|
21
|
+
) -> float: ...
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"""Phase coherence metrics for TNFR networks.
|
|
2
|
+
|
|
3
|
+
This module provides phase alignment and synchronization metrics based on
|
|
4
|
+
circular statistics and the Kuramoto order parameter. These metrics are
|
|
5
|
+
essential for measuring the effectiveness of the IL (Coherence) operator's
|
|
6
|
+
phase locking mechanism.
|
|
7
|
+
|
|
8
|
+
Mathematical Foundation
|
|
9
|
+
-----------------------
|
|
10
|
+
|
|
11
|
+
**Kuramoto Order Parameter:**
|
|
12
|
+
|
|
13
|
+
The phase alignment quality is measured using the Kuramoto order parameter r:
|
|
14
|
+
|
|
15
|
+
.. math::
|
|
16
|
+
r = |\\frac{1}{N} \\sum_{j=1}^{N} e^{i\\theta_j}|
|
|
17
|
+
|
|
18
|
+
where:
|
|
19
|
+
- r ∈ [0, 1]
|
|
20
|
+
- r = 1: Perfect phase synchrony (all nodes aligned)
|
|
21
|
+
- r = 0: Complete phase disorder (uniformly distributed phases)
|
|
22
|
+
- θ_j: Phase of node j in radians
|
|
23
|
+
|
|
24
|
+
**Circular Mean:**
|
|
25
|
+
|
|
26
|
+
The mean phase of a set of angles is computed using the circular mean to
|
|
27
|
+
properly handle phase wrap-around at 2π:
|
|
28
|
+
|
|
29
|
+
.. math::
|
|
30
|
+
\\theta_{mean} = \\text{arg}\\left(\\frac{1}{N} \\sum_{j=1}^{N} e^{i\\theta_j}\\right)
|
|
31
|
+
|
|
32
|
+
This ensures that phases near 0 and 2π are correctly averaged (e.g., 0.1 and
|
|
33
|
+
6.2 radians average to near 0, not π).
|
|
34
|
+
|
|
35
|
+
TNFR Context
|
|
36
|
+
------------
|
|
37
|
+
|
|
38
|
+
Phase alignment is a key component of the IL (Coherence) operator:
|
|
39
|
+
|
|
40
|
+
- **IL Phase Locking**: θ_node → θ_node + α * (θ_network - θ_node)
|
|
41
|
+
- **Network Synchrony**: High r indicates effective IL application
|
|
42
|
+
- **Local vs. Global**: Phase alignment can be measured at node or network level
|
|
43
|
+
- **Structural Traceability**: Phase metrics enable telemetry of synchronization
|
|
44
|
+
|
|
45
|
+
Examples
|
|
46
|
+
--------
|
|
47
|
+
|
|
48
|
+
**Compute phase alignment for a node:**
|
|
49
|
+
|
|
50
|
+
>>> import networkx as nx
|
|
51
|
+
>>> from tnfr.metrics.phase_coherence import compute_phase_alignment
|
|
52
|
+
>>> from tnfr.constants import THETA_PRIMARY
|
|
53
|
+
>>> G = nx.Graph()
|
|
54
|
+
>>> G.add_edges_from([(1, 2), (2, 3)])
|
|
55
|
+
>>> G.nodes[1][THETA_PRIMARY] = 0.0
|
|
56
|
+
>>> G.nodes[2][THETA_PRIMARY] = 0.1
|
|
57
|
+
>>> G.nodes[3][THETA_PRIMARY] = 0.2
|
|
58
|
+
>>> alignment = compute_phase_alignment(G, 2, radius=1)
|
|
59
|
+
>>> 0.0 <= alignment <= 1.0
|
|
60
|
+
True
|
|
61
|
+
|
|
62
|
+
**Compute global phase coherence:**
|
|
63
|
+
|
|
64
|
+
>>> from tnfr.metrics.phase_coherence import compute_global_phase_coherence
|
|
65
|
+
>>> coherence = compute_global_phase_coherence(G)
|
|
66
|
+
>>> 0.0 <= coherence <= 1.0
|
|
67
|
+
True
|
|
68
|
+
|
|
69
|
+
See Also
|
|
70
|
+
--------
|
|
71
|
+
|
|
72
|
+
operators.definitions.Coherence : IL operator that applies phase locking
|
|
73
|
+
metrics.coherence.compute_global_coherence : Global structural coherence C(t)
|
|
74
|
+
observers.kuramoto_order : Alternative Kuramoto order parameter implementation
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
from __future__ import annotations
|
|
78
|
+
|
|
79
|
+
import cmath
|
|
80
|
+
from typing import TYPE_CHECKING, Any
|
|
81
|
+
|
|
82
|
+
if TYPE_CHECKING:
|
|
83
|
+
from ..types import TNFRGraph, NodeId
|
|
84
|
+
|
|
85
|
+
from ..alias import get_attr
|
|
86
|
+
from ..constants.aliases import ALIAS_THETA
|
|
87
|
+
from ..utils import get_numpy
|
|
88
|
+
|
|
89
|
+
__all__ = [
|
|
90
|
+
"compute_phase_alignment",
|
|
91
|
+
"compute_global_phase_coherence",
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def compute_phase_alignment(G: TNFRGraph, node: Any, radius: int = 1) -> float:
|
|
96
|
+
"""Compute phase alignment quality for node and neighborhood.
|
|
97
|
+
|
|
98
|
+
Uses Kuramoto order parameter r = |⟨e^(iθ)⟩| to measure phase synchrony
|
|
99
|
+
within a node's neighborhood. Higher values indicate better phase alignment,
|
|
100
|
+
which is the goal of IL (Coherence) phase locking.
|
|
101
|
+
|
|
102
|
+
Parameters
|
|
103
|
+
----------
|
|
104
|
+
G : TNFRGraph
|
|
105
|
+
Network graph with node phase attributes (θ)
|
|
106
|
+
node : Any
|
|
107
|
+
Central node for local phase alignment computation
|
|
108
|
+
radius : int, default=1
|
|
109
|
+
Neighborhood radius:
|
|
110
|
+
- 1 = node + immediate neighbors (default)
|
|
111
|
+
- 2 = node + neighbors + neighbors-of-neighbors
|
|
112
|
+
- etc.
|
|
113
|
+
|
|
114
|
+
Returns
|
|
115
|
+
-------
|
|
116
|
+
float
|
|
117
|
+
Phase alignment in [0, 1] where:
|
|
118
|
+
- 1.0 = Perfect phase synchrony (all phases aligned)
|
|
119
|
+
- 0.0 = Complete phase disorder (uniformly distributed)
|
|
120
|
+
|
|
121
|
+
Notes
|
|
122
|
+
-----
|
|
123
|
+
**Mathematical Foundation:**
|
|
124
|
+
|
|
125
|
+
Kuramoto order parameter for local neighborhood:
|
|
126
|
+
|
|
127
|
+
.. math::
|
|
128
|
+
r = |\\frac{1}{N} \\sum_{j \\in \\mathcal{N}(i)} e^{i\\theta_j}|
|
|
129
|
+
|
|
130
|
+
where 𝒩(i) is the set of neighbors within `radius` of node i (including i).
|
|
131
|
+
|
|
132
|
+
**Use Cases:**
|
|
133
|
+
|
|
134
|
+
- **IL Effectiveness**: Measure phase locking success after IL application
|
|
135
|
+
- **Synchrony Monitoring**: Track local phase coherence over time
|
|
136
|
+
- **Hotspot Detection**: Identify regions with poor phase alignment
|
|
137
|
+
- **Coupling Validation**: Verify phase prerequisites before UM (Coupling)
|
|
138
|
+
|
|
139
|
+
**Special Cases:**
|
|
140
|
+
|
|
141
|
+
- Isolated node (no neighbors): Returns 1.0 (trivially synchronized)
|
|
142
|
+
- Single neighbor: Returns 1.0 (two nodes always "aligned")
|
|
143
|
+
- Empty neighborhood: Returns 1.0 (no disorder by definition)
|
|
144
|
+
|
|
145
|
+
**TNFR Context:**
|
|
146
|
+
|
|
147
|
+
Phase alignment is a precondition for effective coupling (UM operator) and
|
|
148
|
+
resonance (RA operator). The IL operator increases phase alignment through
|
|
149
|
+
its phase locking mechanism: θ_node → θ_node + α * (θ_network - θ_node).
|
|
150
|
+
|
|
151
|
+
See Also
|
|
152
|
+
--------
|
|
153
|
+
compute_global_phase_coherence : Network-wide phase coherence
|
|
154
|
+
operators.definitions.Coherence : IL operator with phase locking
|
|
155
|
+
|
|
156
|
+
Examples
|
|
157
|
+
--------
|
|
158
|
+
>>> import networkx as nx
|
|
159
|
+
>>> from tnfr.metrics.phase_coherence import compute_phase_alignment
|
|
160
|
+
>>> from tnfr.constants import THETA_PRIMARY
|
|
161
|
+
>>> G = nx.Graph()
|
|
162
|
+
>>> G.add_edges_from([(1, 2), (2, 3), (3, 4)])
|
|
163
|
+
>>> # Highly aligned phases
|
|
164
|
+
>>> for n in [1, 2, 3, 4]:
|
|
165
|
+
... G.nodes[n][THETA_PRIMARY] = 0.1 * n # Small differences
|
|
166
|
+
>>> r = compute_phase_alignment(G, node=2, radius=1)
|
|
167
|
+
>>> r > 0.9 # Should be highly aligned
|
|
168
|
+
True
|
|
169
|
+
>>> # Disordered phases
|
|
170
|
+
>>> import numpy as np
|
|
171
|
+
>>> for n in [1, 2, 3, 4]:
|
|
172
|
+
... G.nodes[n][THETA_PRIMARY] = np.random.uniform(0, 2*np.pi)
|
|
173
|
+
>>> r = compute_phase_alignment(G, node=2, radius=1)
|
|
174
|
+
>>> 0.0 <= r <= 1.0 # Could be anywhere in range
|
|
175
|
+
True
|
|
176
|
+
"""
|
|
177
|
+
import networkx as nx
|
|
178
|
+
|
|
179
|
+
# Get neighborhood
|
|
180
|
+
if radius == 1:
|
|
181
|
+
neighbors = set(G.neighbors(node)) | {node}
|
|
182
|
+
else:
|
|
183
|
+
try:
|
|
184
|
+
neighbors = set(
|
|
185
|
+
nx.single_source_shortest_path_length(G, node, cutoff=radius).keys()
|
|
186
|
+
)
|
|
187
|
+
except (nx.NetworkXError, KeyError):
|
|
188
|
+
# Node not in graph or graph is empty
|
|
189
|
+
neighbors = {node} if node in G.nodes else set()
|
|
190
|
+
|
|
191
|
+
# Collect phases from neighborhood
|
|
192
|
+
phases = []
|
|
193
|
+
for n in neighbors:
|
|
194
|
+
try:
|
|
195
|
+
theta = float(get_attr(G.nodes[n], ALIAS_THETA, 0.0))
|
|
196
|
+
phases.append(theta)
|
|
197
|
+
except (KeyError, ValueError, TypeError):
|
|
198
|
+
# Skip nodes with invalid phase data
|
|
199
|
+
continue
|
|
200
|
+
|
|
201
|
+
# Handle edge cases
|
|
202
|
+
if not phases:
|
|
203
|
+
return 1.0 # Empty neighborhood: trivially synchronized
|
|
204
|
+
|
|
205
|
+
if len(phases) == 1:
|
|
206
|
+
return 1.0 # Single node: perfect synchrony
|
|
207
|
+
|
|
208
|
+
# Compute Kuramoto order parameter using circular statistics
|
|
209
|
+
np = get_numpy()
|
|
210
|
+
|
|
211
|
+
if np is not None:
|
|
212
|
+
# NumPy vectorized computation
|
|
213
|
+
phases_array = np.array(phases)
|
|
214
|
+
complex_phases = np.exp(1j * phases_array)
|
|
215
|
+
mean_complex = np.mean(complex_phases)
|
|
216
|
+
r = np.abs(mean_complex)
|
|
217
|
+
return float(r)
|
|
218
|
+
else:
|
|
219
|
+
# Pure Python fallback
|
|
220
|
+
|
|
221
|
+
# Convert phases to complex exponentials
|
|
222
|
+
complex_phases = [cmath.exp(1j * theta) for theta in phases]
|
|
223
|
+
|
|
224
|
+
# Compute mean complex phasor
|
|
225
|
+
mean_real = sum(z.real for z in complex_phases) / len(complex_phases)
|
|
226
|
+
mean_imag = sum(z.imag for z in complex_phases) / len(complex_phases)
|
|
227
|
+
mean_complex = complex(mean_real, mean_imag)
|
|
228
|
+
|
|
229
|
+
# Kuramoto order parameter is magnitude of mean phasor
|
|
230
|
+
r = abs(mean_complex)
|
|
231
|
+
return float(r)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def compute_global_phase_coherence(G: TNFRGraph) -> float:
|
|
235
|
+
"""Compute global phase coherence across entire network.
|
|
236
|
+
|
|
237
|
+
Measures network-wide phase synchronization using the Kuramoto order
|
|
238
|
+
parameter applied to all nodes. This is the global analog of
|
|
239
|
+
compute_phase_alignment and indicates overall phase alignment quality.
|
|
240
|
+
|
|
241
|
+
Parameters
|
|
242
|
+
----------
|
|
243
|
+
G : TNFRGraph
|
|
244
|
+
Network graph with node phase attributes (θ)
|
|
245
|
+
|
|
246
|
+
Returns
|
|
247
|
+
-------
|
|
248
|
+
float
|
|
249
|
+
Global phase coherence in [0, 1] where:
|
|
250
|
+
- 1.0 = Perfect network-wide phase synchrony
|
|
251
|
+
- 0.0 = Complete phase disorder across network
|
|
252
|
+
|
|
253
|
+
Notes
|
|
254
|
+
-----
|
|
255
|
+
**Mathematical Foundation:**
|
|
256
|
+
|
|
257
|
+
Global Kuramoto order parameter:
|
|
258
|
+
|
|
259
|
+
.. math::
|
|
260
|
+
r_{global} = |\\frac{1}{N} \\sum_{j=1}^{N} e^{i\\theta_j}|
|
|
261
|
+
|
|
262
|
+
where N is the total number of nodes in the network.
|
|
263
|
+
|
|
264
|
+
**Use Cases:**
|
|
265
|
+
|
|
266
|
+
- **IL Effectiveness**: Measure global impact of IL phase locking
|
|
267
|
+
- **Network Health**: Monitor overall synchronization state
|
|
268
|
+
- **Convergence Tracking**: Verify phase alignment over time
|
|
269
|
+
- **Bifurcation Detection**: Low r_global may indicate impending split
|
|
270
|
+
|
|
271
|
+
**Special Cases:**
|
|
272
|
+
|
|
273
|
+
- Empty network: Returns 1.0 (no disorder by definition)
|
|
274
|
+
- Single node: Returns 1.0 (trivially synchronized)
|
|
275
|
+
- All phases = 0: Returns 1.0 (perfect alignment)
|
|
276
|
+
|
|
277
|
+
**TNFR Context:**
|
|
278
|
+
|
|
279
|
+
Global phase coherence is a key metric for network structural health.
|
|
280
|
+
Repeated IL application should increase r_global as nodes synchronize
|
|
281
|
+
their phases. Combined with C(t) (structural coherence), r_global provides
|
|
282
|
+
a complete picture of network stability.
|
|
283
|
+
|
|
284
|
+
See Also
|
|
285
|
+
--------
|
|
286
|
+
compute_phase_alignment : Local phase alignment for node neighborhoods
|
|
287
|
+
metrics.coherence.compute_global_coherence : Global structural coherence C(t)
|
|
288
|
+
observers.kuramoto_order : Alternative Kuramoto implementation
|
|
289
|
+
|
|
290
|
+
Examples
|
|
291
|
+
--------
|
|
292
|
+
>>> import networkx as nx
|
|
293
|
+
>>> from tnfr.metrics.phase_coherence import compute_global_phase_coherence
|
|
294
|
+
>>> from tnfr.constants import THETA_PRIMARY
|
|
295
|
+
>>> G = nx.Graph()
|
|
296
|
+
>>> G.add_nodes_from([1, 2, 3, 4])
|
|
297
|
+
>>> # Aligned network
|
|
298
|
+
>>> for n in [1, 2, 3, 4]:
|
|
299
|
+
... G.nodes[n][THETA_PRIMARY] = 0.5 # All same phase
|
|
300
|
+
>>> r = compute_global_phase_coherence(G)
|
|
301
|
+
>>> r == 1.0 # Perfect alignment
|
|
302
|
+
True
|
|
303
|
+
>>> # Disordered network
|
|
304
|
+
>>> import numpy as np
|
|
305
|
+
>>> for n in [1, 2, 3, 4]:
|
|
306
|
+
... G.nodes[n][THETA_PRIMARY] = np.random.uniform(0, 2*np.pi)
|
|
307
|
+
>>> r = compute_global_phase_coherence(G)
|
|
308
|
+
>>> 0.0 <= r <= 1.0
|
|
309
|
+
True
|
|
310
|
+
"""
|
|
311
|
+
# Collect all node phases
|
|
312
|
+
phases = []
|
|
313
|
+
for n in G.nodes():
|
|
314
|
+
try:
|
|
315
|
+
theta = float(get_attr(G.nodes[n], ALIAS_THETA, 0.0))
|
|
316
|
+
phases.append(theta)
|
|
317
|
+
except (KeyError, ValueError, TypeError):
|
|
318
|
+
# Skip nodes with invalid phase data
|
|
319
|
+
continue
|
|
320
|
+
|
|
321
|
+
# Handle edge cases
|
|
322
|
+
if not phases:
|
|
323
|
+
return 1.0 # Empty network: trivially synchronized
|
|
324
|
+
|
|
325
|
+
if len(phases) == 1:
|
|
326
|
+
return 1.0 # Single node: perfect synchrony
|
|
327
|
+
|
|
328
|
+
# Compute Kuramoto order parameter using circular statistics
|
|
329
|
+
np = get_numpy()
|
|
330
|
+
|
|
331
|
+
if np is not None:
|
|
332
|
+
# NumPy vectorized computation
|
|
333
|
+
phases_array = np.array(phases)
|
|
334
|
+
complex_phases = np.exp(1j * phases_array)
|
|
335
|
+
mean_complex = np.mean(complex_phases)
|
|
336
|
+
r = np.abs(mean_complex)
|
|
337
|
+
return float(r)
|
|
338
|
+
else:
|
|
339
|
+
# Pure Python fallback
|
|
340
|
+
|
|
341
|
+
# Convert phases to complex exponentials
|
|
342
|
+
complex_phases = [cmath.exp(1j * theta) for theta in phases]
|
|
343
|
+
|
|
344
|
+
# Compute mean complex phasor
|
|
345
|
+
mean_real = sum(z.real for z in complex_phases) / len(complex_phases)
|
|
346
|
+
mean_imag = sum(z.imag for z in complex_phases) / len(complex_phases)
|
|
347
|
+
mean_complex = complex(mean_real, mean_imag)
|
|
348
|
+
|
|
349
|
+
# Kuramoto order parameter is magnitude of mean phasor
|
|
350
|
+
r = abs(mean_complex)
|
|
351
|
+
return float(r)
|