Skip to content

Commit 827b6cd

Browse files
authored
Merge pull request #2801 from fermga/copilot/add-nu-f-amplification-coherence
RA operator: Add νf amplification, phase alignment strengthening, and network coherence propagation
2 parents c8a60c2 + 6a9b3ef commit 827b6cd

File tree

5 files changed

+623
-12
lines changed

5 files changed

+623
-12
lines changed

src/tnfr/config/defaults_core.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ class CoreDefaults:
8686
"UM_vf_sync": 0.10,
8787
"UM_dnfr_reduction": 0.15,
8888
"RA_epi_diff": 0.15,
89+
"RA_vf_amplification": 0.05,
90+
"RA_phase_coupling": 0.10, # Canonical phase alignment strengthening
8991
"SHA_vf_factor": 0.85,
9092
# Conservative scaling (1.05) prevents EPI overflow near boundaries
9193
# while maintaining meaningful expansion capacity. Critical threshold:

src/tnfr/operators/__init__.py

Lines changed: 164 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -768,37 +768,194 @@ def _op_UM(node: NodeProtocol, gf: GlyphFactors) -> None: # UM — Coupling
768768

769769

770770
def _op_RA(node: NodeProtocol, gf: GlyphFactors) -> None: # RA — Resonance
771-
"""Diffuse EPI to the node through the Resonance glyph.
771+
"""Propagate coherence through resonance with νf amplification.
772772
773-
Resonance propagates EPI along existing couplings without affecting νf,
774-
ΔNFR, or phase. The glyph nudges the node towards the neighbour mean using
775-
``RA_epi_diff``.
773+
Resonance (RA) propagates EPI along existing couplings while amplifying
774+
the structural frequency (νf) to reflect network coherence propagation.
775+
According to TNFR theory, RA creates "resonant cascades" where coherence
776+
amplifies across the network, increasing collective νf and global C(t).
777+
778+
**Canonical Effects (always active):**
779+
780+
- **EPI Propagation**: Diffuses EPI to neighbors (identity-preserving)
781+
- **νf Amplification**: Increases structural frequency when propagating coherence
782+
- **Phase Alignment**: Strengthens phase synchrony across propagation path
783+
- **Network C(t)**: Contributes to global coherence increase
784+
- **Identity Preservation**: Maintains structural identity during propagation
776785
777786
Parameters
778787
----------
779788
node : NodeProtocol
780789
Node harmonising with its neighbourhood.
781790
gf : GlyphFactors
782-
Provides ``RA_epi_diff`` as the mixing coefficient.
791+
Provides ``RA_epi_diff`` (mixing coefficient, default 0.15),
792+
``RA_vf_amplification`` (νf boost factor, default 0.05), and
793+
``RA_phase_coupling`` (phase alignment factor, default 0.10).
794+
795+
Notes
796+
-----
797+
**νf Amplification (Canonical)**: When neighbors have coherence (|epi_bar| > 1e-9),
798+
node.vf is multiplied by (1.0 + RA_vf_amplification). This reflects
799+
the canonical TNFR property that resonance amplifies collective νf.
800+
This is NOT optional - it is a fundamental property of resonance per TNFR theory.
801+
802+
**Phase Alignment Strengthening (Canonical)**: RA strengthens phase alignment
803+
with neighbors by applying a small phase correction toward the network mean.
804+
This ensures that "Phase alignment: Strengthens across propagation path" as
805+
stated in the theoretical foundations. Uses existing phase utility functions
806+
to avoid code duplication.
807+
808+
**Network Coherence Tracking (Optional)**: If ``TRACK_NETWORK_COHERENCE`` is enabled,
809+
global C(t) is measured before/after RA application to quantify network-level
810+
coherence increase.
811+
812+
**Identity Preservation (Canonical)**: EPI structure (kind and sign) are preserved
813+
during propagation to ensure structural identity is maintained as required by theory.
783814
784815
Examples
785816
--------
786817
>>> class MockNode:
787818
... def __init__(self, epi, neighbors):
788819
... self.EPI = epi
789820
... self.epi_kind = "seed"
821+
... self.vf = 1.0
822+
... self.theta = 0.0
790823
... self.graph = {}
791824
... self._neighbors = neighbors
792825
... def neighbors(self):
793826
... return self._neighbors
794827
>>> neighbor = MockNode(1.0, [])
828+
>>> neighbor.theta = 0.1
795829
>>> node = MockNode(0.2, [neighbor])
796-
>>> _op_RA(node, {"RA_epi_diff": 0.25})
830+
>>> _op_RA(node, {"RA_epi_diff": 0.25, "RA_vf_amplification": 0.05})
797831
>>> round(node.EPI, 2)
798832
0.4
833+
>>> node.vf # Amplified due to neighbor coherence (canonical effect)
834+
1.05
799835
"""
836+
# Get configuration factors
800837
diff = get_factor(gf, "RA_epi_diff", 0.15)
801-
_mix_epi_with_neighbors(node, diff, Glyph.RA)
838+
vf_boost = get_factor(gf, "RA_vf_amplification", 0.05)
839+
phase_coupling = get_factor(gf, "RA_phase_coupling", 0.10) # Canonical phase strengthening
840+
841+
# Track network C(t) before RA if enabled (optional telemetry)
842+
track_coherence = bool(node.graph.get("TRACK_NETWORK_COHERENCE", False))
843+
c_before = None
844+
if track_coherence and hasattr(node, "G"):
845+
try:
846+
from ..metrics.coherence import compute_network_coherence
847+
c_before = compute_network_coherence(node.G)
848+
if "_ra_c_tracking" not in node.graph:
849+
node.graph["_ra_c_tracking"] = []
850+
except ImportError:
851+
pass # Metrics module not available
852+
853+
# Capture state before for metrics
854+
vf_before = node.vf
855+
epi_before = node.EPI
856+
kind_before = node.epi_kind
857+
theta_before = node.theta if hasattr(node, "theta") else None
858+
859+
# EPI diffusion (existing behavior)
860+
neigh, epi_bar = get_neighbor_epi(node)
861+
epi_bar_result, kind_result = _mix_epi_with_neighbors(node, diff, Glyph.RA)
862+
863+
# CANONICAL EFFECT 1: νf amplification through resonance
864+
# This is always active - it's a fundamental property of resonance per TNFR theory
865+
# Only amplify if neighbors have coherence to propagate
866+
if abs(epi_bar_result) > 1e-9 and len(neigh) > 0:
867+
node.vf *= (1.0 + vf_boost)
868+
869+
# CANONICAL EFFECT 2: Phase alignment strengthening
870+
# Per theory: "Phase alignment: Strengthens across propagation path"
871+
# Uses existing phase locking logic from IL operator (avoid duplication)
872+
phase_strengthened = False
873+
if len(neigh) > 0 and hasattr(node, "theta") and hasattr(node, "G"):
874+
try:
875+
# Use existing phase locking utility from IL operator
876+
from ..alias import get_attr
877+
from ..constants.aliases import ALIAS_THETA
878+
import cmath
879+
import math
880+
881+
# Get neighbor phases using existing utilities
882+
neighbor_phases = []
883+
for n in neigh:
884+
try:
885+
theta_n = float(get_attr(n, ALIAS_THETA, 0.0))
886+
neighbor_phases.append(theta_n)
887+
except (KeyError, ValueError, TypeError):
888+
continue
889+
890+
if neighbor_phases:
891+
# Circular mean using the same method as in phase_coherence.py
892+
complex_phases = [cmath.exp(1j * theta) for theta in neighbor_phases]
893+
mean_real = sum(z.real for z in complex_phases) / len(complex_phases)
894+
mean_imag = sum(z.imag for z in complex_phases) / len(complex_phases)
895+
mean_complex = complex(mean_real, mean_imag)
896+
mean_phase = cmath.phase(mean_complex)
897+
898+
# Ensure positive phase [0, 2π]
899+
if mean_phase < 0:
900+
mean_phase += 2 * math.pi
901+
902+
# Calculate phase difference (shortest arc)
903+
delta_theta = mean_phase - node.theta
904+
if delta_theta > math.pi:
905+
delta_theta -= 2 * math.pi
906+
elif delta_theta < -math.pi:
907+
delta_theta += 2 * math.pi
908+
909+
# Apply phase strengthening (move toward network mean)
910+
# Same approach as IL operator phase locking
911+
node.theta = node.theta + phase_coupling * delta_theta
912+
913+
# Normalize to [0, 2π]
914+
node.theta = node.theta % (2 * math.pi)
915+
phase_strengthened = True
916+
except (AttributeError, ImportError):
917+
pass # Phase alignment not possible in this context
918+
919+
# Track identity preservation (canonical validation)
920+
identity_preserved = (
921+
(kind_result == kind_before or kind_result == Glyph.RA.value)
922+
and (float(epi_before) * float(node.EPI) >= 0) # Sign preserved
923+
)
924+
925+
# Collect propagation metrics if enabled (optional telemetry)
926+
collect_metrics = bool(node.graph.get("COLLECT_RA_METRICS", False))
927+
if collect_metrics:
928+
metrics = {
929+
"operator": "RA",
930+
"epi_propagated": epi_bar_result,
931+
"vf_amplification": node.vf / vf_before if vf_before > 0 else 1.0,
932+
"neighbors_influenced": len(neigh),
933+
"identity_preserved": identity_preserved,
934+
"epi_before": epi_before,
935+
"epi_after": float(node.EPI),
936+
"vf_before": vf_before,
937+
"vf_after": node.vf,
938+
"phase_before": theta_before,
939+
"phase_after": node.theta if hasattr(node, "theta") else None,
940+
"phase_alignment_strengthened": phase_strengthened,
941+
}
942+
if "ra_metrics" not in node.graph:
943+
node.graph["ra_metrics"] = []
944+
node.graph["ra_metrics"].append(metrics)
945+
946+
# Track network C(t) after RA if enabled (optional telemetry)
947+
if track_coherence and c_before is not None and hasattr(node, "G"):
948+
try:
949+
from ..metrics.coherence import compute_network_coherence
950+
c_after = compute_network_coherence(node.G)
951+
node.graph["_ra_c_tracking"].append({
952+
"node": getattr(node, "n", None),
953+
"c_before": c_before,
954+
"c_after": c_after,
955+
"c_delta": c_after - c_before,
956+
})
957+
except ImportError:
958+
pass
802959

803960

804961
def _op_SHA(node: NodeProtocol, gf: GlyphFactors) -> None: # SHA — Silence

src/tnfr/operators/definitions.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1663,10 +1663,15 @@ def _validate_preconditions(self, G: TNFRGraph, node: Any) -> None:
16631663
def _collect_metrics(
16641664
self, G: TNFRGraph, node: Any, state_before: dict[str, Any]
16651665
) -> dict[str, Any]:
1666-
"""Collect RA-specific metrics."""
1666+
"""Collect RA-specific metrics with canonical νf amplification tracking."""
16671667
from .metrics import resonance_metrics
16681668

1669-
return resonance_metrics(G, node, state_before["epi"])
1669+
return resonance_metrics(
1670+
G,
1671+
node,
1672+
state_before["epi"],
1673+
vf_before=state_before["vf"] # Include νf for amplification tracking
1674+
)
16701675

16711676

16721677
@register_operator

src/tnfr/operators/metrics.py

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -747,8 +747,20 @@ def coupling_metrics(
747747
return metrics
748748

749749

750-
def resonance_metrics(G: TNFRGraph, node: NodeId, epi_before: float) -> dict[str, Any]:
751-
"""RA - Resonance metrics: EPI propagation, affected neighbors, resonance strength.
750+
def resonance_metrics(
751+
G: TNFRGraph,
752+
node: NodeId,
753+
epi_before: float,
754+
vf_before: float | None = None,
755+
) -> dict[str, Any]:
756+
"""RA - Resonance metrics: EPI propagation, νf amplification, phase strengthening.
757+
758+
Canonical TNFR resonance metrics include:
759+
- EPI propagation effectiveness
760+
- νf amplification (structural frequency increase)
761+
- Phase alignment strengthening
762+
- Identity preservation validation
763+
- Network coherence contribution
752764
753765
Parameters
754766
----------
@@ -758,13 +770,21 @@ def resonance_metrics(G: TNFRGraph, node: NodeId, epi_before: float) -> dict[str
758770
Node to collect metrics from
759771
epi_before : float
760772
EPI value before operator application
773+
vf_before : float | None
774+
νf value before operator application (for amplification tracking)
761775
762776
Returns
763777
-------
764778
dict
765-
Resonance-specific metrics including propagation effectiveness
779+
Resonance-specific metrics including:
780+
- EPI propagation metrics
781+
- νf amplification ratio (canonical effect)
782+
- Phase alignment quality
783+
- Identity preservation status
784+
- Network coherence contribution
766785
"""
767786
epi_after = _get_node_attr(G, node, ALIAS_EPI)
787+
vf_after = _get_node_attr(G, node, ALIAS_VF)
768788
neighbors = list(G.neighbors(node))
769789
neighbor_count = len(neighbors)
770790

