Skip to content

Commit 6ea6103

Browse files
Copilotfermga
andcommitted
Implement R6: Operational closure and coherence conservation validation
Co-authored-by: fermga <203334638+fermga@users.noreply.github.com>
1 parent e79458e commit 6ea6103

File tree

3 files changed

+677
-2
lines changed

3 files changed

+677
-2
lines changed

src/tnfr/operators/grammar.py

Lines changed: 252 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
"""Canonical grammar and sequence validation for structural operators.
22
3-
This module enforces TNFR canonical grammar rules (R1-R5) to ensure that
3+
This module enforces TNFR canonical grammar rules (R1-R6) to ensure that
44
operator sequences respect the fundamental physics of the nodal equation:
55
66
∂EPI/∂t = νf · ΔNFR(t)
77
8-
Grammar Rules (R1-R5)
8+
Grammar Rules (R1-R6)
99
---------------------
1010
R1: Start operators - Must be able to generate or activate EPI
1111
R2: Stabilizer requirement - Must contain IL (coherence) or THOL (self-organization)
1212
R3: End operators - Must stabilize reorganization or achieve operational closure
1313
R4: Bifurcation control - Transformers (ZHIR/THOL) require recent destabilizer
1414
R5: Frequency transitions - Must respect structural frequency harmonics
15+
R6: Operational closure - Sequences must complete coherent reorganization cycles
1516
1617
Physics-Based Operator Derivation
1718
----------------------------------
@@ -815,6 +816,252 @@ def _has_graduated_destabilizer(self, current_index: int) -> bool:
815816

816817
return False
817818

819+
def _compute_frequency_balance(self, sequence: Sequence[str]) -> float:
820+
"""Calculate net structural frequency balance of sequence.
821+
822+
Computes weighted sum of frequency levels to assess whether sequence
823+
tends toward activation (positive), silence (negative), or balance (neutral).
824+
825+
Parameters
826+
----------
827+
sequence : Sequence[str]
828+
Operator sequence in canonical form
829+
830+
Returns
831+
-------
832+
float
833+
> 0: Sequence tends toward activation
834+
~ 0: Sequence balanced
835+
< 0: Sequence tends toward silence/collapse
836+
837+
Notes
838+
-----
839+
Frequency weights from STRUCTURAL_FREQUENCIES:
840+
- high: +1.0 (AL, OZ, RA, NUL, ZHIR)
841+
- medium: +0.5 (EN, IL, UM, VAL, THOL, NAV, REMESH)
842+
- zero: -1.0 (SHA)
843+
844+
Balance indicates net reorganization tendency according to nodal equation:
845+
∂EPI/∂t = νf · ΔNFR. Negative balance suggests insufficient activation
846+
for sustained reorganization.
847+
"""
848+
freq_weights = {"high": 1.0, "medium": 0.5, "zero": -1.0}
849+
850+
balance = sum(
851+
freq_weights.get(STRUCTURAL_FREQUENCIES.get(op, "medium"), 0.5)
852+
for op in sequence
853+
)
854+
855+
return balance / len(sequence) if sequence else 0.0
856+
857+
def _check_structural_convergence(self, sequence: Sequence[str]) -> bool:
858+
"""Verify sequence ending doesn't create divergent state.
859+
860+
This validates the ending operator doesn't leave the system in an
861+
actively diverging state. The operational closure balance is validated
862+
separately in _check_operational_closure().
863+
864+
Parameters
865+
----------
866+
sequence : Sequence[str]
867+
Operator sequence in canonical form
868+
869+
Returns
870+
-------
871+
bool
872+
True if ending is acceptable (not divergent)
873+
874+
Notes
875+
-----
876+
Ending operator validation:
877+
878+
**Always acceptable:**
879+
- SHA (Silence): νf → 0 ⟹ ∂EPI/∂t → 0 (guaranteed convergence)
880+
881+
**Acceptable if balanced (validated by closure check):**
882+
- NAV (Transition): Regime handoff provides operational closure
883+
BUT is itself a destabilizer, so requires balance
884+
- REMESH (Recursivity): Fractal completion, doesn't destabilize
885+
886+
**Never acceptable:**
887+
- OZ (Dissonance): Leaves system with high |ΔNFR| and high νf,
888+
actively divergent without continuation
889+
890+
Theoretical justification:
891+
From nodal equation ∂EPI/∂t = νf · ΔNFR:
892+
- SHA: νf → 0, so ∂EPI/∂t → 0 regardless of ΔNFR (strongest)
893+
- NAV: Medium νf, generates ΔNFR, requires balance (validated separately)
894+
- REMESH: Medium νf, neutral on ΔNFR, operational closure
895+
- OZ: High νf, increases |ΔNFR|, leaves divergent state
896+
897+
The key insight: SHA guarantees convergence. NAV/REMESH provide
898+
operational closure but NAV is a destabilizer so needs balance.
899+
OZ creates divergence without providing closure mechanism.
900+
"""
901+
last_op = sequence[-1]
902+
903+
# SHA always acceptable (convergent: νf → 0)
904+
if last_op == SILENCE:
905+
return True
906+
907+
# OZ never acceptable as ending (divergent: high νf, high ΔNFR)
908+
if last_op == DISSONANCE:
909+
return False
910+
911+
# NAV/REMESH acceptable as endings (operational closure)
912+
# NAV's destabilization will be validated by closure balance check
913+
# REMESH is neutral, provides fractal closure
914+
if last_op in {TRANSITION, RECURSIVITY}:
915+
return True
916+
917+
# Unknown ending (shouldn't happen if R3 validated)
918+
return False
919+
920+
def _check_operational_closure(self, sequence: Sequence[str]) -> bool:
921+
"""Verify balance between destabilization and stabilization.
922+
923+
Operational closure requires that destabilizing operators are balanced
924+
by stabilizing operators, preventing runaway divergence or collapse.
925+
926+
Parameters
927+
----------
928+
sequence : Sequence[str]
929+
Operator sequence in canonical form
930+
931+
Returns
932+
-------
933+
bool
934+
True if sequence has operational closure
935+
936+
Notes
937+
-----
938+
Destabilizers (increase |ΔNFR|):
939+
- Strong: OZ (Dissonance)
940+
- Moderate: NAV (Transition), VAL (Expansion)
941+
- Weak: EN (Reception, context-dependent)
942+
943+
Stabilizers (reduce |ΔNFR| or achieve closure):
944+
- IL (Coherence)
945+
- THOL (Self-organization)
946+
- SHA (Silence)
947+
- RA (Resonance)
948+
949+
Closure conditions:
950+
1. destabilizers ≤ stabilizers (strict balance), OR
951+
2. Controlled mutation: ZHIR after IL (prepared phase change)
952+
953+
Exception for controlled mutation:
954+
IL → ZHIR sequences allow destabilizers > stabilizers because
955+
coherence provides stable base for phase transformation. This
956+
represents intentional structural reorganization, not collapse.
957+
"""
958+
# Count destabilizers (all levels)
959+
from ..config.operator_names import DESTABILIZERS_ALL
960+
961+
destabilizers = sum(
962+
1 for op in sequence
963+
if op in DESTABILIZERS_ALL
964+
)
965+
966+
# Count stabilizers
967+
stabilizers = sum(
968+
1 for op in sequence
969+
if op in {COHERENCE, SELF_ORGANIZATION, SILENCE, RESONANCE}
970+
)
971+
972+
# Strict balance: destabilizers ≤ stabilizers
973+
if destabilizers <= stabilizers:
974+
return True
975+
976+
# Exception: controlled mutation (IL before ZHIR)
977+
# This allows destabilizers > stabilizers when mutation is prepared
978+
if MUTATION in sequence and COHERENCE in sequence:
979+
# Check if coherence precedes mutation (stable base for transformation)
980+
coherence_idx = sequence.index(COHERENCE)
981+
mutation_idx = sequence.index(MUTATION)
982+
if coherence_idx < mutation_idx:
983+
return True
984+
985+
return False
986+
987+
def _validate_operational_closure(self, sequence: Sequence[str]) -> None:
988+
"""R6: Validate sequence completes coherent reorganization cycle.
989+
990+
Ensures sequences respect conservation of structural coherence and
991+
achieve operational closure according to TNFR canonical principles.
992+
993+
Parameters
994+
----------
995+
sequence : Sequence[str]
996+
Operator sequence in canonical form
997+
998+
Raises
999+
------
1000+
SequenceSyntaxError
1001+
If sequence violates operational closure or convergence requirements
1002+
1003+
Notes
1004+
-----
1005+
R6 validates three aspects of sequence coherence:
1006+
1007+
1. **Structural Convergence**: Sequence must end with operator that
1008+
stabilizes reorganization (∂EPI/∂t → stable state)
1009+
1010+
2. **Operational Closure**: Balance between destabilizers and stabilizers
1011+
prevents divergent or collapsing trajectories
1012+
1013+
3. **Frequency Balance** (future): Net frequency tendency should support
1014+
coherent reorganization (currently informational only)
1015+
1016+
Theoretical foundation:
1017+
From integrated nodal equation:
1018+
EPI(t_final) = EPI(t_initial) + ∫_{t_0}^{t_f} νf(t) · ΔNFR(t) dt
1019+
1020+
For physical coherence:
1021+
- Integral must converge (not diverge toward collapse)
1022+
- EPI_final must be stable (sustainable coherence)
1023+
- Reorganization must close (∂EPI/∂t → 0 at sequence end)
1024+
1025+
References:
1026+
- TNFR.pdf Section 2.1.4: Nodal stability conditions
1027+
- AGENTS.md Section 3: Canonical invariants
1028+
"""
1029+
# 1. Verify ending is not divergent (OZ rejected, NAV/REMESH/SHA OK)
1030+
if not self._check_structural_convergence(sequence):
1031+
last_op = sequence[-1]
1032+
raise SequenceSyntaxError(
1033+
index=-1,
1034+
token=None,
1035+
message=(
1036+
f"R6: Sequence ends with divergent operator {operator_display_name(last_op)}. "
1037+
f"{operator_display_name(DISSONANCE)} leaves system with high |ΔNFR| and high νf, "
1038+
f"creating actively divergent state without continuation. "
1039+
f"Acceptable endings: {operator_display_name(SILENCE)} (convergent), "
1040+
f"{operator_display_name(TRANSITION)}/{operator_display_name(RECURSIVITY)} (operational closure)."
1041+
),
1042+
)
1043+
1044+
# 2. Validate operational closure (destabilizer/stabilizer balance)
1045+
if not self._check_operational_closure(sequence):
1046+
raise SequenceSyntaxError(
1047+
index=-1,
1048+
token=None,
1049+
message=(
1050+
f"R6: Sequence lacks operational closure. "
1051+
f"Destabilizers exceed stabilizers without controlled mutation. "
1052+
f"Balance destabilization ({operator_display_name(DISSONANCE)}, {operator_display_name(TRANSITION)}, "
1053+
f"{operator_display_name(EXPANSION)}) with stabilization ({operator_display_name(COHERENCE)}, "
1054+
f"{operator_display_name(SELF_ORGANIZATION)}, {operator_display_name(SILENCE)})."
1055+
),
1056+
)
1057+
1058+
# 3. Compute frequency balance (informational - for future warnings)
1059+
# Currently not enforced as error, but could be used for telemetry
1060+
freq_balance = self._compute_frequency_balance(sequence)
1061+
# Store for metadata/telemetry
1062+
# Future: Could add warnings for extremely negative balance (< -0.5)
1063+
# indicating tendency toward collapse
1064+
8181065
def _finalize(self, names: Sequence[str]) -> None:
8191066
if self._unknown_tokens:
8201067
ordered = ", ".join(sorted({token for _, token in self._unknown_tokens}))
@@ -865,6 +1112,9 @@ def _finalize(self, names: Sequence[str]) -> None:
8651112
# R5: Validate regenerative cycles if pattern is REGENERATIVE
8661113
if self._detected_pattern == StructuralPattern.REGENERATIVE:
8671114
self._validate_regenerative_cycle()
1115+
1116+
# R6: Validate operational closure and coherence conservation
1117+
self._validate_operational_closure(self._canonical)
8681118

8691119
def _validate_regenerative_cycle(self) -> None:
8701120
"""Validate regenerative cycle structural requirements (R5).

tests/unit/operators/test_canonical_grammar_rules.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,9 @@ def test_detect_cyclic_pattern(self):
278278
Note: With coherence weighting, FRACTAL (NAV + RECURSIVITY) may be
279279
detected instead of CYCLIC when both are present, as FRACTAL represents
280280
deeper structural complexity (recursive structure across scales).
281+
282+
Note: R6 requires balance. NAV is a destabilizer, so multiple NAVs
283+
require additional stabilizers. Added IL to balance.
281284
"""
282285
result = validate_sequence(
283286
[
@@ -286,6 +289,7 @@ def test_detect_cyclic_pattern(self):
286289
COHERENCE,
287290
TRANSITION,
288291
RESONANCE,
292+
COHERENCE, # Additional stabilizer for balance (R6)
289293
TRANSITION,
290294
]
291295
)

0 commit comments

Comments
 (0)