@@ -552,9 +552,19 @@ def dissonance_metrics(
552552 }
553553
554554
555- def coupling_metrics (G : TNFRGraph , node : NodeId , theta_before : float , dnfr_before : float = None ) -> dict [str , Any ]:
555+ def coupling_metrics (
556+ G : TNFRGraph ,
557+ node : NodeId ,
558+ theta_before : float ,
559+ dnfr_before : float = None ,
560+ vf_before : float = None ,
561+ edges_before : int = None ,
562+ ) -> dict [str , Any ]:
556563 """UM - Coupling metrics: phase alignment, link formation, synchrony, ΔNFR reduction.
557564
565+ Extended metrics for Coupling (UM) operator that track structural changes,
566+ network formation, and synchronization effectiveness.
567+
558568 Parameters
559569 ----------
560570 G : TNFRGraph
@@ -565,16 +575,64 @@ def coupling_metrics(G: TNFRGraph, node: NodeId, theta_before: float, dnfr_befor
565575 Phase value before operator application
566576 dnfr_before : float, optional
567577 ΔNFR value before operator application (for reduction tracking)
578+ vf_before : float, optional
579+ Structural frequency (νf) before operator application
580+ edges_before : int, optional
581+ Number of edges before operator application
568582
569583 Returns
570584 -------
571585 dict
572- Coupling-specific metrics including phase synchronization and ΔNFR reduction
586+ Coupling-specific metrics including:
587+
588+ **Phase metrics:**
589+
590+ - theta_shift: Absolute phase change
591+ - theta_final: Post-coupling phase
592+ - mean_neighbor_phase: Average phase of neighbors
593+ - phase_alignment: Alignment with neighbors [0,1]
594+ - phase_dispersion: Standard deviation of phases in local cluster
595+ - is_synchronized: Boolean indicating strong synchronization (alignment > 0.8)
596+
597+ **Frequency metrics:**
598+
599+ - delta_vf: Change in structural frequency (νf)
600+ - vf_final: Post-coupling structural frequency
601+
602+ **Reorganization metrics:**
603+
604+ - delta_dnfr: Change in ΔNFR
605+ - dnfr_stabilization: Reduction of reorganization pressure (positive if stabilized)
606+ - dnfr_final: Post-coupling ΔNFR
607+ - dnfr_reduction: Absolute reduction (before - after)
608+ - dnfr_reduction_pct: Percentage reduction
609+
610+ **Network metrics:**
611+
612+ - neighbor_count: Number of neighbors after coupling
613+ - new_edges_count: Number of edges added
614+ - total_edges: Total edges after coupling
615+ - coupling_strength_total: Sum of coupling weights on edges
616+ - local_coherence: Kuramoto order parameter of local subgraph
617+
618+ Notes
619+ -----
620+ The extended metrics align with TNFR canonical theory (§2.2.2) that UM creates
621+ structural links through phase synchronization (φᵢ(t) ≈ φⱼ(t)). The metrics
622+ capture both the synchronization quality and the network structural changes
623+ resulting from coupling.
624+
625+ See Also
626+ --------
627+ operators.definitions.Coupling : UM operator implementation
628+ metrics.phase_coherence.compute_phase_alignment : Phase alignment computation
573629 """
574630 import math
631+ import statistics
575632
576633 theta_after = _get_node_attr (G , node , ALIAS_THETA )
577634 dnfr_after = _get_node_attr (G , node , ALIAS_DNFR )
635+ vf_after = _get_node_attr (G , node , ALIAS_VF )
578636 neighbors = list (G .neighbors (node ))
579637 neighbor_count = len (neighbors )
580638
@@ -587,6 +645,7 @@ def coupling_metrics(G: TNFRGraph, node: NodeId, theta_before: float, dnfr_befor
587645 mean_neighbor_phase = theta_after
588646 phase_alignment = 0.0
589647
648+ # Base metrics (always present)
590649 metrics = {
591650 "operator" : "Coupling" ,
592651 "glyph" : "UM" ,
@@ -597,17 +656,68 @@ def coupling_metrics(G: TNFRGraph, node: NodeId, theta_before: float, dnfr_befor
597656 "phase_alignment" : max (0.0 , phase_alignment ),
598657 }
599658
600- # Add ΔNFR reduction metrics if dnfr_before is provided
659+ # Structural frequency metrics (if vf_before provided)
660+ if vf_before is not None :
661+ delta_vf = vf_after - vf_before
662+ metrics .update ({
663+ "delta_vf" : delta_vf ,
664+ "vf_final" : vf_after ,
665+ })
666+
667+ # ΔNFR reduction metrics (if dnfr_before provided)
601668 if dnfr_before is not None :
602669 dnfr_reduction = dnfr_before - dnfr_after
603670 dnfr_reduction_pct = (dnfr_reduction / (abs (dnfr_before ) + 1e-9 )) * 100.0
671+ dnfr_stabilization = dnfr_before - dnfr_after # Positive if stabilized
604672 metrics .update ({
605673 "dnfr_before" : dnfr_before ,
606674 "dnfr_after" : dnfr_after ,
675+ "delta_dnfr" : dnfr_after - dnfr_before ,
607676 "dnfr_reduction" : dnfr_reduction ,
608677 "dnfr_reduction_pct" : dnfr_reduction_pct ,
678+ "dnfr_stabilization" : dnfr_stabilization ,
679+ "dnfr_final" : dnfr_after ,
609680 })
610681
682+ # Edge/network formation metrics (if edges_before provided)
683+ edges_after = G .degree (node )
684+ if edges_before is not None :
685+ new_edges_count = edges_after - edges_before
686+ metrics .update ({
687+ "new_edges_count" : new_edges_count ,
688+ "total_edges" : edges_after ,
689+ })
690+ else :
691+ # Still provide total_edges even without edges_before
692+ metrics ["total_edges" ] = edges_after
693+
694+ # Coupling strength (sum of edge weights)
695+ coupling_strength_total = 0.0
696+ for neighbor in neighbors :
697+ edge_data = G .get_edge_data (node , neighbor )
698+ if edge_data and isinstance (edge_data , dict ):
699+ coupling_strength_total += edge_data .get ('coupling' , 0.0 )
700+ metrics ["coupling_strength_total" ] = coupling_strength_total
701+
702+ # Phase dispersion (standard deviation of local phases)
703+ if neighbor_count > 1 :
704+ phases = [theta_after ] + [_get_node_attr (G , n , ALIAS_THETA ) for n in neighbors ]
705+ phase_std = statistics .stdev (phases )
706+ metrics ["phase_dispersion" ] = phase_std
707+ else :
708+ metrics ["phase_dispersion" ] = 0.0
709+
710+ # Local coherence (Kuramoto order parameter of subgraph)
711+ if neighbor_count > 0 :
712+ from ..metrics .phase_coherence import compute_phase_alignment
713+ local_coherence = compute_phase_alignment (G , node , radius = 1 )
714+ metrics ["local_coherence" ] = local_coherence
715+ else :
716+ metrics ["local_coherence" ] = 0.0
717+
718+ # Synchronization indicator
719+ metrics ["is_synchronized" ] = phase_alignment > 0.8
720+
611721 return metrics
612722
613723
0 commit comments