@@ -773,20 +793,42 @@ def resonance_metrics(G: TNFRGraph, node: NodeId, epi_before: float) -> dict[str
773793
neighbor_epi_sum = sum(_get_node_attr(G, n, ALIAS_EPI) for n in neighbors)
774794
neighbor_epi_mean = neighbor_epi_sum / neighbor_count
775795
resonance_strength = abs(epi_after - epi_before) * neighbor_count
796+
797+
# Canonical νf amplification tracking
798+
if vf_before is not None and vf_before > 0:
799+
vf_amplification = vf_after / vf_before
800+
else:
801+
vf_amplification = 1.0
802+
803+
# Phase alignment quality (measure coherence with neighbors)
804+
from ..metrics.phase_coherence import compute_phase_alignment
805+
phase_alignment = compute_phase_alignment(G, node)
776806
else:
777807
neighbor_epi_mean = 0.0
778808
resonance_strength = 0.0
809+
vf_amplification = 1.0
810+
phase_alignment = 0.0
811+
812+
# Identity preservation check (sign should be preserved)
813+
identity_preserved = (epi_before * epi_after >= 0)
779814

780815
return {
781816
"operator": "Resonance",
782817
"glyph": "RA",
783818
"delta_epi": epi_after - epi_before,
784819
"epi_final": epi_after,
820+
"epi_before": epi_before,
785821
"neighbor_count": neighbor_count,
786822
"neighbor_epi_mean": neighbor_epi_mean,
787823
"resonance_strength": resonance_strength,
788824
"propagation_successful": neighbor_count > 0
789825
and abs(epi_after - neighbor_epi_mean) < 0.5,
826+
# Canonical TNFR effects
827+
"vf_amplification": vf_amplification, # Canonical: νf increases through resonance
828+
"vf_before": vf_before if vf_before is not None else vf_after,
829+
"vf_after": vf_after,
830+
"phase_alignment": phase_alignment, # Canonical: phase strengthens
831+
"identity_preserved": identity_preserved, # Canonical: EPI identity maintained
790832
}
791833

792834

0 commit comments

Comments
 (0)