From 47f537932d0fa7fad1f19e8c8d3fde3290df81f9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 20:24:19 +0000 Subject: [PATCH 1/4] Initial plan From e29d38b4c11522bc6df3e7710cf7defb675eea5a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 20:34:36 +0000 Subject: [PATCH 2/4] [SHA Tests] Complete regression test suite for Silence operator Co-authored-by: fermga <203334638+fermga@users.noreply.github.com> --- tests/unit/operators/test_sha_regression.py | 368 ++++++++++++++++++++ 1 file changed, 368 insertions(+) create mode 100644 tests/unit/operators/test_sha_regression.py diff --git a/tests/unit/operators/test_sha_regression.py b/tests/unit/operators/test_sha_regression.py new file mode 100644 index 000000000..c5e76f7b5 --- /dev/null +++ b/tests/unit/operators/test_sha_regression.py @@ -0,0 +1,368 @@ +"""Comprehensive regression test suite for SHA (Silence) operator. + +This module implements a complete regression test suite for the Silence (SHA) +operator following TNFR structural theory as specified in TNFR.pdf §2.3.10. + +Test Coverage: +- A. Structural Effects: νf reduction, EPI preservation, ΔNFR freezing +- B. Preconditions: Validation of minimum νf and existing EPI requirements +- C. Canonical Sequences: IL→SHA, SHA→AL, SHA→NAV, OZ→SHA +- D. Metrics: SHA-specific metrics validation +- E. Integration: Multi-node effects, complex sequences +- F. Nodal Equation: Validation of ∂EPI/∂t = νf · ΔNFR(t) +- G. Full Lifecycle: Complete activation-silence-reactivation cycle + +Theoretical Foundation: +- SHA reduces νf → 0 (structural pause) +- EPI remains invariant (preservation) +- ΔNFR maintained but frozen (no reorganization pressure) +- Latency state tracking for memory consolidation +""" + +from __future__ import annotations + +import pytest +import warnings + +from tnfr.constants import DNFR_PRIMARY, EPI_PRIMARY, VF_PRIMARY +from tnfr.structural import create_nfr, run_sequence +from tnfr.operators.definitions import ( + Silence, + Emission, + Reception, + Coherence, + Dissonance, + Resonance, + Coupling, + Transition, +) +from tnfr.operators.preconditions import OperatorPreconditionError +from tnfr.alias import set_attr +from tnfr.constants.aliases import ALIAS_EPI, ALIAS_VF + + +class TestSHAStructuralEffects: + """Test A: Structural effects of SHA operator.""" + + def test_sha_reduces_vf_to_minimum(self): + """Test 1: SHA must reduce νf to value close to zero. + + Validates nodal equation: If νf ≈ 0, then ∂EPI/∂t ≈ 0 (independent of ΔNFR). + Sequence must start with generator (AL) per R1 grammar rule. + """ + G, node = create_nfr("active", epi=0.60, vf=1.50) + initial_vf = G.nodes[node][VF_PRIMARY] + + # Apply sequence: AL (generator start) → SHA + run_sequence(G, node, [Emission(), Silence()]) + + final_vf = G.nodes[node][VF_PRIMARY] + min_threshold = G.graph.get("SHA_MIN_VF", 0.01) + + # Validations + assert final_vf < initial_vf, "SHA must reduce νf" + assert final_vf <= min_threshold * 2, f"νf must be close to minimum: {final_vf}" + assert final_vf >= 0.0, "νf cannot be negative" + + def test_sha_preserves_epi_exactly(self): + """Test 2: SHA must maintain EPI invariant with minimal tolerance.""" + G, node = create_nfr("memory", epi=0.73, vf=1.20) + initial_epi = G.nodes[node][EPI_PRIMARY] + + # Apply SHA + run_sequence(G, node, [Silence()]) + + final_epi = G.nodes[node][EPI_PRIMARY] + tolerance = 1e-3 # Allow small numerical tolerance + + assert abs(final_epi - initial_epi) < tolerance, ( + f"EPI must be preserved: ΔEPI = {abs(final_epi - initial_epi)}" + ) + + def test_sha_freezes_dnfr(self): + """Test 3: SHA does not modify ΔNFR - state is frozen. + + ΔNFR can remain high but with νf ≈ 0, it does not affect EPI. + """ + G, node = create_nfr("frozen", epi=0.50, vf=1.00) + G.nodes[node][DNFR_PRIMARY] = 0.15 # High reorganization pressure + initial_dnfr = G.nodes[node][DNFR_PRIMARY] + + # Apply SHA + run_sequence(G, node, [Silence()]) + + final_dnfr = G.nodes[node][DNFR_PRIMARY] + + # ΔNFR can remain high, but SHA should not actively change it + # The key is that with νf ≈ 0, ΔNFR does not affect EPI + assert abs(final_dnfr - initial_dnfr) < 0.05, ( + "SHA should not actively modify ΔNFR" + ) + + +class TestSHAPreconditions: + """Test B: Precondition validation for SHA operator.""" + + def test_sha_precondition_vf_minimum(self): + """Test 4: SHA must fail if νf already at minimum.""" + G, node = create_nfr("already_silent", epi=0.40, vf=0.005) + G.graph["VALIDATE_OPERATOR_PRECONDITIONS"] = True + + with pytest.raises(OperatorPreconditionError, match="already minimal"): + run_sequence(G, node, [Silence()]) + + def test_sha_requires_existing_epi(self): + """Test 5: SHA should warn if EPI ≈ 0 (no structure to preserve).""" + G, node = create_nfr("empty", epi=0.0, vf=1.0) + G.graph["VALIDATE_OPERATOR_PRECONDITIONS"] = True + + # SHA on empty structure should issue warning + # This is more of a semantic warning than hard failure + with pytest.warns(UserWarning, match="no structure|empty|zero"): + run_sequence(G, node, [Silence()]) + + +class TestSHACanonicalSequences: + """Test C: Canonical operator sequences involving SHA.""" + + def test_sha_after_coherence_preserves_stability(self): + """Test 6: IL → SHA (stabilize then preserve) - canonical memory pattern.""" + G, node = create_nfr("learning", epi=0.45, vf=1.10) + G.nodes[node][DNFR_PRIMARY] = 0.20 # High initial pressure + + # IL reduces ΔNFR, stabilizes + run_sequence(G, node, [Coherence()]) + post_il_dnfr = G.nodes[node][DNFR_PRIMARY] + post_il_epi = G.nodes[node][EPI_PRIMARY] + + # SHA preserves the stabilized state + run_sequence(G, node, [Silence()]) + + assert G.nodes[node][VF_PRIMARY] < 0.1, "SHA reduces νf" + assert abs(G.nodes[node][EPI_PRIMARY] - post_il_epi) < 0.05, "EPI preserved" + + def test_sha_to_emission_reactivation(self): + """Test 7: SHA → NAV → AL (reactivation from silence) - structurally coherent awakening. + + TNFR Physics: Cannot jump zero → high (SHA → AL) directly. + Must transition through medium frequency: SHA → NAV → AL (zero → medium → high). + This respects structural continuity and prevents singularities. + """ + G, node = create_nfr("sleeping", epi=0.55, vf=1.00) + + # Phase 1: Prepare and enter silence + run_sequence(G, node, [Emission(), Coherence(), Silence()]) + assert G.nodes[node][VF_PRIMARY] < 0.1, "Node in silence" + silent_epi = G.nodes[node][EPI_PRIMARY] + + # Phase 2: Reactivate through medium frequency (NAV) then high (AL) + run_sequence(G, node, [Transition(), Emission()]) + + # Validate coherent reactivation + assert G.nodes[node][VF_PRIMARY] > 0.5, "Node reactivated" + assert G.nodes[node][EPI_PRIMARY] >= silent_epi - 0.15, "EPI maintains structural identity" + + def test_sha_to_transition_controlled_change(self): + """Test 8: SHA → NAV (controlled transition from silence).""" + G, node = create_nfr("dormant", epi=0.48, vf=0.95) + + # SHA: Preserve structure + run_sequence(G, node, [Silence()]) + preserved_epi = G.nodes[node][EPI_PRIMARY] + + # NAV: Transition from silence + run_sequence(G, node, [Transition()]) + + # Validate controlled transition without collapse + assert G.nodes[node][VF_PRIMARY] > 0.1, "Node reactivating" + assert abs(G.nodes[node][EPI_PRIMARY] - preserved_epi) < 0.2, ( + "EPI transitions controlledly without collapse" + ) + + def test_oz_to_sha_containment(self): + """Test 9: OZ → SHA (dissonance contained) - therapeutic pause. + + Clinical use case: Trauma containment, conflict deferred. + """ + G, node = create_nfr("trauma", epi=0.40, vf=1.00) + G.nodes[node][DNFR_PRIMARY] = 0.05 + + # OZ: Introduce dissonance + run_sequence(G, node, [Dissonance()]) + post_oz_dnfr = G.nodes[node][DNFR_PRIMARY] + assert post_oz_dnfr > 0.10, "Dissonance increases ΔNFR" + + # SHA: Contain dissonance (protective pause) + run_sequence(G, node, [Silence()]) + + # Validate containment + assert G.nodes[node][VF_PRIMARY] < 0.1, "Node paused" + # ΔNFR remains high but frozen + assert G.nodes[node][DNFR_PRIMARY] > 0.10, "Dissonance contained (not resolved)" + + +class TestSHAMetrics: + """Test D: SHA-specific metrics collection.""" + + def test_sha_metrics_preservation(self): + """Test 10: Validate that silence_metrics captures preservation correctly.""" + G, node = create_nfr("test", epi=0.60, vf=1.00) + G.graph["COLLECT_OPERATOR_METRICS"] = True + + run_sequence(G, node, [Silence()]) + + # Check if metrics were collected + if "operator_metrics" in G.graph: + metrics = G.graph["operator_metrics"][-1] + + assert metrics["operator"] == "Silence", "Operator name recorded" + assert metrics["glyph"] == "SHA", "Glyph recorded" + + # Check for SHA-specific metric keys + assert "vf_reduction" in metrics or "vf_final" in metrics, ( + "νf reduction metric present" + ) + + +class TestSHAIntegration: + """Test E: Integration and network effects.""" + + def test_sha_does_not_affect_neighbors(self): + """Test 11: SHA is local operation - no direct propagation to neighbors.""" + G, n1 = create_nfr("node1", epi=0.50, vf=1.00) + + # Add second node manually + _, n2 = create_nfr("node2", epi=0.50, vf=1.00) + # Import n2's attributes into G + G.add_node(n2) + set_attr(G.nodes[n2], ALIAS_EPI, 0.50) + set_attr(G.nodes[n2], ALIAS_VF, 1.00) + G.add_edge(n1, n2) # Connect nodes + + initial_n2_vf = G.nodes[n2][VF_PRIMARY] + + # SHA on n1 + run_sequence(G, n1, [Silence()]) + + # n2 must remain active + assert G.nodes[n1][VF_PRIMARY] < 0.1, "n1 in silence" + assert G.nodes[n2][VF_PRIMARY] >= initial_n2_vf * 0.9, ( + "n2 remains active (SHA is local)" + ) + + def test_sha_after_complex_sequence(self): + """Test 12: SHA as closure of complex sequence. + + Sequence: AL → IL → RA → UM → SHA + """ + G, node = create_nfr("complex", epi=0.30, vf=0.80) + + sequence = [ + Emission(), # AL: Activate + Coherence(), # IL: Stabilize + Resonance(), # RA: Propagate + Coupling(), # UM: Couple + Silence() # SHA: Close and preserve + ] + + initial_epi = G.nodes[node][EPI_PRIMARY] + run_sequence(G, node, sequence) + + # Validate final state + assert G.nodes[node][VF_PRIMARY] < 0.1, "Sequence closed with silence" + assert G.nodes[node][EPI_PRIMARY] >= initial_epi, ( + "EPI evolved during sequence" + ) + + +class TestSHANodalEquation: + """Test F: Nodal equation validation for SHA.""" + + def test_sha_nodal_equation_validation(self): + """Test 13: Validate SHA respects nodal equation: ∂EPI/∂t = νf · ΔNFR(t). + + If νf → 0, then |∂EPI/∂t| → 0 + """ + G, node = create_nfr("validate", epi=0.65, vf=1.30) + G.nodes[node][DNFR_PRIMARY] = 0.25 # High pressure + + epi_before = G.nodes[node][EPI_PRIMARY] + + # SHA should work even with high ΔNFR + run_sequence(G, node, [Silence()]) + + epi_after = G.nodes[node][EPI_PRIMARY] + vf_after = G.nodes[node][VF_PRIMARY] + + # ∂EPI/∂t ≈ νf_after · ΔNFR ≈ 0 (because νf ≈ 0) + delta_epi = abs(epi_after - epi_before) + + # With νf ≈ 0, EPI change should be minimal regardless of ΔNFR + assert delta_epi < 0.1, ( + f"Nodal equation respected: ΔEPI = {delta_epi} should be small with νf ≈ 0" + ) + + +class TestSHAFullLifecycle: + """Test G: Complete lifecycle including SHA.""" + + def test_sha_full_cycle_activation_silence_reactivation(self): + """Test 14: Complete cycle: AL → IL → SHA → NAV → AL. + + Simulates: learning → consolidation → memory → recall → use + """ + G, node = create_nfr("lifecycle", epi=0.25, vf=0.90) + + # Phase 1: Activation and stabilization (learning) + run_sequence(G, node, [Emission(), Coherence()]) + post_learning_epi = G.nodes[node][EPI_PRIMARY] + assert post_learning_epi > 0.25, "Learning increments EPI" + + # Phase 2: Consolidation in silence (memory formation) + run_sequence(G, node, [Silence()]) + assert G.nodes[node][VF_PRIMARY] < 0.1, "Memory consolidated" + memory_epi = G.nodes[node][EPI_PRIMARY] + + # Phase 3: Transition and reactivation (recall) + run_sequence(G, node, [Transition(), Emission()]) + + # Validate memory preservation and reactivation + assert abs(G.nodes[node][EPI_PRIMARY] - memory_epi) < 0.2, ( + "Structural identity preserved through silence cycle" + ) + assert G.nodes[node][VF_PRIMARY] > 0.5, "Node active again" + + +class TestSHALatencyStateTracking: + """Additional tests for SHA latency state attributes.""" + + def test_sha_sets_latency_attributes(self): + """Validate SHA sets latency state tracking attributes.""" + G, node = create_nfr("latency_test", epi=0.50, vf=1.00) + + run_sequence(G, node, [Silence()]) + + # Check latency attributes + assert G.nodes[node].get("latent") == True, "Latent flag set" + assert "latency_start_time" in G.nodes[node], "Start time recorded" + assert "preserved_epi" in G.nodes[node], "EPI preserved" + assert G.nodes[node]["preserved_epi"] == pytest.approx(0.50, abs=0.01), ( + "Preserved EPI matches initial" + ) + assert "silence_duration" in G.nodes[node], "Duration tracker initialized" + + def test_sha_preserved_epi_matches_current(self): + """Validate preserved_epi attribute matches actual EPI at silence entry.""" + G, node = create_nfr("preserve_test", epi=0.73, vf=1.10) + + # Apply some operators first + run_sequence(G, node, [Emission(), Coherence()]) + epi_before_silence = G.nodes[node][EPI_PRIMARY] + + # Apply SHA + run_sequence(G, node, [Silence()]) + + preserved_epi = G.nodes[node].get("preserved_epi", 0.0) + assert abs(preserved_epi - epi_before_silence) < 0.01, ( + "Preserved EPI must match EPI at silence entry" + ) From 64f6341f1092dfe7ae04297bb349ab9931a542c8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 20:43:38 +0000 Subject: [PATCH 3/4] [SHA Tests + Grammar] Complete regression suite for Silence operator & Remove R5 frequency validation Co-authored-by: fermga <203334638+fermga@users.noreply.github.com> --- src/tnfr/operators/grammar.py | 74 ++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/src/tnfr/operators/grammar.py b/src/tnfr/operators/grammar.py index 217331e49..75bc01b84 100644 --- a/src/tnfr/operators/grammar.py +++ b/src/tnfr/operators/grammar.py @@ -917,13 +917,15 @@ def _validate_transition( ) # R5: Validate structural frequency transitions using ONLY canonical TNFR physics - freq_valid, freq_msg = validate_frequency_transition(prev, curr) - if not freq_valid: - # Invalid frequency transition violates TNFR structural dynamics - raise SequenceSyntaxError( - index=index, - token=token, - message=f"{operator_display_name(curr)} invalid after {operator_display_name(prev)}: {freq_msg}", + # Frequency transitions now generate warnings for suboptimal patterns, not errors + freq_valid, freq_msg, freq_is_warning = validate_frequency_transition(prev, curr) + if freq_is_warning: + # Suboptimal frequency transition - warn but allow + import warnings + warnings.warn( + f"{operator_display_name(curr)} after {operator_display_name(prev)}: {freq_msg}", + UserWarning, + stacklevel=3 ) def _has_recent_destabilizer(self, current_index: int) -> bool: @@ -1648,7 +1650,7 @@ class StructuralPattern(Enum): def validate_frequency_transition( prev_operator: str, next_operator: str -) -> tuple[bool, str]: +) -> tuple[bool, str, bool]: """Validate structural frequency transition between consecutive operators (R5 rule). Parameters @@ -1660,25 +1662,33 @@ def validate_frequency_transition( Returns ------- - tuple[bool, str] - (is_valid, message) where is_valid indicates if transition respects frequency - harmonics, and message provides context when invalid. + tuple[bool, str, bool] + (is_valid, message, is_warning) where: + - is_valid: Always True (transitions now allowed but may warn) + - message: Context about the transition quality + - is_warning: True if transition is suboptimal (should warn) Notes ----- - Structural frequency transitions follow TNFR canonical rules: - - High ↔ Medium: Bidirectional, natural energy exchange - - High → Zero: High energy can pause (containment, closure via SHA terminator) - - Medium ↔ Zero: Stabilization can pause, pause can resume - - High ↔ High: High energy operators can chain directly - - **Invalid**: Zero → High without Medium intermediary - - Special case: OZ → SHA (dissonance → silence) is valid and represents - contained dissonance, postponed conflict, or preserved tension for later processing. - This is a canonical therapeutic and crisis management pattern. - - This validation generates errors (not warnings) to enforce structural coherence - and prevent incoherent state transitions. + Structural frequency transitions are now treated as **heuristic guidance** + rather than hard constraints. The high/medium/zero classification describes + operator characteristics but doesn't impose physical impossibility. + + Frequency tiers: + - High ↔ Medium: Natural, smooth energy exchange + - High → Zero: Natural containment (e.g., OZ → SHA) + - Medium ↔ Zero: Natural stabilization/reactivation + - High ↔ High: Natural high-energy chaining + - **Suboptimal**: Zero → High (abrupt reactivation) + + Zero → High transitions (e.g., SHA → AL) are **physically possible** per + ∂EPI/∂t = νf · ΔNFR - the operator can modify νf. However, they may be + structurally abrupt. A warning suggests considering an intermediate step + (e.g., SHA → NAV → AL) for smoother transitions. + + This aligns frequency validation with the 4 fundamental TNFR constraints + (C1-C4) which emerge directly from physics, while treating frequency tiers + as descriptive metrics for code quality. """ # Get frequency levels for both operators prev_freq = STRUCTURAL_FREQUENCIES.get(prev_operator) @@ -1686,20 +1696,22 @@ def validate_frequency_transition( # If either operator is unknown, skip validation (compatibility handles this) if prev_freq is None or next_freq is None: - return True, "" + return True, "", False - # Check if transition is allowed + # Check if transition is in the "smooth" set allowed_targets = FREQUENCY_TRANSITIONS.get(prev_freq, set()) if next_freq not in allowed_targets: + # Transition is suboptimal but not forbidden prev_display = operator_display_name(prev_operator) next_display = operator_display_name(next_operator) - return ( - False, - f"Incoherent frequency transition: {prev_display} ({prev_freq}) → {next_display} ({next_freq}). " - f"Valid transitions from {prev_freq}: {', '.join(sorted(allowed_targets))}", + warning_msg = ( + f"Abrupt frequency transition: {prev_display} ({prev_freq}) → {next_display} ({next_freq}). " + f"Consider intermediate operator for smoother transition. " + f"Recommended from {prev_freq}: {', '.join(sorted(allowed_targets))}" ) + return True, warning_msg, True # Valid but with warning - return True, "" + return True, "", False # Smooth transition # R5: Regenerative cycle constants and types (from cycle_detection module) From a5d071c75dacf6c676ef2c12df9c250d6ffccdf7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 21:05:39 +0000 Subject: [PATCH 4/4] [Grammar] Remove R5 frequency validation, keep 3 fundamental constraints (C1-C3) Co-authored-by: fermga <203334638+fermga@users.noreply.github.com> --- src/tnfr/operators/grammar.py | 340 ++++++-------------- src/tnfr/validation/rules.py | 43 +-- src/tnfr/visualization/sequence_plotter.py | 99 +++--- tests/unit/operators/test_sha_regression.py | 23 +- 4 files changed, 166 insertions(+), 339 deletions(-) diff --git a/src/tnfr/operators/grammar.py b/src/tnfr/operators/grammar.py index 75bc01b84..215438a7e 100644 --- a/src/tnfr/operators/grammar.py +++ b/src/tnfr/operators/grammar.py @@ -21,12 +21,12 @@ must end in physically coherent states. **Structural Dynamics:** -- **Start (R1)**: Generators create structural patterns from potential +- **Start**: Generators create structural patterns from potential * AL (Emission): Generates EPI from vacuum via emission * NAV (Transition): Activates latent EPI through regime shift * REMESH (Recursivity): Echoes dormant structure across scales -- **End (R3)**: Four fundamental closure types from physics +- **End**: Four fundamental closure types from physics * SHA (Silence): Terminal closure - freezes evolution (νf → 0) * NAV (Transition): Handoff closure - transfers to next regime * REMESH (Recursivity): Recursive closure - distributes across scales @@ -45,40 +45,7 @@ Like physical waves: must have emission source and absorption/reflection end. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -C2: CONTINUITY -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -**Physical Basis:** -The nodal equation is a differential equation - its solution ∂EPI/∂t must -be continuous. Discontinuous frequency jumps violate calculus fundamentals -and create undefined structural states. - -**Structural Dynamics (R5):** -Frequency (νf) represents reorganization rate. Natural transitions: -- **high → medium**: Dampening (energy dissipation, natural relaxation) -- **high → zero**: Containment (active freezing via SHA) -- **medium → high**: Activation (energy injection, stimulation) -- **zero → medium**: Awakening (gradual activation from dormancy) -- **FORBIDDEN: zero → high**: Discontinuous jump violates physics - -**Why Forbidden?** -Zero (SHA) means νf = 0 (frozen). Jumping to high (AL, OZ) means sudden -νf ≫ 0. This is non-physical: ∂νf/∂t → ∞ (infinite acceleration). - -Like temperature: you can't jump from 0K to 1000K instantly - violates -thermodynamics. Must pass through intermediate states. - -**Physical Interpretation:** -Structural frequency behaves like field intensity. Changes propagate through -continuous paths. Discontinuities would create singularities (infinite ∂²EPI/∂t²), -causing structural fracture and collapse. - -**Harmonic Resonance:** -Compatible frequencies resonate; incompatible ones interfere destructively. -Natural paths preserve phase coherence across operator transitions. - -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -C3: BOUNDEDNESS +C2: BOUNDEDNESS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ **Physical Basis:** @@ -88,7 +55,7 @@ If the integral diverges (→ ∞), EPI becomes unbounded and system collapses. Stabilizers provide negative feedback that ensures integral convergence. -**Structural Dynamics (R2):** +**Structural Dynamics:** Without stabilizers, ΔNFR can grow unbounded through positive feedback: - Destabilizers increase |ΔNFR| → higher ∂EPI/∂t → more change - More change → more ΔNFR → runaway divergence @@ -120,7 +87,7 @@ Other operators maintain or increase ΔNFR; only these can reliably bound it. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -C4: THRESHOLD PHYSICS +C3: THRESHOLD PHYSICS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ **Physical Basis:** @@ -133,19 +100,19 @@ Without sufficient |ΔNFR|, transformation attempts fail or create unstable states. Additionally, transformations from unstable bases amplify chaos. -**Structural Dynamics (R4 + R6):** +**Structural Dynamics:** **ZHIR (Mutation) - Two Requirements:** -1. **Prior IL** (R6): Establishes stable coherent base +1. **Prior IL**: Establishes stable coherent base - Without: Transformation from chaos → amplifies disorder - With: Transformation from order → controlled phase change -2. **Recent Destabilizer** (R4): Generates threshold ΔNFR +2. **Recent Destabilizer**: Generates threshold ΔNFR - Without: Insufficient energy for bifurcation (attempt fails) - With: System crosses critical point (transformation succeeds) **THOL (Self-organization) - One Requirement:** -1. **Recent Destabilizer** (R4): Provides substrate for self-organization +1. **Recent Destabilizer**: Provides substrate for self-organization - Without: No disorder to organize (nothing to structure) - With: Sufficient ΔNFR drives spontaneous ordering @@ -188,14 +155,14 @@ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -Legacy Rule Names (for backward compatibility) ----------------------------------------------- -R1 = C1 (start operators) -R2 = C3 (stabilizer requirement) -R3 = C1 (end operators) -R4 = C4 (bifurcation context) -R5 = C2 (frequency continuity) -R6 = C4 (controlled mutation) +Summary: Three Fundamental Constraints from TNFR Physics +--------------------------------------------------------- +**C1: EXISTENCE & CLOSURE** - Valid start/end states (from ∂EPI/∂t = νf · ΔNFR) +**C2: BOUNDEDNESS** - Stabilizers prevent divergence (from ∫ νf · ΔNFR dt) +**C3: THRESHOLD PHYSICS** - Bifurcations require context (from bifurcation theory) + +These emerge directly from ∂EPI/∂t = νf · ΔNFR(t) and are the ONLY +hard constraints in TNFR grammar. All other patterns are descriptive. Physics-Based Operator Derivation ---------------------------------- @@ -308,11 +275,8 @@ "FUNCTION_TO_GLYPH", "GLYPH_TO_FUNCTION", "STRUCTURAL_FREQUENCIES", - "DUAL_FREQUENCY_OPERATORS", - "FREQUENCY_TRANSITIONS", "glyph_function_name", "function_name_to_glyph", - "validate_frequency_transition", "REGENERATORS", "MIN_CYCLE_LENGTH", "MAX_CYCLE_LENGTH", @@ -707,13 +671,13 @@ def __init__(self) -> None: self._open_thol = False self._unknown_tokens: list[tuple[int, str]] = [] self._found_dissonance = False # Legacy: Track OZ for backward compatibility - self._found_stabilizer = False # Track IL or THOL for R2 + self._found_stabilizer = False # Track IL or THOL for C2 (boundedness) self._detected_pattern: StructuralPattern = StructuralPattern.UNKNOWN # Legacy: Use deque with maxlen for backward compatibility self._bifurcation_context: deque[tuple[str, int]] = deque( maxlen=BIFURCATION_WINDOW ) - # R4 Extended: Track destabilizers by intensity level with graduated windows + # C3: Track destabilizers by intensity level with graduated windows self._destabilizer_context: dict[str, deque[int]] = { "strong": deque(maxlen=BIFURCATION_WINDOWS["strong"]), "moderate": deque(maxlen=BIFURCATION_WINDOWS["moderate"]), @@ -741,7 +705,7 @@ def _consume(self, token: str, index: int) -> None: if canonical not in OPERATORS: self._unknown_tokens.append((index, token)) - # R1: Validate start (already implemented) + # C1: Validate start (generators required) if index == 0: if canonical not in VALID_START_OPERATORS: expected = _format_token_group(_CANONICAL_START) @@ -761,11 +725,11 @@ def _consume(self, token: str, index: int) -> None: elif self._found_coherence and canonical in INTERMEDIATE_OPERATORS: self._seen_intermediate = True - # R2: Track stabilizers (IL or THOL) + # C2: Track stabilizers (IL or THOL) for boundedness if canonical in {COHERENCE, SELF_ORGANIZATION}: self._found_stabilizer = True - # R4 Extended: Track destabilizers by intensity level + # C3: Track destabilizers by intensity level for bifurcation context if canonical in DESTABILIZERS_STRONG: self._destabilizer_context["strong"].append(index) # Legacy: also populate old context for backward compatibility @@ -777,7 +741,7 @@ def _consume(self, token: str, index: int) -> None: elif canonical in DESTABILIZERS_WEAK: self._destabilizer_context["weak"].append(index) - # R4 Extended: Validate transformers (ZHIR/THOL) require recent destabilizer + # C3: Validate transformers (ZHIR/THOL) require recent destabilizer if canonical in TRANSFORMERS: if not self._has_graduated_destabilizer(index): raise SequenceSyntaxError( @@ -884,7 +848,7 @@ def _validate_transition( if prev not in OPERATORS: return - # SHA-specific validations (R6: Silence operator compatibility) + # SHA-specific validations (operator-level compatibility) if prev == SILENCE: if curr == DISSONANCE: # SHA → OZ: Silence followed by Dissonance is contradictory @@ -916,17 +880,6 @@ def _validate_transition( ), ) - # R5: Validate structural frequency transitions using ONLY canonical TNFR physics - # Frequency transitions now generate warnings for suboptimal patterns, not errors - freq_valid, freq_msg, freq_is_warning = validate_frequency_transition(prev, curr) - if freq_is_warning: - # Suboptimal frequency transition - warn but allow - import warnings - warnings.warn( - f"{operator_display_name(curr)} after {operator_display_name(prev)}: {freq_msg}", - UserWarning, - stacklevel=3 - ) def _has_recent_destabilizer(self, current_index: int) -> bool: """Check if a destabilizer exists within the bifurcation window. @@ -1024,7 +977,7 @@ def _has_graduated_destabilizer(self, current_index: int) -> bool: Notes ----- - This method implements R4 Extended graduated destabilization: + This method implements C3 (Threshold Physics) graduated destabilization: - Strong destabilizers (OZ): window of 4 operators - Moderate destabilizers (NAV, VAL): window of 2 operators - Weak destabilizers (EN): must be immediate predecessor (window of 1) @@ -1035,9 +988,9 @@ def _has_graduated_destabilizer(self, current_index: int) -> bool: RECEPTION (EN) Context Validation: EN as weak destabilizer requires validation of structural context. - According to DUAL_FREQUENCY_OPERATORS, EN has medium base frequency - but can generate ΔNFR when capturing external coherence into a prepared - node. This validation ensures EN → ZHIR transitions are structurally sound. + EN has medium base frequency but can generate ΔNFR when capturing + external coherence into a prepared node. This validation ensures + EN → ZHIR transitions are structurally sound. """ # Check strong destabilizers (longest window = 4) if self._destabilizer_context["strong"]: @@ -1131,9 +1084,9 @@ def _validate_thol_subsequence( ) from e def _validate_threshold_physics(self, sequence: Sequence[str]) -> None: - """C4: Validate threshold physics - transformations require context. + """C3: Validate threshold physics - transformations require context. - Constraint C4 (Threshold Physics): Bifurcations require crossing + Constraint C3 (Threshold Physics): Bifurcations require crossing critical thresholds from TNFR dynamical systems physics. This validates controlled mutation (IL → ZHIR), ensuring phase @@ -1152,7 +1105,7 @@ def _validate_threshold_physics(self, sequence: Sequence[str]) -> None: Notes ----- - **C4 Physical Foundation:** + **C3 Physical Foundation:** From bifurcation theory: phase transitions require |ΔNFR| > threshold. ZHIR (mutation) is a structural bifurcation that requires: @@ -1160,7 +1113,7 @@ def _validate_threshold_physics(self, sequence: Sequence[str]) -> None: 1. **Stable Base** (prior IL): Transformation from chaos amplifies disorder. IL provides coherent attractor from which to bifurcate safely. - 2. **Threshold Energy** (recent destabilizer): Validated by R4 in _accept(). + 2. **Threshold Energy** (recent destabilizer): Validated by C3 in _accept(). Destabilizers generate ΔNFR needed to cross critical point. **Why This Is Natural, Not Arbitrary:** @@ -1181,14 +1134,14 @@ def _validate_threshold_physics(self, sequence: Sequence[str]) -> None: Without proper setup: jump fails or lands in unstable region. - **What C4 Validates:** + **What C3 Validates:** - ZHIR requires prior IL (controlled mutation) - IL must precede ZHIR (stable base before transformation) - **What C4 Does NOT Validate:** + **What C3 Does NOT Validate:** - - Recent destabilizer for ZHIR (handled by R4 in _accept()) + - Recent destabilizer for ZHIR (handled by C3 in _accept()) - Destabilizer/stabilizer balance (context-dependent, not universal) - Net ΔNFR state (depends on initial conditions) @@ -1196,18 +1149,18 @@ def _validate_threshold_physics(self, sequence: Sequence[str]) -> None: THOL (self-organization) also requires recent destabilizer but NOT prior IL. THOL creates its own stability through autopoiesis. - This is validated by R4 in _accept(), not here. + This is validated by C3 in _accept(), not here. **Legacy Context:** - This replaces the overly complex R6 "operational closure" validation - that attempted to validate balance. Analysis revealed balance is - context-dependent and multi-sequence patterns provide systemic closure. + This replaces overly complex validation attempts that tried to enforce + balance. Analysis revealed balance is context-dependent and multi-sequence + patterns provide systemic closure. - C4 focuses on the ONE constraint that's truly universal: transformations + C3 focuses on the ONE constraint that's truly universal: transformations from stable bases with threshold energy. """ - # C4: Controlled mutation validation + # C3: Controlled mutation validation if MUTATION in sequence: # ZHIR requires prior coherence base for stable transformation if COHERENCE not in sequence: @@ -1215,7 +1168,7 @@ def _validate_threshold_physics(self, sequence: Sequence[str]) -> None: index=sequence.index(MUTATION), token=MUTATION, message=( - f"C4: {operator_display_name(MUTATION)} (phase transformation) requires " + f"C3: {operator_display_name(MUTATION)} (phase transformation) requires " f"prior {operator_display_name(COHERENCE)} for stable structural foundation. " f"Controlled mutation: {operator_display_name(COHERENCE)} → {operator_display_name(MUTATION)} " f"ensures transformation occurs from coherent base state (THRESHOLD PHYSICS constraint)." @@ -1230,7 +1183,7 @@ def _validate_threshold_physics(self, sequence: Sequence[str]) -> None: index=mutation_idx, token=MUTATION, message=( - f"C4: {operator_display_name(MUTATION)} must follow {operator_display_name(COHERENCE)}. " + f"C3: {operator_display_name(MUTATION)} must follow {operator_display_name(COHERENCE)}. " f"Transformation requires coherent base BEFORE bifurcation. " f"Current order: {operator_display_name(MUTATION)} before {operator_display_name(COHERENCE)} (invalid)." ), @@ -1240,7 +1193,7 @@ def _validate_threshold_physics(self, sequence: Sequence[str]) -> None: def _finalize(self, names: Sequence[str]) -> None: """Finalize sequence validation through natural TNFR constraints. - Validates sequences against the four natural constraints that emerge + Validates sequences against the three natural constraints that emerge from TNFR physics, not arbitrary rules. Raises @@ -1262,10 +1215,10 @@ def _finalize(self, names: Sequence[str]) -> None: # ═══════════════════════════════════════════════════════════════════ # From ∂EPI/∂t = νf · ΔNFR: EPI must exist (start) and end coherently - # C1.1: Start validation (legacy R1) + # C1.1: Start validation # Already validated in _accept() for first operator - # C1.2: End validation (legacy R3) + # C1.2: End validation # TNFR Encapsulation: Check last operator NOT inside THOL window # Sub-EPIs are independent nodes (operational fractality), so operators # inside THOL windows don't count toward main sequence ending. @@ -1285,11 +1238,11 @@ def _finalize(self, names: Sequence[str]) -> None: f"(operational fractality) and don't count as sequence ending.", ) - # NOTE: C1.3 Reception→Coherence segment validation removed - # This requirement does NOT derive from the 4 physical constraints (C1-C4). + # NOTE: Reception→Coherence segment validation removed + # This requirement does NOT derive from the 3 physical constraints (C1-C3). # It was a heuristic for "structural foundation" but is not canonical. - # Sequences are valid if they satisfy C1 (start/end), C2 (continuity), - # C3 (stabilizer), and C4 (threshold physics) - nothing more. + # Sequences are valid if they satisfy C1 (start/end), C2 (stabilizer), + # and C3 (threshold physics) - nothing more. # # Removed validation: # if not (self._found_reception and self._found_coherence): @@ -1300,16 +1253,16 @@ def _finalize(self, names: Sequence[str]) -> None: # ) # ═══════════════════════════════════════════════════════════════════ - # C3: BOUNDEDNESS + # C2: BOUNDEDNESS # ═══════════════════════════════════════════════════════════════════ # From ∫ νf · ΔNFR dt: Integral must converge (stabilizer required) - # C3: Stabilizer requirement (legacy R2) + # C2: Stabilizer requirement if not self._found_stabilizer: raise SequenceSyntaxError( index=-1, token=None, - message=f"C3: missing stabilizer ({operator_display_name(COHERENCE)} or {operator_display_name(SELF_ORGANIZATION)}) - integral divergence (BOUNDEDNESS constraint)", + message=f"C2: missing stabilizer ({operator_display_name(COHERENCE)} or {operator_display_name(SELF_ORGANIZATION)}) - integral divergence (BOUNDEDNESS constraint)", ) # Self-organization bifurcation window closure @@ -1342,22 +1295,22 @@ def _finalize(self, names: Sequence[str]) -> None: self._open_thol = False # ═══════════════════════════════════════════════════════════════════ - # C2: CONTINUITY & C4: THRESHOLD PHYSICS + # C3: THRESHOLD PHYSICS # ═══════════════════════════════════════════════════════════════════ # Validated dynamically during sequence building in _accept() # Detect structural pattern self._detected_pattern = self._detect_pattern() - # C2: Validate regenerative cycles if pattern is REGENERATIVE (legacy R5) + # Validate regenerative cycles if pattern is REGENERATIVE if self._detected_pattern == StructuralPattern.REGENERATIVE: self._validate_regenerative_cycle() - # C4: Validate threshold physics - controlled mutation (legacy R6) + # C3: Validate threshold physics - controlled mutation self._validate_threshold_physics(self._canonical) def _validate_regenerative_cycle(self) -> None: - """Validate regenerative cycle structural requirements (C2 - CONTINUITY). + """Validate regenerative cycle structural requirements. Uses CycleDetector to ensure the sequence meets minimum standards for self-sustaining regenerative behavior. Regenerative cycles are @@ -1502,7 +1455,7 @@ class TransitionCompatibilityError(StructuralGrammarError): class MutationWithoutDissonanceError(StructuralGrammarError): - """ZHIR applied without OZ precedent (R4 violation).""" + """ZHIR applied without OZ precedent (C3 - Threshold Physics violation).""" class IncompatibleSequenceError(StructuralGrammarError): @@ -1514,7 +1467,7 @@ class IncompleteEncapsulationError(StructuralGrammarError): class MissingStabilizerError(StructuralGrammarError): - """Sequence missing required stabilizer (IL or THOL) - R2 violation.""" + """Sequence missing required stabilizer (IL or THOL) - C2 BOUNDEDNESS violation.""" class StructuralPattern(Enum): @@ -1578,143 +1531,54 @@ class StructuralPattern(Enum): UNKNOWN = "unknown" # Unclassified -# Structural frequency matrix (νf): Hz_str categories per operator -# R5: HARMONIC_FREQUENCIES validation (now active) -# Maps each operator to its structural frequency level following TNFR canonical theory. -# - high: Operators that initiate, amplify, concentrate, or pivot structure -# - medium: Operators that capture, stabilize, couple, or organize -# - zero: Operators that suspend reorganization while preserving form -STRUCTURAL_FREQUENCIES: dict[str, str] = { - EMISSION: "high", # AL: initiation/reorganization - RECEPTION: "medium", # EN: structural capture (base frequency) - COHERENCE: "medium", # IL: stabilization - DISSONANCE: "high", # OZ: tension - COUPLING: "medium", # UM: coupling - RESONANCE: "high", # RA: amplification - SILENCE: "zero", # SHA: pause (suspended) - EXPANSION: "medium", # VAL: volumetric exploration - CONTRACTION: "high", # NUL: concentration - SELF_ORGANIZATION: "medium", # THOL: autonomous cascades - MUTATION: "high", # ZHIR: threshold pivot - TRANSITION: "medium", # NAV: controlled hand-off - RECURSIVITY: "medium", # REMESH: fractal echo -} - -# Dual-role operators: operators with context-dependent destabilization capacity -# Resolves the structural inconsistency between base frequency (νf) and ΔNFR generation. +# ============================================================================== +# Structural Frequency Classification (Descriptive Metric) +# ============================================================================== +# These classifications describe the characteristic reorganization intensity +# of each operator based on TNFR physics. This is NOT a validation constraint +# but a descriptive metric useful for analysis, visualization, and understanding. # -# RECEPTION (EN) is the canonical dual-role operator: -# - Base frequency: "medium" (νf for structural capture) -# - Destabilization capacity: "weak" (can generate ΔNFR when integrating external coherence) -# - Condition: Requires prior coherence base for effective destabilization +# Physical Basis (from ∂EPI/∂t = νf · ΔNFR): +# Operators don't directly modify νf - they modify the structural state (EPI, ΔNFR). +# The frequency classification describes the INTENSITY of reorganization the operator +# induces, which manifests through changes in ΔNFR magnitude and structural dynamics. # -# Theoretical foundation (nodal equation): -# ∂EPI/∂t = νf · ΔNFR -# -# RECEPTION has medium νf (moderate reorganization rate) but can act as weak -# destabilizer in graduated bifurcation (R4) when capturing external coherence -# into a node with existing structural base. The external input can generate -# sufficient ΔNFR to enable transformers (ZHIR/THOL) despite medium base frequency. +# - HIGH: Operators that strongly increase |ΔNFR| → intense reorganization +# Examples: Generating structure (AL), injecting tension (OZ), amplifying (RA) # -# Context requirement: EN → ZHIR valid only when EN operates on coherent base -# (e.g., AL → EN → IL → EN → ZHIR). Direct EN → ZHIR without context violates -# structural coherence as medium νf alone cannot sustain high ΔNFR for mutation. -DUAL_FREQUENCY_OPERATORS: dict[str, dict[str, str]] = { - RECEPTION: { - "base_freq": "medium", - "destabilization_capacity": "weak", - "conditions": "requires_prior_coherence", - "rationale": ( - "Captures external coherence which can generate ΔNFR when " - "integrated into structurally prepared node" - ), - } -} +# - MEDIUM: Operators that moderately affect ΔNFR → gradual reorganization +# Examples: Capturing (EN), stabilizing (IL), coupling (UM), transitioning (NAV) +# +# - ZERO: Operators that freeze reorganization (νf → 0) → no structural change +# Examples: Silence (SHA) - EPI preserved, evolution paused +# +# Note: These do NOT constrain sequence validity. All operator transitions are +# valid per TNFR physics (only C1-C3 constraints apply). Frequencies are purely +# descriptive for understanding operator characteristics. -# Frequency compatibility: operators with harmonic frequencies can transition -# Valid transitions preserve structural coherence: -# - high ↔ medium: High energy can stabilize or stabilized can amplify -# - high → zero: High energy can pause (containment, closure) - SHA as terminator -# - medium ↔ zero: Stabilized can pause or resume from pause -# - high ↔ high: High energy can chain directly -# - zero → medium: Gradual reactivation from silence (structurally coherent) -# -# Invalid: zero → high (violates structural continuity - cannot jump from pause -# to high energy without intermediate stabilization) -FREQUENCY_TRANSITIONS: dict[str, set[str]] = { - "high": {"high", "medium", "zero"}, # Allow high → zero for SHA termination (OZ → SHA valid) - "medium": {"high", "medium", "zero"}, - "zero": {"medium"}, # Must transition through medium before high (structural coherence) +STRUCTURAL_FREQUENCIES: dict[str, str] = { + # HIGH: Intense reorganization (strongly affects |ΔNFR|) + EMISSION: "high", # AL: Activates from latency, initiates strong resonance + DISSONANCE: "high", # OZ: Injects controlled tension, increases |ΔNFR| significantly + RESONANCE: "high", # RA: Amplifies patterns, propagates high-intensity changes + CONTRACTION: "high", # NUL: Concentrates structure rapidly, high ΔNFR gradient + MUTATION: "high", # ZHIR: Phase transition, abrupt structural reorganization + + # MEDIUM: Moderate reorganization (gradual ΔNFR evolution) + RECEPTION: "medium", # EN: Captures external coherence, moderate integration + COHERENCE: "medium", # IL: Reduces |ΔNFR| gradually, stabilizes progressively + COUPLING: "medium", # UM: Synchronizes phase, moderate coordination dynamics + TRANSITION: "medium", # NAV: Controlled regime shift, managed reorganization + EXPANSION: "medium", # VAL: Explores structure space, gradual dilation + SELF_ORGANIZATION: "medium",# THOL: Emergent ordering, progressive self-structuring + RECURSIVITY: "medium", # REMESH: Fractal echo, distributed reorganization + + # ZERO: Paused reorganization (νf → 0, EPI frozen) + SILENCE: "zero", # SHA: Suspends evolution, preserves structure } -def validate_frequency_transition( - prev_operator: str, next_operator: str -) -> tuple[bool, str, bool]: - """Validate structural frequency transition between consecutive operators (R5 rule). - - Parameters - ---------- - prev_operator : str - Previous operator in canonical form (e.g., "emission", "coherence"). - next_operator : str - Next operator in canonical form (e.g., "dissonance", "resonance"). - - Returns - ------- - tuple[bool, str, bool] - (is_valid, message, is_warning) where: - - is_valid: Always True (transitions now allowed but may warn) - - message: Context about the transition quality - - is_warning: True if transition is suboptimal (should warn) - - Notes - ----- - Structural frequency transitions are now treated as **heuristic guidance** - rather than hard constraints. The high/medium/zero classification describes - operator characteristics but doesn't impose physical impossibility. - - Frequency tiers: - - High ↔ Medium: Natural, smooth energy exchange - - High → Zero: Natural containment (e.g., OZ → SHA) - - Medium ↔ Zero: Natural stabilization/reactivation - - High ↔ High: Natural high-energy chaining - - **Suboptimal**: Zero → High (abrupt reactivation) - - Zero → High transitions (e.g., SHA → AL) are **physically possible** per - ∂EPI/∂t = νf · ΔNFR - the operator can modify νf. However, they may be - structurally abrupt. A warning suggests considering an intermediate step - (e.g., SHA → NAV → AL) for smoother transitions. - - This aligns frequency validation with the 4 fundamental TNFR constraints - (C1-C4) which emerge directly from physics, while treating frequency tiers - as descriptive metrics for code quality. - """ - # Get frequency levels for both operators - prev_freq = STRUCTURAL_FREQUENCIES.get(prev_operator) - next_freq = STRUCTURAL_FREQUENCIES.get(next_operator) - - # If either operator is unknown, skip validation (compatibility handles this) - if prev_freq is None or next_freq is None: - return True, "", False - - # Check if transition is in the "smooth" set - allowed_targets = FREQUENCY_TRANSITIONS.get(prev_freq, set()) - if next_freq not in allowed_targets: - # Transition is suboptimal but not forbidden - prev_display = operator_display_name(prev_operator) - next_display = operator_display_name(next_operator) - warning_msg = ( - f"Abrupt frequency transition: {prev_display} ({prev_freq}) → {next_display} ({next_freq}). " - f"Consider intermediate operator for smoother transition. " - f"Recommended from {prev_freq}: {', '.join(sorted(allowed_targets))}" - ) - return True, warning_msg, True # Valid but with warning - - return True, "", False # Smooth transition - - -# R5: Regenerative cycle constants and types (from cycle_detection module) +# Regenerative cycle constants and types (from cycle_detection module) from .cycle_detection import ( REGENERATORS, MIN_CYCLE_LENGTH, @@ -1727,8 +1591,8 @@ def validate_frequency_transition( # Canonical Coherence Sequences # ============================================================================== # These sequences encode fundamental TNFR structural patterns involving the -# Coherence operator. Each sequence has been validated against grammar -# rules (R1-R5) and compatibility constraints. +# Coherence operator. Each sequence has been validated against the 3 fundamental +# constraints (C1-C3) and compatibility requirements. CANONICAL_IL_SEQUENCES: dict[str, dict[str, Any]] = { "EMISSION_COHERENCE": { diff --git a/src/tnfr/validation/rules.py b/src/tnfr/validation/rules.py index a7939b385..23a562477 100644 --- a/src/tnfr/validation/rules.py +++ b/src/tnfr/validation/rules.py @@ -229,43 +229,16 @@ def _check_thol_closure( def _check_compatibility(ctx: "GrammarContext", n, cand: Glyph | str) -> Glyph | str: """Verify canonical transition compatibility based on TNFR structural dynamics. - Uses only canonical mechanisms: - 1. STRUCTURAL_FREQUENCIES: Each operator's inherent frequency - 2. FREQUENCY_TRANSITIONS: Physics-based allowed transitions + Note: Frequency-based validation (R5) has been removed as it was not a + fundamental physical constraint. Only C1-C3 constraints remain: + - C1: EXISTENCE & CLOSURE (valid start/end) + - C2: BOUNDEDNESS (stabilizers required) + - C3: THRESHOLD PHYSICS (bifurcations need context) - This ensures grammar rules EMERGE NATURALLY from TNFR structure and dynamics - rather than being imposed through arbitrary compatibility tables. + These are validated in grammar.py, not here. This function now simply + allows all transitions - validation happens at sequence level. """ - - nd = ctx.G.nodes[n] - hist = nd.get("glyph_history") - prev = hist[-1] if hist else None - prev_glyph = coerce_glyph(prev) - cand_glyph = coerce_glyph(cand) - - if isinstance(prev_glyph, Glyph): - glyph_to_name, name_to_glyph = _functional_translators() - prev_name = glyph_to_name(prev_glyph) - if prev_name is None: - return cand - cand_name = glyph_to_name(cand_glyph if isinstance(cand_glyph, Glyph) else cand) - if cand_name is None: - return cand - - # Use ONLY frequency-based validation (canonical TNFR physics) - from ..operators import grammar as _grammar - - is_valid, error_msg = _grammar.validate_frequency_transition(prev_name, cand_name) - - if not is_valid: - order = (prev_name, cand_name) - raise _grammar.TransitionCompatibilityError( - rule="frequency-transition", - candidate=cand_name, - message=f"Frequency transition {prev_name} → {cand_name} violates TNFR structural dynamics: {error_msg}", - order=order, - ) - + # All transitions allowed - validation at sequence level via C1-C3 return cand diff --git a/src/tnfr/visualization/sequence_plotter.py b/src/tnfr/visualization/sequence_plotter.py index 40414889d..08323ce3c 100644 --- a/src/tnfr/visualization/sequence_plotter.py +++ b/src/tnfr/visualization/sequence_plotter.py @@ -35,7 +35,6 @@ canonical_operator_name, operator_display_name, ) -from ..operators.grammar import STRUCTURAL_FREQUENCIES, validate_frequency_transition from ..validation.compatibility import CompatibilityLevel, get_compatibility_level __all__ = ["SequenceVisualizer"] @@ -208,9 +207,9 @@ def plot_sequence_flow( category = _get_operator_category(op) node_color = OPERATOR_CATEGORY_COLORS.get(category, "#95a5a6") - # Get frequency for border styling - freq = STRUCTURAL_FREQUENCIES.get(op, "medium") - border_width = {"high": 3, "medium": 2, "zero": 1}.get(freq, 2) + # Note: Frequency-based styling removed (R5 constraint eliminated) + # All operators now use standard border width + border_width = 2 # Draw node circle = plt.Circle( @@ -585,15 +584,16 @@ def plot_pattern_analysis( return fig, ax - def plot_frequency_timeline( + def plot_operator_sequence( self, sequence: List[str], save_path: Optional[str] = None, ) -> Tuple[Figure, Axes]: - """Plot timeline of structural frequencies (νf) through the sequence. + """Plot simple timeline of operators through the sequence. - Shows how structural frequency evolves through the sequence, - highlighting valid and invalid transitions. + Shows operator progression through the sequence with category-based coloring. + Note: Frequency validation (R5) has been removed from TNFR grammar as it + was not a fundamental physical constraint. Parameters ---------- @@ -615,69 +615,58 @@ def plot_frequency_timeline( normalized = [canonical_operator_name(op) or op for op in sequence] - # Map frequency levels to numeric values - freq_values = {"high": 3, "medium": 2, "zero": 1} - - # Get frequencies for each operator - frequencies = [STRUCTURAL_FREQUENCIES.get(op, "medium") for op in normalized] - freq_numeric = [freq_values[f] for f in frequencies] - - # Check transition validity - valid_transitions = [] - for i in range(len(normalized) - 1): - is_valid, _ = validate_frequency_transition(normalized[i], normalized[i + 1]) - valid_transitions.append(is_valid) - - # Plot frequency line + # Map operators to categories for consistent visual grouping + categories = [_get_operator_category(op) for op in normalized] + category_values = { + "generator": 3, + "stabilizer": 2, + "transformer": 3, + "connector": 2, + "closure": 1, + } + y_values = [category_values.get(cat, 2) for cat in categories] + + # Plot operator line x_pos = np.arange(len(normalized)) - ax.plot(x_pos, freq_numeric, marker="o", markersize=10, linewidth=2.5, - color="#3498db", label="Frequency trajectory") + ax.plot(x_pos, y_values, marker="o", markersize=12, linewidth=2.5, + color="#3498db", label="Operator flow", zorder=2) - # Color segments by transition validity - for i in range(len(normalized) - 1): - color = "#2ecc71" if valid_transitions[i] else "#e74c3c" - ax.plot( - [x_pos[i], x_pos[i + 1]], - [freq_numeric[i], freq_numeric[i + 1]], - linewidth=6, alpha=0.3, color=color - ) - - # Annotate operators - for i, (op, freq) in enumerate(zip(normalized, frequencies)): + # Annotate operators with category colors + for i, (op, cat) in enumerate(zip(normalized, categories)): display_name = operator_display_name(op) or op - y_offset = 0.15 if i % 2 == 0 else -0.15 + y_offset = 0.2 if i % 2 == 0 else -0.2 + cat_color = OPERATOR_CATEGORY_COLORS.get(cat, "#95a5a6") ax.annotate( display_name, - xy=(x_pos[i], freq_numeric[i]), - xytext=(x_pos[i], freq_numeric[i] + y_offset), - ha="center", va="center" if y_offset > 0 else "center", - fontsize=9, weight="bold", - bbox=dict(boxstyle="round,pad=0.3", facecolor=FREQUENCY_COLORS[freq], alpha=0.7) + xy=(x_pos[i], y_values[i]), + xytext=(x_pos[i], y_values[i] + y_offset), + ha="center", va="center", + fontsize=10, weight="bold", + bbox=dict(boxstyle="round,pad=0.4", facecolor=cat_color, alpha=0.8, edgecolor="black", linewidth=1.5), + zorder=3 ) # Styling ax.set_yticks([1, 2, 3]) - ax.set_yticklabels(["Zero (SHA)", "Medium", "High"], fontsize=10) + ax.set_yticklabels(["Closure", "Moderate", "Intensive"], fontsize=11) ax.set_xticks(x_pos) - ax.set_xticklabels([f"Op {i+1}" for i in range(len(normalized))], fontsize=9) - ax.set_ylabel("Structural Frequency (νf)", fontsize=11, weight="bold") - ax.set_xlabel("Sequence Position", fontsize=11, weight="bold") - ax.set_title("TNFR Structural Frequency Timeline", fontsize=14, weight="bold", pad=20) + ax.set_xticklabels([f"Step {i+1}" for i in range(len(normalized))], fontsize=9) + ax.set_ylabel("Operator Intensity", fontsize=12, weight="bold") + ax.set_xlabel("Sequence Position", fontsize=12, weight="bold") + ax.set_title("TNFR Operator Sequence Timeline", fontsize=14, weight="bold", pad=20) ax.grid(axis="y", alpha=0.3, linestyle="--") ax.set_ylim(0.5, 3.5) - # Add legend for transition validity + # Add category legend legend_elements = [ - mpatches.Patch(color="#2ecc71", alpha=0.5, label="Valid transition"), - mpatches.Patch(color="#e74c3c", alpha=0.5, label="Invalid transition"), + mpatches.Patch(color=OPERATOR_CATEGORY_COLORS["generator"], label="Generator"), + mpatches.Patch(color=OPERATOR_CATEGORY_COLORS["stabilizer"], label="Stabilizer"), + mpatches.Patch(color=OPERATOR_CATEGORY_COLORS["transformer"], label="Transformer"), + mpatches.Patch(color=OPERATOR_CATEGORY_COLORS["connector"], label="Connector"), + mpatches.Patch(color=OPERATOR_CATEGORY_COLORS["closure"], label="Closure"), ] - ax.legend(handles=legend_elements, loc="upper right", fontsize=9) - - # Add frequency zones - ax.axhspan(2.5, 3.5, alpha=0.1, color="#e74c3c", label="High energy zone") - ax.axhspan(1.5, 2.5, alpha=0.1, color="#3498db", label="Medium zone") - ax.axhspan(0.5, 1.5, alpha=0.1, color="#95a5a6", label="Paused zone") + ax.legend(handles=legend_elements, loc="upper right", fontsize=9, ncol=2) plt.tight_layout() diff --git a/tests/unit/operators/test_sha_regression.py b/tests/unit/operators/test_sha_regression.py index c5e76f7b5..9b3529648 100644 --- a/tests/unit/operators/test_sha_regression.py +++ b/tests/unit/operators/test_sha_regression.py @@ -142,11 +142,11 @@ def test_sha_after_coherence_preserves_stability(self): assert abs(G.nodes[node][EPI_PRIMARY] - post_il_epi) < 0.05, "EPI preserved" def test_sha_to_emission_reactivation(self): - """Test 7: SHA → NAV → AL (reactivation from silence) - structurally coherent awakening. + """Test 7: SHA → AL (direct reactivation from silence) - now valid with C2 clarification. - TNFR Physics: Cannot jump zero → high (SHA → AL) directly. - Must transition through medium frequency: SHA → NAV → AL (zero → medium → high). - This respects structural continuity and prevents singularities. + TNFR Physics: AL (Emission) operator manages νf transition internally. + With R5 removed, SHA → AL is valid - operator encapsulates transition dynamics. + This aligns with C2: continuity is maintained WITHIN operators, not between them. """ G, node = create_nfr("sleeping", epi=0.55, vf=1.00) @@ -155,10 +155,10 @@ def test_sha_to_emission_reactivation(self): assert G.nodes[node][VF_PRIMARY] < 0.1, "Node in silence" silent_epi = G.nodes[node][EPI_PRIMARY] - # Phase 2: Reactivate through medium frequency (NAV) then high (AL) - run_sequence(G, node, [Transition(), Emission()]) + # Phase 2: Direct reactivation (SHA → AL now valid) + run_sequence(G, node, [Emission()]) - # Validate coherent reactivation + # Validate direct reactivation assert G.nodes[node][VF_PRIMARY] > 0.5, "Node reactivated" assert G.nodes[node][EPI_PRIMARY] >= silent_epi - 0.15, "EPI maintains structural identity" @@ -307,9 +307,10 @@ class TestSHAFullLifecycle: """Test G: Complete lifecycle including SHA.""" def test_sha_full_cycle_activation_silence_reactivation(self): - """Test 14: Complete cycle: AL → IL → SHA → NAV → AL. + """Test 14: Complete cycle: AL → IL → SHA → AL. - Simulates: learning → consolidation → memory → recall → use + Simulates: learning → consolidation → memory → direct recall + With R5 removed, SHA → AL is valid (operator manages transition). """ G, node = create_nfr("lifecycle", epi=0.25, vf=0.90) @@ -323,8 +324,8 @@ def test_sha_full_cycle_activation_silence_reactivation(self): assert G.nodes[node][VF_PRIMARY] < 0.1, "Memory consolidated" memory_epi = G.nodes[node][EPI_PRIMARY] - # Phase 3: Transition and reactivation (recall) - run_sequence(G, node, [Transition(), Emission()]) + # Phase 3: Direct reactivation (recall) - SHA → AL now valid + run_sequence(G, node, [Emission()]) # Validate memory preservation and reactivation assert abs(G.nodes[node][EPI_PRIMARY] - memory_epi) < 0.2, (