Skip to content

Commit ddd4890

Browse files
Copilotfermga
andcommitted
Implement comprehensive NAV precondition validation with tests
Co-authored-by: fermga <203334638+fermga@users.noreply.github.com>
1 parent a1254d9 commit ddd4890

File tree

2 files changed

+534
-3
lines changed

2 files changed

+534
-3
lines changed

src/tnfr/operators/preconditions/__init__.py

Lines changed: 113 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,7 +1065,26 @@ def validate_mutation(G: "TNFRGraph", node: "NodeId") -> None:
10651065

10661066

10671067
def validate_transition(G: "TNFRGraph", node: "NodeId") -> None:
1068-
"""NAV - Transition requires ΔNFR and vf for controlled handoff.
1068+
"""NAV - Comprehensive canonical preconditions for transition.
1069+
1070+
Validates comprehensive preconditions for NAV (Transition) operator according
1071+
to TNFR.pdf §2.3.11:
1072+
1073+
1. **Minimum νf**: Structural frequency must exceed threshold
1074+
2. **Controlled ΔNFR**: |ΔNFR| must be below maximum for stable transition
1075+
3. **Regime validation**: Warns if transitioning from deep latency (EPI < 0.05)
1076+
4. **Sequence compatibility** (optional): Warns if NAV applied after incompatible operators
1077+
1078+
Configuration Parameters
1079+
------------------------
1080+
NAV_MIN_VF : float, default 0.01
1081+
Minimum structural frequency for transition
1082+
NAV_MAX_DNFR : float, default 1.0
1083+
Maximum |ΔNFR| for stable transition
1084+
NAV_STRICT_SEQUENCE_CHECK : bool, default False
1085+
Enable strict sequence compatibility checking
1086+
NAV_MIN_EPI_FROM_LATENCY : float, default 0.05
1087+
Minimum EPI for smooth transition from latency
10691088
10701089
Parameters
10711090
----------
@@ -1077,16 +1096,107 @@ def validate_transition(G: "TNFRGraph", node: "NodeId") -> None:
10771096
Raises
10781097
------
10791098
OperatorPreconditionError
1080-
If node lacks necessary dynamics for transition
1099+
If node lacks necessary dynamics for transition:
1100+
- νf below minimum threshold
1101+
- |ΔNFR| exceeds maximum threshold (unstable state)
1102+
1103+
Warnings
1104+
--------
1105+
UserWarning
1106+
For suboptimal but valid conditions:
1107+
- Transitioning from deep latency (EPI < 0.05)
1108+
- NAV applied after incompatible operator (when strict checking enabled)
1109+
1110+
Notes
1111+
-----
1112+
NAV requires controlled ΔNFR to prevent instability during regime transitions.
1113+
High |ΔNFR| indicates significant reorganization pressure that could cause
1114+
structural disruption during transition. Apply IL (Coherence) first to reduce
1115+
ΔNFR before attempting regime transition.
1116+
1117+
Deep latency transitions (EPI < 0.05 after SHA) benefit from prior AL (Emission)
1118+
to provide smoother reactivation path.
1119+
1120+
Examples
1121+
--------
1122+
>>> from tnfr.structural import create_nfr
1123+
>>> from tnfr.operators.preconditions import validate_transition
1124+
>>>
1125+
>>> # Valid transition - controlled state
1126+
>>> G, node = create_nfr("test", epi=0.5, vf=0.6)
1127+
>>> G.nodes[node]['delta_nfr'] = 0.3 # Controlled ΔNFR
1128+
>>> validate_transition(G, node) # Passes
1129+
>>>
1130+
>>> # Invalid - ΔNFR too high
1131+
>>> G.nodes[node]['delta_nfr'] = 1.5
1132+
>>> validate_transition(G, node) # Raises OperatorPreconditionError
1133+
1134+
See Also
1135+
--------
1136+
Transition : NAV operator implementation
1137+
validate_coherence : IL preconditions for ΔNFR reduction
1138+
TNFR.pdf : §2.3.11 for NAV canonical requirements
10811139
"""
1140+
import warnings
1141+
1142+
# 1. νf minimum (existing check - preserved for backward compatibility)
10821143
vf = _get_node_attr(G, node, ALIAS_VF)
10831144
min_vf = float(G.graph.get("NAV_MIN_VF", 0.01))
10841145
if vf < min_vf:
10851146
raise OperatorPreconditionError(
10861147
"Transition",
1087-
f"Structural frequency too low for transition (νf={vf:.3f} < {min_vf:.3f})",
1148+
f"Structural frequency too low (νf={vf:.3f} < {min_vf:.3f})",
1149+
)
1150+
1151+
# 2. ΔNFR positivity and bounds check (NEW - CRITICAL)
1152+
dnfr = _get_node_attr(G, node, ALIAS_DNFR)
1153+
max_dnfr = float(G.graph.get("NAV_MAX_DNFR", 1.0))
1154+
if abs(dnfr) > max_dnfr:
1155+
raise OperatorPreconditionError(
1156+
"Transition",
1157+
f"ΔNFR too high for stable transition (|ΔNFR|={abs(dnfr):.3f} > {max_dnfr}). "
1158+
f"Apply IL (Coherence) first to reduce reorganization pressure.",
1159+
)
1160+
1161+
# 3. Regime origin validation (NEW - WARNING)
1162+
latent = G.nodes[node].get("latent", False)
1163+
epi = _get_node_attr(G, node, ALIAS_EPI)
1164+
min_epi_from_latency = float(G.graph.get("NAV_MIN_EPI_FROM_LATENCY", 0.05))
1165+
1166+
if latent and epi < min_epi_from_latency:
1167+
# Warning: transitioning from deep latency
1168+
warnings.warn(
1169+
f"Node {node} in deep latency (EPI={epi:.3f} < {min_epi_from_latency:.3f}). "
1170+
f"Consider AL (Emission) before NAV for smoother activation.",
1171+
UserWarning,
1172+
stacklevel=2,
10881173
)
10891174

1175+
# 4. Sequence compatibility check (NEW - OPTIONAL)
1176+
if G.graph.get("NAV_STRICT_SEQUENCE_CHECK", False):
1177+
history = G.nodes[node].get("glyph_history", [])
1178+
if history:
1179+
from ..grammar import glyph_function_name
1180+
1181+
last_op = glyph_function_name(history[-1]) if history else None
1182+
1183+
# NAV works best after stabilizers or generators
1184+
# Valid predecessors per TNFR.pdf §2.3.11 and AGENTS.md
1185+
valid_predecessors = {
1186+
"emission", # AL → NAV (activation-transition)
1187+
"coherence", # IL → NAV (stable-transition)
1188+
"silence", # SHA → NAV (latency-transition)
1189+
"self_organization", # THOL → NAV (bifurcation-transition)
1190+
}
1191+
1192+
if last_op and last_op not in valid_predecessors:
1193+
warnings.warn(
1194+
f"NAV applied after {last_op}. "
1195+
f"More coherent after: {', '.join(sorted(valid_predecessors))}",
1196+
UserWarning,
1197+
stacklevel=2,
1198+
)
1199+
10901200

10911201
def validate_recursivity(G: "TNFRGraph", node: "NodeId") -> None:
10921202
"""REMESH - Recursivity requires global network coherence threshold.

0 commit comments

Comments
 (0)