Skip to content

Commit d1b8287

Browse files
authored
Merge pull request #2878 from fermga/copilot/implement-threshold-check-mutation
Implement ZHIR threshold verification (∂EPI/∂t > ξ)
2 parents 5885d14 + a7e794b commit d1b8287

File tree

5 files changed

+570
-3
lines changed

5 files changed

+570
-3
lines changed
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
#!/usr/bin/env python3
2+
"""Example demonstrating ZHIR threshold verification (∂EPI/∂t > ξ).
3+
4+
This example shows how the canonical mutation threshold requirement works
5+
in practice, including cases where it's met and where it generates warnings.
6+
7+
References:
8+
- AGENTS.md §11 (Mutation operator)
9+
- TNFR.pdf §2.2.11 (ZHIR physics)
10+
"""
11+
12+
from tnfr.structural import create_nfr
13+
from tnfr.operators.definitions import Dissonance, Mutation, Coherence
14+
15+
16+
def example_low_threshold():
17+
"""Mutation without sufficient velocity generates warning."""
18+
print("\n=== Example 1: Low Velocity (∂EPI/∂t < ξ) ===")
19+
print("Creating node with low structural change velocity...")
20+
21+
G, node = create_nfr("test_low", epi=0.3, vf=1.0)
22+
G.graph["ZHIR_THRESHOLD_XI"] = 0.1 # Canonical threshold
23+
G.graph["VALIDATE_OPERATOR_PRECONDITIONS"] = True # Enable validation
24+
25+
# Build EPI history with low velocity
26+
# ∂EPI/∂t ≈ 0.30 - 0.29 = 0.01 < ξ=0.1
27+
G.nodes[node]["epi_history"] = [0.28, 0.29, 0.30]
28+
29+
print(f"EPI history: {G.nodes[node]['epi_history']}")
30+
print(f"∂EPI/∂t ≈ {0.30 - 0.29:.3f} < ξ={G.graph['ZHIR_THRESHOLD_XI']}")
31+
print("\nApplying ZHIR (Mutation)...")
32+
33+
# Apply mutation - will generate warning
34+
Mutation()(G, node)
35+
36+
print(f"✓ Mutation applied")
37+
print(f"⚠ Warning flag set: {G.nodes[node].get('_zhir_threshold_warning', False)}")
38+
print("→ Mutation may lack structural justification")
39+
40+
41+
def example_high_threshold():
42+
"""Mutation with sufficient velocity succeeds cleanly."""
43+
print("\n=== Example 2: High Velocity (∂EPI/∂t > ξ) ===")
44+
print("Creating node with high structural change velocity...")
45+
46+
G, node = create_nfr("test_high", epi=0.5, vf=1.0)
47+
G.graph["ZHIR_THRESHOLD_XI"] = 0.1 # Canonical threshold
48+
G.graph["VALIDATE_OPERATOR_PRECONDITIONS"] = True # Enable validation
49+
50+
# Build EPI history with high velocity
51+
# ∂EPI/∂t ≈ 0.50 - 0.38 = 0.12 > ξ=0.1
52+
G.nodes[node]["epi_history"] = [0.25, 0.38, 0.50]
53+
54+
print(f"EPI history: {G.nodes[node]['epi_history']}")
55+
print(f"∂EPI/∂t ≈ {0.50 - 0.38:.3f} > ξ={G.graph['ZHIR_THRESHOLD_XI']}")
56+
print("\nApplying ZHIR (Mutation)...")
57+
58+
# Apply mutation - will succeed without warning
59+
Mutation()(G, node)
60+
61+
print(f"✓ Mutation applied")
62+
print(f"✓ Threshold met: {G.nodes[node].get('_zhir_threshold_met', False)}")
63+
print("→ Phase transformation justified by structural velocity")
64+
65+
66+
def example_canonical_sequence():
67+
"""OZ → ZHIR sequence generates sufficient threshold."""
68+
print("\n=== Example 3: Canonical Sequence (OZ → ZHIR) ===")
69+
print("Canonical sequence: IL → OZ → ZHIR")
70+
print("OZ (Dissonance) elevates ΔNFR, increasing structural velocity...")
71+
72+
G, node = create_nfr("test_canonical", epi=0.4, vf=1.0)
73+
G.graph["ZHIR_THRESHOLD_XI"] = 0.1
74+
G.graph["COLLECT_OPERATOR_METRICS"] = True
75+
G.graph["VALIDATE_OPERATOR_PRECONDITIONS"] = True # Enable validation
76+
77+
# Initialize with moderate history
78+
G.nodes[node]["epi_history"] = [0.35, 0.38, 0.40]
79+
80+
print(f"Initial EPI history: {G.nodes[node]['epi_history']}")
81+
print("\nApplying sequence: IL → OZ → ZHIR...")
82+
83+
# Apply canonical sequence
84+
Coherence()(G, node) # Stabilize
85+
Dissonance()(G, node) # Increase ΔNFR (elevates velocity)
86+
Mutation()(G, node) # Phase transformation
87+
88+
print(f"✓ Sequence completed")
89+
90+
# Check metrics
91+
if "operator_metrics" in G.graph:
92+
zhir_metrics = [m for m in G.graph["operator_metrics"] if m.get("glyph") == "ZHIR"]
93+
if zhir_metrics:
94+
m = zhir_metrics[-1]
95+
print(f"\nZHIR Metrics:")
96+
print(f" ∂EPI/∂t = {m.get('depi_dt', 0):.3f}")
97+
print(f" Threshold ξ = {m.get('threshold_xi', 0):.3f}")
98+
print(f" Threshold met: {m.get('threshold_met', False)}")
99+
print(f" Ratio: {m.get('threshold_ratio', 0):.2f}x")
100+
101+
if m.get('threshold_met'):
102+
print("→ OZ successfully elevated velocity above threshold")
103+
104+
105+
def example_metrics():
106+
"""Show detailed threshold metrics."""
107+
print("\n=== Example 4: Detailed Threshold Metrics ===")
108+
print("Configuring detailed metrics collection...")
109+
110+
G, node = create_nfr("test_metrics", epi=0.6, vf=1.0)
111+
G.graph["ZHIR_THRESHOLD_XI"] = 0.1
112+
G.graph["COLLECT_OPERATOR_METRICS"] = True
113+
G.graph["VALIDATE_OPERATOR_PRECONDITIONS"] = True # Enable validation
114+
115+
# Build history with known velocity: 0.30 velocity / 0.1 threshold = 3.0x
116+
G.nodes[node]["epi_history"] = [0.10, 0.30, 0.60]
117+
118+
print(f"EPI history: {G.nodes[node]['epi_history']}")
119+
print(f"∂EPI/∂t ≈ {0.60 - 0.30:.3f} (3.0x threshold)")
120+
print("\nApplying ZHIR...")
121+
122+
Mutation()(G, node)
123+
124+
# Display full metrics
125+
if "operator_metrics" in G.graph:
126+
m = G.graph["operator_metrics"][-1]
127+
print(f"\nFull ZHIR Metrics:")
128+
print(f" Operator: {m['operator']}")
129+
print(f" Glyph: {m['glyph']}")
130+
print(f" Phase shift: {m['theta_shift']:.3f}")
131+
print(f" Delta EPI: {m['delta_epi']:.3f}")
132+
print(f" ---")
133+
print(f" ∂EPI/∂t: {m['depi_dt']:.3f}")
134+
print(f" Threshold ξ: {m['threshold_xi']:.3f}")
135+
print(f" Threshold met: {m['threshold_met']}")
136+
print(f" Threshold ratio: {m['threshold_ratio']:.2f}x")
137+
print(f" Exceeded by: {m['threshold_exceeded_by']:.3f}")
138+
print(f" ---")
139+
print(f" Validated: {m['threshold_validated']}")
140+
print(f" Warning: {m['threshold_warning']}")
141+
142+
143+
def main():
144+
"""Run all examples."""
145+
print("=" * 70)
146+
print("ZHIR (Mutation) Threshold Verification Examples")
147+
print("=" * 70)
148+
print("\nCanonical Requirement: θ → θ' when ∂EPI/∂t > ξ")
149+
print("(Phase transformation requires sufficient structural velocity)")
150+
151+
example_low_threshold()
152+
example_high_threshold()
153+
example_canonical_sequence()
154+
example_metrics()
155+
156+
print("\n" + "=" * 70)
157+
print("Summary")
158+
print("=" * 70)
159+
print("• ZHIR threshold verification ensures mutations are justified")
160+
print("• Default threshold ξ=0.1 represents minimum velocity for phase change")
161+
print("• OZ (Dissonance) sequences naturally elevate velocity above threshold")
162+
print("• Warnings are logged (not blocking) for backward compatibility")
163+
print("• Metrics include full threshold verification telemetry")
164+
print("\nConfiguration:")
165+
print(" G.graph['ZHIR_THRESHOLD_XI'] = 0.1 # Default threshold")
166+
print(" G.graph['COLLECT_OPERATOR_METRICS'] = True # Enable telemetry")
167+
print("=" * 70)
168+
169+
170+
if __name__ == "__main__":
171+
main()

