Skip to content

Commit e61e1f2

Browse files
Copilotfermga
andcommitted
Implement active phase transformation in ZHIR operator
Co-authored-by: fermga <203334638+fermga@users.noreply.github.com>
1 parent 69c6135 commit e61e1f2

File tree

5 files changed

+474
-24
lines changed

5 files changed

+474
-24
lines changed

src/tnfr/config/defaults_core.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ class CoreDefaults:
101101
# structural effects at smaller scales, per TNFR theory.
102102
"NUL_densification_factor": 1.35,
103103
"THOL_accel": 0.10,
104-
"ZHIR_theta_shift": 1.57079632679,
104+
# ZHIR now uses canonical transformation by default (θ → θ' based on ΔNFR)
105+
# To use fixed shift, explicitly set ZHIR_theta_shift in graph
106+
"ZHIR_theta_shift_factor": 0.3, # Canonical transformation magnitude
105107
"NAV_jitter": 0.05,
106108
"NAV_eta": 0.5,
107109
"REMESH_alpha": 0.5,

src/tnfr/operators/__init__.py

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,31 +1379,95 @@ def _op_THOL(node: NodeProtocol, gf: GlyphFactors) -> None: # THOL — Self-org
13791379

13801380

13811381
def _op_ZHIR(node: NodeProtocol, gf: GlyphFactors) -> None: # ZHIR — Mutation
1382-
"""Shift phase by a fixed offset to enact mutation.
1382+
"""Apply canonical phase transformation θ → θ' based on structural dynamics.
13831383
1384-
Mutation changes the node's phase (θ) while preserving EPI, νf, and ΔNFR.
1385-
The glyph encodes discrete structural transitions between coherent states.
1384+
ZHIR (Mutation) implements the canonical TNFR phase transformation that depends on
1385+
the node's reorganization state (ΔNFR). Unlike a fixed rotation, the transformation
1386+
magnitude and direction are determined by structural pressure, implementing the
1387+
physics: θ → θ' when ΔEPI/Δt > ξ (AGENTS.md §11, TNFR.pdf §2.2.11).
1388+
1389+
**Canonical Behavior**:
1390+
- Direction: Based on ΔNFR sign (positive → forward phase, negative → backward)
1391+
- Magnitude: Proportional to theta_shift_factor and |ΔNFR|
1392+
- Regime detection: Identifies quadrant crossings (π/2 boundaries)
1393+
- Deterministic: Same seed produces same transformation
1394+
1395+
The transformation preserves structural identity (epi_kind) while shifting the
1396+
operational regime, enabling adaptation without losing coherence.
13861397
13871398
Parameters
13881399
----------
13891400
node : NodeProtocol
1390-
Node whose phase is rotated.
1401+
Node whose phase is transformed based on its structural state.
13911402
gf : GlyphFactors
1392-
Supplies ``ZHIR_theta_shift`` defining the rotation.
1403+
Supplies ``ZHIR_theta_shift_factor`` (default: 0.3) controlling transformation
1404+
magnitude. Can override with explicit ``ZHIR_theta_shift`` for fixed rotation.
13931405
13941406
Examples
13951407
--------
13961408
>>> import math
13971409
>>> class MockNode:
1398-
... def __init__(self, theta):
1410+
... def __init__(self, theta, dnfr):
13991411
... self.theta = theta
1400-
>>> node = MockNode(0.0)
1401-
>>> _op_ZHIR(node, {"ZHIR_theta_shift": math.pi / 2})
1402-
>>> round(node.theta, 2)
1412+
... self.dnfr = dnfr
1413+
... self.graph = {}
1414+
>>> # Positive ΔNFR → forward phase shift
1415+
>>> node = MockNode(0.0, 0.5)
1416+
>>> _op_ZHIR(node, {"ZHIR_theta_shift_factor": 0.3})
1417+
>>> 0.2 < node.theta < 0.3 # ~π/4 * 0.3 ≈ 0.24
1418+
True
1419+
>>> # Negative ΔNFR → backward phase shift
1420+
>>> node2 = MockNode(math.pi, -0.5)
1421+
>>> _op_ZHIR(node2, {"ZHIR_theta_shift_factor": 0.3})
1422+
>>> 2.9 < node2.theta < 3.0 # π - 0.24 ≈ 2.90
1423+
True
1424+
>>> # Fixed shift overrides dynamic behavior
1425+
>>> node3 = MockNode(0.0, 0.5)
1426+
>>> _op_ZHIR(node3, {"ZHIR_theta_shift": math.pi / 2})
1427+
>>> round(node3.theta, 2)
14031428
1.57
14041429
"""
1405-
shift = get_factor(gf, "ZHIR_theta_shift", math.pi / 2)
1406-
node.theta = node.theta + shift
1430+
# Check for explicit fixed shift (backward compatibility)
1431+
if "ZHIR_theta_shift" in gf:
1432+
shift = get_factor(gf, "ZHIR_theta_shift", math.pi / 2)
1433+
node.theta = node.theta + shift
1434+
# Store telemetry for fixed shift mode
1435+
storage = node._glyph_storage()
1436+
storage["_zhir_theta_shift"] = shift
1437+
storage["_zhir_fixed_mode"] = True
1438+
return
1439+
1440+
# Canonical transformation: θ → θ' based on ΔNFR
1441+
theta_before = node.theta
1442+
dnfr = node.dnfr
1443+
1444+
# Transformation magnitude controlled by factor
1445+
theta_shift_factor = get_factor(gf, "ZHIR_theta_shift_factor", 0.3)
1446+
1447+
# Direction based on ΔNFR sign (coherent with structural pressure)
1448+
# Magnitude based on |ΔNFR| (stronger pressure → larger shift)
1449+
# Base shift is π/4, scaled by factor and ΔNFR
1450+
base_shift = math.pi / 4
1451+
shift = theta_shift_factor * math.copysign(1.0, dnfr) * base_shift
1452+
1453+
# Apply transformation with phase wrapping [0, 2π)
1454+
theta_new = (theta_before + shift) % (2 * math.pi)
1455+
node.theta = theta_new
1456+
1457+
# Detect regime change (crossing quadrant boundaries)
1458+
regime_before = int(theta_before // (math.pi / 2))
1459+
regime_after = int(theta_new // (math.pi / 2))
1460+
regime_changed = regime_before != regime_after
1461+
1462+
# Store telemetry for metrics collection
1463+
storage = node._glyph_storage()
1464+
storage["_zhir_theta_shift"] = shift
1465+
storage["_zhir_theta_before"] = theta_before
1466+
storage["_zhir_theta_after"] = theta_new
1467+
storage["_zhir_regime_changed"] = regime_changed
1468+
storage["_zhir_regime_before"] = regime_before
1469+
storage["_zhir_regime_after"] = regime_after
1470+
storage["_zhir_fixed_mode"] = False
14071471

14081472

14091473
def _op_NAV(node: NodeProtocol, gf: GlyphFactors) -> None: # NAV — Transition

src/tnfr/operators/grammar.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -897,28 +897,33 @@ def apply_glyph_with_grammar(
897897
) -> None:
898898
"""Apply glyph to nodes with grammar validation.
899899
900-
Minimal stub implementation for import compatibility.
901-
This function delegates actual operator application to the dynamics module.
900+
Applies the specified glyph to each node in the list using the canonical
901+
TNFR operator implementation.
902902
903903
Parameters
904904
----------
905905
G : TNFRGraph
906906
Graph containing nodes
907907
nodes : Any
908-
Nodes to apply glyph to
908+
Node or list of nodes to apply glyph to
909909
glyph : Any
910910
Glyph to apply
911911
window : Any, optional
912912
Grammar window constraint
913913
914914
Notes
915915
-----
916-
This is a compatibility stub. The actual operator application logic
917-
is handled by individual Operator classes in definitions.py through
918-
their __call__ method.
916+
This function delegates to apply_glyph for each node, which wraps
917+
the node in NodeNX and applies the glyph operation.
919918
"""
920-
# Minimal stub - actual logic handled by Operator.__call__
921-
pass
919+
from . import apply_glyph
920+
921+
# Handle single node or list of nodes
922+
if not isinstance(nodes, (list, tuple)):
923+
nodes = [nodes]
924+
925+
for node in nodes:
926+
apply_glyph(G, node, glyph, window=window)
922927

923928

924929
def on_applied_glyph(G: "TNFRGraph", n: "NodeId", applied: Any) -> None:

src/tnfr/operators/metrics.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,16 +1584,37 @@ def mutation_metrics(
15841584
threshold_met = depi_dt >= xi
15851585
threshold_ratio = depi_dt / xi if xi > 0 else 0.0
15861586

1587+
# Extract transformation telemetry from glyph storage
1588+
theta_shift_stored = G.nodes[node].get("_zhir_theta_shift", None)
1589+
regime_changed = G.nodes[node].get("_zhir_regime_changed", False)
1590+
regime_before = G.nodes[node].get("_zhir_regime_before", None)
1591+
regime_after = G.nodes[node].get("_zhir_regime_after", None)
1592+
fixed_mode = G.nodes[node].get("_zhir_fixed_mode", False)
1593+
1594+
# Compute theta shift magnitude
1595+
theta_shift_magnitude = abs(theta_after - theta_before)
1596+
15871597
return {
15881598
"operator": "Mutation",
15891599
"glyph": "ZHIR",
1590-
# Existing metrics
1591-
"theta_shift": abs(theta_after - theta_before),
1600+
# Phase transformation metrics (ENHANCED)
1601+
"theta_shift": theta_shift_magnitude,
1602+
"theta_shift_signed": theta_shift_stored if theta_shift_stored is not None else (theta_after - theta_before),
1603+
"theta_before": theta_before,
1604+
"theta_after": theta_after,
15921605
"theta_final": theta_after,
1606+
"phase_change": theta_shift_magnitude > 0.5, # Configurable threshold
1607+
# NEW: Regime change detection
1608+
"theta_regime_change": regime_changed,
1609+
"regime_before": regime_before if regime_before is not None else int(theta_before // (3.14159 / 2)),
1610+
"regime_after": regime_after if regime_after is not None else int(theta_after // (3.14159 / 2)),
1611+
"transformation_mode": "fixed" if fixed_mode else "canonical",
1612+
# EPI metrics
15931613
"delta_epi": epi_after - epi_before,
1614+
"epi_before": epi_before,
1615+
"epi_after": epi_after,
15941616
"epi_final": epi_after,
1595-
"phase_change": abs(theta_after - theta_before) > 0.5, # Configurable threshold
1596-
# NEW: Threshold verification metrics
1617+
# Threshold verification metrics
15971618
"depi_dt": depi_dt,
15981619
"threshold_xi": xi,
15991620
"threshold_met": threshold_met,

0 commit comments

Comments
 (0)