Skip to content

Commit 4f2a51d

Browse files
Copilotfermga
andcommitted
Add THOL collective coherence validation
Co-authored-by: fermga <203334638+fermga@users.noreply.github.com>
1 parent b441f28 commit 4f2a51d

File tree

4 files changed

+515
-1
lines changed

4 files changed

+515
-1
lines changed

src/tnfr/config/defaults_core.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ class CoreDefaults:
158158
THOL_MIN_DEGREE: int = 1 # Minimum network connectivity
159159
THOL_MIN_HISTORY_LENGTH: int = 3 # Minimum EPI history for acceleration computation
160160
THOL_ALLOW_ISOLATED: bool = False # Require network context by default
161+
THOL_MIN_COLLECTIVE_COHERENCE: float = 0.3 # Minimum collective coherence for sub-EPI ensemble
161162

162163
# VAL (Expansion) precondition thresholds
163164
VAL_MAX_VF: float = 10.0 # Maximum structural frequency threshold

src/tnfr/operators/definitions.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2720,6 +2720,13 @@ def __call__(self, G: TNFRGraph, node: Any, **kw: Any) -> None:
27202720
if d2_epi > tau:
27212721
self._spawn_sub_epi(G, node, d2_epi=d2_epi, tau=tau)
27222722

2723+
# CANONICAL VALIDATION: Verify collective coherence of sub-EPIs
2724+
# When THOL creates multiple sub-EPIs, they must form a coherent ensemble
2725+
# that preserves the structural identity of the parent node (TNFR Manual §2.2.10)
2726+
# Always validate if node has sub-EPIs (whether created now or previously)
2727+
if G.nodes[node].get("sub_epis"):
2728+
self._validate_collective_coherence(G, node)
2729+
27232730
def _compute_epi_acceleration(self, G: TNFRGraph, node: Any) -> float:
27242731
"""Calculate ∂²EPI/∂t² from node's EPI history.
27252732
@@ -2947,6 +2954,71 @@ def _create_sub_node(
29472954

29482955
return sub_node_id
29492956

2957+
def _validate_collective_coherence(self, G: TNFRGraph, node: Any) -> None:
2958+
"""Validate collective coherence of sub-EPI ensemble after bifurcation.
2959+
2960+
When THOL creates multiple sub-EPIs, they must form a coherent ensemble
2961+
that preserves the structural identity of the parent node. This validation
2962+
ensures the emergent sub-structures maintain structural alignment.
2963+
2964+
Parameters
2965+
----------
2966+
G : TNFRGraph
2967+
Graph containing the node
2968+
node : Any
2969+
Node that underwent bifurcation
2970+
2971+
Notes
2972+
-----
2973+
TNFR Canonical Principle (TNFR Manual §2.2.10):
2974+
"THOL reorganiza la forma desde dentro, en respuesta a la coherencia
2975+
vibracional del campo. La autoorganización es resonancia estructurada
2976+
desde el interior del nodo."
2977+
2978+
Implication: Sub-EPIs are not random fragments but coherent structures
2979+
that emerge from internal resonance.
2980+
2981+
This method:
2982+
1. Computes collective coherence of sub-EPI ensemble
2983+
2. Stores coherence value for telemetry
2984+
3. Logs warning if coherence < threshold
2985+
4. Records event for analysis
2986+
2987+
Does NOT fail the operation - allows monitoring and analysis of
2988+
low-coherence bifurcations for research purposes.
2989+
"""
2990+
import logging
2991+
from .metabolism import compute_subepi_collective_coherence
2992+
2993+
# Compute collective coherence
2994+
coherence = compute_subepi_collective_coherence(G, node)
2995+
2996+
# Store for telemetry (always store, even if 0.0 for single/no sub-EPIs)
2997+
G.nodes[node]["_thol_collective_coherence"] = coherence
2998+
2999+
# Get threshold from graph config
3000+
min_coherence = float(G.graph.get("THOL_MIN_COLLECTIVE_COHERENCE", 0.3))
3001+
3002+
# Validate against threshold (only warn if we have multiple sub-EPIs)
3003+
sub_epis = G.nodes[node].get("sub_epis", [])
3004+
if len(sub_epis) >= 2 and coherence < min_coherence:
3005+
# Log warning (but don't fail - allow monitoring)
3006+
logger = logging.getLogger(__name__)
3007+
logger.warning(
3008+
f"Node {node}: THOL collective coherence ({coherence:.3f}) < "
3009+
f"threshold ({min_coherence}). Sub-EPIs may be fragmenting. "
3010+
f"Sub-EPI count: {len(sub_epis)}."
3011+
)
3012+
3013+
# Record event for analysis
3014+
events = G.graph.setdefault("thol_coherence_warnings", [])
3015+
events.append({
3016+
"node": node,
3017+
"coherence": coherence,
3018+
"threshold": min_coherence,
3019+
"sub_epi_count": len(sub_epis),
3020+
})
3021+
29503022
def _validate_preconditions(self, G: TNFRGraph, node: Any) -> None:
29513023
"""Validate THOL-specific preconditions."""
29523024
from .preconditions import validate_self_organization

src/tnfr/operators/health_analyzer.py

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
from __future__ import annotations
88

99
from functools import lru_cache
10-
from typing import List, Tuple
10+
from typing import Any, List, Tuple, TYPE_CHECKING
11+
12+
if TYPE_CHECKING:
13+
from ..types import TNFRGraph
1114

1215
from ..compat.dataclass import dataclass
1316
from ..config.operator_names import (
@@ -725,3 +728,82 @@ def _detect_pattern(self, sequence: List[str]) -> str:
725728
return "exploratory"
726729

727730
return "unknown"
731+
732+
def analyze_thol_coherence(self, G: TNFRGraph) -> dict[str, Any] | None:
733+
"""Analyze collective coherence of THOL bifurcations across the network.
734+
735+
Examines all nodes that have undergone THOL bifurcation and provides
736+
statistics on their collective coherence metrics.
737+
738+
Parameters
739+
----------
740+
G : TNFRGraph
741+
Graph containing nodes with potential THOL bifurcations
742+
743+
Returns
744+
-------
745+
dict or None
746+
Dictionary containing coherence statistics:
747+
- mean_coherence: Average coherence across all THOL nodes
748+
- min_coherence: Lowest coherence value observed
749+
- max_coherence: Highest coherence value observed
750+
- nodes_below_threshold: Count of nodes with coherence < 0.3
751+
- total_thol_nodes: Total nodes with sub-EPIs
752+
Returns None if no THOL bifurcations exist in the network.
753+
754+
Notes
755+
-----
756+
TNFR Principle: Collective coherence measures the structural alignment
757+
of emergent sub-EPIs. Low coherence may indicate chaotic fragmentation
758+
rather than controlled emergence.
759+
760+
This metric is particularly useful for:
761+
- Detecting pathological bifurcation patterns
762+
- Monitoring network-wide self-organization quality
763+
- Identifying nodes requiring stabilization
764+
765+
Examples
766+
--------
767+
>>> analyzer = SequenceHealthAnalyzer()
768+
>>> # After running THOL operations on graph G
769+
>>> coherence_stats = analyzer.analyze_thol_coherence(G)
770+
>>> if coherence_stats:
771+
... print(f"Mean coherence: {coherence_stats['mean_coherence']:.3f}")
772+
... print(f"Nodes below threshold: {coherence_stats['nodes_below_threshold']}")
773+
"""
774+
# Find all nodes with sub-EPIs (THOL bifurcation occurred)
775+
thol_nodes = []
776+
for node in G.nodes():
777+
if G.nodes[node].get("sub_epis"):
778+
thol_nodes.append(node)
779+
780+
if not thol_nodes:
781+
return None
782+
783+
# Collect coherence values
784+
coherences = []
785+
for node in thol_nodes:
786+
coh = G.nodes[node].get("_thol_collective_coherence")
787+
if coh is not None:
788+
coherences.append(coh)
789+
790+
if not coherences:
791+
return None
792+
793+
# Compute statistics
794+
mean_coherence = sum(coherences) / len(coherences)
795+
min_coherence = min(coherences)
796+
max_coherence = max(coherences)
797+
798+
# Get threshold from graph config
799+
threshold = float(G.graph.get("THOL_MIN_COLLECTIVE_COHERENCE", 0.3))
800+
nodes_below_threshold = sum(1 for c in coherences if c < threshold)
801+
802+
return {
803+
"mean_coherence": mean_coherence,
804+
"min_coherence": min_coherence,
805+
"max_coherence": max_coherence,
806+
"nodes_below_threshold": nodes_below_threshold,
807+
"total_thol_nodes": len(thol_nodes),
808+
"threshold": threshold,
809+
}

0 commit comments

Comments
 (0)