@@ -559,6 +559,7 @@ def coupling_metrics(
559559 dnfr_before : float = None ,
560560 vf_before : float = None ,
561561 edges_before : int = None ,
562+ epi_before : float = None ,
562563) -> dict [str , Any ]:
563564 """UM - Coupling metrics: phase alignment, link formation, synchrony, ΔNFR reduction.
564565
@@ -579,6 +580,8 @@ def coupling_metrics(
579580 Structural frequency (νf) before operator application
580581 edges_before : int, optional
581582 Number of edges before operator application
583+ epi_before : float, optional
584+ EPI value before operator application (for invariance verification)
582585
583586 Returns
584587 -------
@@ -607,6 +610,13 @@ def coupling_metrics(
607610 - dnfr_reduction: Absolute reduction (before - after)
608611 - dnfr_reduction_pct: Percentage reduction
609612
613+ **EPI Invariance metrics:**
614+
615+ - epi_before: EPI value before coupling
616+ - epi_after: EPI value after coupling
617+ - epi_drift: Absolute difference between before and after
618+ - epi_preserved: Boolean indicating EPI invariance (drift < 1e-9)
619+
610620 **Network metrics:**
611621
612622 - neighbor_count: Number of neighbors after coupling
@@ -622,6 +632,10 @@ def coupling_metrics(
622632 capture both the synchronization quality and the network structural changes
623633 resulting from coupling.
624634
635+ **EPI Invariance**: UM MUST preserve EPI identity. The epi_preserved metric
636+ validates this fundamental invariant. If epi_preserved is False, it indicates
637+ a violation of TNFR canonical requirements.
638+
625639 See Also
626640 --------
627641 operators.definitions.Coupling : UM operator implementation
@@ -679,6 +693,18 @@ def coupling_metrics(
679693 "dnfr_final" : dnfr_after ,
680694 })
681695
696+ # EPI invariance verification (if epi_before provided)
697+ # CRITICAL: UM MUST preserve EPI identity per TNFR canonical theory
698+ if epi_before is not None :
699+ epi_after = _get_node_attr (G , node , ALIAS_EPI )
700+ epi_drift = abs (epi_after - epi_before )
701+ metrics .update ({
702+ "epi_before" : epi_before ,
703+ "epi_after" : epi_after ,
704+ "epi_drift" : epi_drift ,
705+ "epi_preserved" : epi_drift < 1e-9 , # Should ALWAYS be True
706+ })
707+
682708 # Edge/network formation metrics (if edges_before provided)
683709 edges_after = G .degree (node )
684710 if edges_before is not None :
0 commit comments