src/tnfr/operators/definitions.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3121,14 +3121,34 @@ class Mutation(Operator):
31213121
random variation, ZHIR is controlled transformation that preserves structural identity
31223122
while shifting operational regime. Critical for adaptation and evolution.
31233123
3124+
**Canonical Threshold Requirement (∂EPI/∂t > ξ)**:
3125+
3126+
According to TNFR physics (AGENTS.md §11, TNFR.pdf §2.2.11), ZHIR requires sufficient
3127+
structural change velocity to justify phase transformation. The threshold ξ (default 0.1)
3128+
represents the minimum rate of reorganization needed for meaningful mutation.
3129+
3130+
- **∂EPI/∂t ≥ ξ**: Mutation validated, phase transformation proceeds
3131+
- **∂EPI/∂t < ξ**: Warning logged, mutation may lack structural justification
3132+
- **Insufficient history**: Warning logged, threshold cannot be verified
3133+
3134+
Configuration: Set ``G.graph["ZHIR_THRESHOLD_XI"]`` to customize threshold (default: 0.1).
3135+
3136+
Metrics: Threshold verification included in mutation_metrics() telemetry:
3137+
- ``depi_dt``: Computed structural change velocity
3138+
- ``threshold_met``: Boolean flag indicating threshold status
3139+
- ``threshold_ratio``: Ratio of velocity to threshold
3140+
31243141
Use Cases: Paradigm shifts, strategic pivots, adaptive responses, regime transitions,
31253142
identity transformation while maintaining continuity.
31263143
31273144
Typical Sequences: IL → ZHIR → IL (stabilize-mutate-stabilize), OZ → ZHIR (dissonance
31283145
enables mutation), ZHIR → NAV → IL (mutate-transition-stabilize).
31293146
3130-
Preconditions: Requires stable base (often IL), sufficient ΔNFR for phase change,
3131-
network support for new phase.
3147+
Preconditions:
3148+
- Minimum νf for phase transformation capacity (ZHIR_MIN_VF, default: 0.05)
3149+
- **Structural velocity threshold (∂EPI/∂t > ξ)** - validated with EPI history
3150+
- Stable base (often IL) validated by grammar U4b
3151+
- Network support for new phase
31323152
31333153
Examples
31343154
--------

