Skip to content

Commit 48cb4e7

Browse files
authored
Merge pull request #2876 from fermga/copilot/add-collective-coherence-validation
Add collective coherence validation for THOL sub-EPI ensembles
2 parents 6d9955a + ec828f7 commit 48cb4e7

File tree

5 files changed

+533
-1
lines changed

5 files changed

+533
-1
lines changed

docs/THOL_CONFIGURATION_REFERENCE.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Parameters controlling when and how bifurcation (sub-EPI creation) occurs.
2929
| `THOL_MIN_EPI` | 0.2 | [0.05, 0.5] || Minimum EPI magnitude required for structural bifurcation |
3030
| `THOL_MIN_VF` | 0.1 | [0.01, 1.0] | Hz_str | Minimum structural frequency for reorganization capacity |
3131
| `THOL_ACCEL` | 0.10 | [0.01, 0.5] || Acceleration factor in glyph: ΔNFR += `THOL_accel` × d²EPI/dt² |
32+
| `THOL_MIN_COLLECTIVE_COHERENCE` | 0.3 | [0.0, 1.0] || Minimum collective coherence for sub-EPI ensemble. When multiple sub-EPIs exist and coherence < threshold, warning is logged |
3233

3334
**Physical Basis:**
3435

@@ -38,6 +39,12 @@ From the nodal equation ∂EPI/∂t = νf · ΔNFR(t), bifurcation occurs when *
3839
- **EPI_min**: Coherence floor. Nodes below this lack sufficient form to bifurcate coherently.
3940
- **νf_min**: Reorganization capacity floor. Nodes below this are "frozen" and cannot respond.
4041
- **THOL_accel**: Controls how strongly d²EPI/dt² influences ΔNFR in glyph sequences.
42+
- **THOL_MIN_COLLECTIVE_COHERENCE**: Monitors ensemble coherence of sub-EPIs. According to TNFR.pdf §2.2.10, sub-EPIs must form a **coherent ensemble** rather than fragmenting chaotically. Collective coherence is computed as `C = 1/(1 + var(sub_epi_magnitudes))`. Interpretation:
43+
- **> 0.7**: High coherence (structurally solid bifurcation)
44+
- **0.3-0.7**: Moderate (acceptable, monitor)
45+
- **< 0.3**: Low (possible fragmentation, warning logged)
46+
47+
When multiple sub-EPIs exist and coherence falls below threshold, a warning is logged and the event is recorded in `G.graph["thol_coherence_warnings"]` for analysis. This validation is **non-blocking** (warnings only) to allow research into low-coherence dynamics.
4148

4249
**Configuration Example:**
4350
```python
@@ -48,11 +55,21 @@ G = nx.Graph()
4855
G.graph["BIFURCATION_THRESHOLD_TAU"] = 0.8 # High threshold
4956
G.graph["THOL_MIN_EPI"] = 0.3 # Require strong coherence
5057
G.graph["THOL_MIN_VF"] = 0.2 # Require high capacity
58+
G.graph["THOL_MIN_COLLECTIVE_COHERENCE"] = 0.5 # Require coherent ensemble
5159

5260
# Sensitive bifurcation (easier to trigger)
5361
G.graph["BIFURCATION_THRESHOLD_TAU"] = 0.2 # Low threshold
5462
G.graph["THOL_MIN_EPI"] = 0.1 # Lower coherence floor
5563
G.graph["THOL_MIN_VF"] = 0.05 # Lower capacity floor
64+
G.graph["THOL_MIN_COLLECTIVE_COHERENCE"] = 0.2 # More tolerant of fragmentation
65+
66+
# Monitor collective coherence
67+
from tnfr.operators.health_analyzer import SequenceHealthAnalyzer
68+
analyzer = SequenceHealthAnalyzer()
69+
coherence_stats = analyzer.analyze_thol_coherence(G)
70+
if coherence_stats:
71+
print(f"Mean coherence: {coherence_stats['mean_coherence']:.3f}")
72+
print(f"Nodes below threshold: {coherence_stats['nodes_below_threshold']}")
5673
```
5774

5875
---
@@ -668,6 +685,7 @@ G.graph["BIFURCATION_THRESHOLD_TAU"] = 0.5 # Higher threshold
668685
| Version | Date | Changes |
669686
|---------|------|---------|
670687
| 1.0.0 | 2025-11-09 | Initial release: Centralized all THOL parameters with canonical constraints |
688+
| 1.1.0 | 2025-11-09 | Added `THOL_MIN_COLLECTIVE_COHERENCE` parameter for sub-EPI ensemble validation |
671689

672690
---
673691

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)