src/tnfr/operators/metrics.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1546,6 +1546,9 @@ def mutation_metrics(
15461546
) -> dict[str, Any]:
15471547
"""ZHIR - Mutation metrics: phase transition, structural regime change.
15481548
1549+
Collects mutation-specific metrics including canonical threshold verification
1550+
(∂EPI/∂t > ξ) as per TNFR physics (AGENTS.md §11, TNFR.pdf §2.2.11).
1551+
15491552
Parameters
15501553
----------
15511554
G : TNFRGraph
@@ -1560,19 +1563,46 @@ def mutation_metrics(
15601563
Returns
15611564
-------
15621565
dict
1563-
Mutation-specific metrics including phase change indicators
1566+
Mutation-specific metrics including:
1567+
- Phase change indicators (theta_shift, phase_change)
1568+
- EPI change (delta_epi)
1569+
- **Threshold verification**: depi_dt, threshold_met, threshold_ratio
1570+
- Structural justification flags
15641571
"""
15651572
theta_after = _get_node_attr(G, node, ALIAS_THETA)
15661573
epi_after = _get_node_attr(G, node, ALIAS_EPI)
15671574

1575+
# Compute ∂EPI/∂t from history
1576+
epi_history = G.nodes[node].get("epi_history") or G.nodes[node].get("_epi_history", [])
1577+
if len(epi_history) >= 2:
1578+
depi_dt = abs(epi_history[-1] - epi_history[-2])
1579+
else:
1580+
depi_dt = 0.0
1581+
1582+
# Check threshold
1583+
xi = float(G.graph.get("ZHIR_THRESHOLD_XI", 0.1))
1584+
threshold_met = depi_dt >= xi
1585+
threshold_ratio = depi_dt / xi if xi > 0 else 0.0
1586+
15681587
return {
15691588
"operator": "Mutation",
15701589
"glyph": "ZHIR",
1590+
# Existing metrics
15711591
"theta_shift": abs(theta_after - theta_before),
15721592
"theta_final": theta_after,
15731593
"delta_epi": epi_after - epi_before,
15741594
"epi_final": epi_after,
15751595
"phase_change": abs(theta_after - theta_before) > 0.5, # Configurable threshold
1596+
# NEW: Threshold verification metrics
1597+
"depi_dt": depi_dt,
1598+
"threshold_xi": xi,
1599+
"threshold_met": threshold_met,
1600+
"threshold_ratio": threshold_ratio,
1601+
"threshold_exceeded_by": max(0.0, depi_dt - xi),
1602+
# Structural justification flags from preconditions
1603+
"threshold_warning": G.nodes[node].get("_zhir_threshold_warning", False),
1604+
"threshold_validated": G.nodes[node].get("_zhir_threshold_met", False),
1605+
"threshold_unknown": G.nodes[node].get("_zhir_threshold_unknown", False),
15761606
}
15771607

15781608

src/tnfr/operators/preconditions/__init__.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,12 @@ def _record_destabilizer_context(
990990
def validate_mutation(G: "TNFRGraph", node: "NodeId") -> None:
991991
"""ZHIR - Mutation requires node to be in valid structural state.
992992
993+
Implements canonical TNFR requirements for mutation (AGENTS.md §11, TNFR.pdf §2.2.11):
994+
995+
1. Minimum νf for phase transformation capacity
996+
2. **∂EPI/∂t > ξ: Structural change velocity exceeds threshold**
997+
3. Stable base (IL precedence validated by grammar U4b)
998+
993999
Also detects and records the destabilizer type that enabled this mutation
9941000
for telemetry and structural tracing purposes.
9951001
@@ -1007,6 +1013,16 @@ def validate_mutation(G: "TNFRGraph", node: "NodeId") -> None:
10071013
10081014
Notes
10091015
-----
1016+
**Canonical threshold verification (∂EPI/∂t > ξ)**:
1017+
1018+
ZHIR is a phase transformation that requires sufficient structural reorganization
1019+
velocity to justify the transition. The threshold ξ represents the minimum rate
1020+
of structural change needed for a phase shift to be physically meaningful.
1021+
1022+
- If ∂EPI/∂t < ξ: Logs warning (soft check for backward compatibility)
1023+
- If ∂EPI/∂t ≥ ξ: Logs success, sets validation flag
1024+
- If insufficient history: Logs warning, cannot verify
1025+
10101026
This function implements R4 Extended telemetry by analyzing the glyph_history
10111027
to determine which destabilizer (strong/moderate/weak) enabled the mutation.
10121028
The destabilizer context is stored in node metadata for structural tracing.
@@ -1024,6 +1040,41 @@ def validate_mutation(G: "TNFRGraph", node: "NodeId") -> None:
10241040
f"Structural frequency too low for mutation (νf={vf:.3f} < {min_vf:.3f})",
10251041
)
10261042

1043+
# NEW: Threshold crossing validation (∂EPI/∂t > ξ)
1044+
# Get EPI history - check both keys for compatibility
1045+
epi_history = G.nodes[node].get("epi_history") or G.nodes[node].get("_epi_history", [])
1046+
1047+
if len(epi_history) >= 2:
1048+
# Compute ∂EPI/∂t (discrete approximation using last two points)
1049+
# For discrete operator applications with Δt=1: ∂EPI/∂t ≈ EPI_t - EPI_{t-1}
1050+
depi_dt = abs(epi_history[-1] - epi_history[-2])
1051+
1052+
# Get threshold from configuration
1053+
xi_threshold = float(G.graph.get("ZHIR_THRESHOLD_XI", 0.1))
1054+
1055+
# Verify threshold crossed
1056+
if depi_dt < xi_threshold:
1057+
# Allow mutation but log warning (soft check for backward compatibility)
1058+
logger.warning(
1059+
f"Node {node}: ZHIR applied with ∂EPI/∂t={depi_dt:.3f} < ξ={xi_threshold}. "
1060+
f"Mutation may lack structural justification. "
1061+
f"Consider increasing dissonance (OZ) first."
1062+
)
1063+
G.nodes[node]["_zhir_threshold_warning"] = True
1064+
else:
1065+
# Threshold met - log success
1066+
logger.info(
1067+
f"Node {node}: ZHIR threshold crossed (∂EPI/∂t={depi_dt:.3f} > ξ={xi_threshold})"
1068+
)
1069+
G.nodes[node]["_zhir_threshold_met"] = True
1070+
else:
1071+
# Insufficient history - cannot verify threshold
1072+
logger.warning(
1073+
f"Node {node}: ZHIR applied without sufficient EPI history "
1074+
f"(need ≥2 points, have {len(epi_history)}). Cannot verify threshold."
1075+
)
1076+
G.nodes[node]["_zhir_threshold_unknown"] = True
1077+
10271078
# R4 Extended: Detect and record destabilizer type for telemetry
10281079
# This provides structural traceability for bifurcation events
10291080
_record_destabilizer_context(G, node, logger)

0 commit comments

Comments
 (0)