tnfr 4.5.2__py3-none-any.whl → 6.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. tnfr/__init__.py +228 -49
  2. tnfr/__init__.pyi +40 -0
  3. tnfr/_compat.py +11 -0
  4. tnfr/_version.py +7 -0
  5. tnfr/_version.pyi +7 -0
  6. tnfr/alias.py +106 -21
  7. tnfr/alias.pyi +140 -0
  8. tnfr/cache.py +666 -512
  9. tnfr/cache.pyi +232 -0
  10. tnfr/callback_utils.py +2 -9
  11. tnfr/callback_utils.pyi +105 -0
  12. tnfr/cli/__init__.py +21 -7
  13. tnfr/cli/__init__.pyi +47 -0
  14. tnfr/cli/arguments.py +42 -20
  15. tnfr/cli/arguments.pyi +33 -0
  16. tnfr/cli/execution.py +54 -20
  17. tnfr/cli/execution.pyi +80 -0
  18. tnfr/cli/utils.py +0 -2
  19. tnfr/cli/utils.pyi +8 -0
  20. tnfr/config/__init__.py +12 -0
  21. tnfr/config/__init__.pyi +8 -0
  22. tnfr/config/constants.py +104 -0
  23. tnfr/config/constants.pyi +12 -0
  24. tnfr/{config.py → config/init.py} +11 -7
  25. tnfr/config/init.pyi +8 -0
  26. tnfr/config/operator_names.py +106 -0
  27. tnfr/config/operator_names.pyi +28 -0
  28. tnfr/config/presets.py +104 -0
  29. tnfr/config/presets.pyi +7 -0
  30. tnfr/constants/__init__.py +78 -24
  31. tnfr/constants/__init__.pyi +104 -0
  32. tnfr/constants/core.py +1 -2
  33. tnfr/constants/core.pyi +17 -0
  34. tnfr/constants/init.pyi +12 -0
  35. tnfr/constants/metric.py +4 -12
  36. tnfr/constants/metric.pyi +19 -0
  37. tnfr/constants_glyphs.py +9 -91
  38. tnfr/constants_glyphs.pyi +12 -0
  39. tnfr/dynamics/__init__.py +112 -634
  40. tnfr/dynamics/__init__.pyi +83 -0
  41. tnfr/dynamics/adaptation.py +201 -0
  42. tnfr/dynamics/aliases.py +22 -0
  43. tnfr/dynamics/coordination.py +343 -0
  44. tnfr/dynamics/dnfr.py +1936 -354
  45. tnfr/dynamics/dnfr.pyi +33 -0
  46. tnfr/dynamics/integrators.py +369 -75
  47. tnfr/dynamics/integrators.pyi +35 -0
  48. tnfr/dynamics/runtime.py +521 -0
  49. tnfr/dynamics/sampling.py +8 -5
  50. tnfr/dynamics/sampling.pyi +7 -0
  51. tnfr/dynamics/selectors.py +680 -0
  52. tnfr/execution.py +56 -41
  53. tnfr/execution.pyi +65 -0
  54. tnfr/flatten.py +7 -7
  55. tnfr/flatten.pyi +28 -0
  56. tnfr/gamma.py +54 -37
  57. tnfr/gamma.pyi +40 -0
  58. tnfr/glyph_history.py +85 -38
  59. tnfr/glyph_history.pyi +53 -0
  60. tnfr/grammar.py +19 -338
  61. tnfr/grammar.pyi +13 -0
  62. tnfr/helpers/__init__.py +110 -30
  63. tnfr/helpers/__init__.pyi +66 -0
  64. tnfr/helpers/numeric.py +1 -0
  65. tnfr/helpers/numeric.pyi +12 -0
  66. tnfr/immutable.py +55 -19
  67. tnfr/immutable.pyi +37 -0
  68. tnfr/initialization.py +12 -10
  69. tnfr/initialization.pyi +73 -0
  70. tnfr/io.py +99 -34
  71. tnfr/io.pyi +11 -0
  72. tnfr/locking.pyi +7 -0
  73. tnfr/metrics/__init__.pyi +20 -0
  74. tnfr/metrics/coherence.py +934 -294
  75. tnfr/metrics/common.py +1 -3
  76. tnfr/metrics/common.pyi +15 -0
  77. tnfr/metrics/core.py +192 -34
  78. tnfr/metrics/core.pyi +13 -0
  79. tnfr/metrics/diagnosis.py +707 -101
  80. tnfr/metrics/diagnosis.pyi +89 -0
  81. tnfr/metrics/export.py +27 -13
  82. tnfr/metrics/glyph_timing.py +218 -38
  83. tnfr/metrics/reporting.py +22 -18
  84. tnfr/metrics/reporting.pyi +12 -0
  85. tnfr/metrics/sense_index.py +199 -25
  86. tnfr/metrics/sense_index.pyi +9 -0
  87. tnfr/metrics/trig.py +53 -18
  88. tnfr/metrics/trig.pyi +12 -0
  89. tnfr/metrics/trig_cache.py +3 -7
  90. tnfr/metrics/trig_cache.pyi +10 -0
  91. tnfr/node.py +148 -125
  92. tnfr/node.pyi +161 -0
  93. tnfr/observers.py +44 -30
  94. tnfr/observers.pyi +46 -0
  95. tnfr/ontosim.py +14 -13
  96. tnfr/ontosim.pyi +33 -0
  97. tnfr/operators/__init__.py +84 -52
  98. tnfr/operators/__init__.pyi +31 -0
  99. tnfr/operators/definitions.py +181 -0
  100. tnfr/operators/definitions.pyi +92 -0
  101. tnfr/operators/jitter.py +86 -23
  102. tnfr/operators/jitter.pyi +11 -0
  103. tnfr/operators/registry.py +80 -0
  104. tnfr/operators/registry.pyi +15 -0
  105. tnfr/operators/remesh.py +141 -57
  106. tnfr/presets.py +9 -54
  107. tnfr/presets.pyi +7 -0
  108. tnfr/py.typed +0 -0
  109. tnfr/rng.py +259 -73
  110. tnfr/rng.pyi +14 -0
  111. tnfr/selector.py +24 -17
  112. tnfr/selector.pyi +19 -0
  113. tnfr/sense.py +55 -43
  114. tnfr/sense.pyi +30 -0
  115. tnfr/structural.py +44 -267
  116. tnfr/structural.pyi +46 -0
  117. tnfr/telemetry/__init__.py +13 -0
  118. tnfr/telemetry/verbosity.py +37 -0
  119. tnfr/tokens.py +3 -2
  120. tnfr/tokens.pyi +41 -0
  121. tnfr/trace.py +272 -82
  122. tnfr/trace.pyi +68 -0
  123. tnfr/types.py +345 -6
  124. tnfr/types.pyi +145 -0
  125. tnfr/utils/__init__.py +158 -0
  126. tnfr/utils/__init__.pyi +133 -0
  127. tnfr/utils/cache.py +755 -0
  128. tnfr/utils/cache.pyi +156 -0
  129. tnfr/{collections_utils.py → utils/data.py} +57 -90
  130. tnfr/utils/data.pyi +73 -0
  131. tnfr/utils/graph.py +87 -0
  132. tnfr/utils/graph.pyi +10 -0
  133. tnfr/utils/init.py +746 -0
  134. tnfr/utils/init.pyi +85 -0
  135. tnfr/{json_utils.py → utils/io.py} +13 -18
  136. tnfr/utils/io.pyi +10 -0
  137. tnfr/utils/validators.py +130 -0
  138. tnfr/utils/validators.pyi +19 -0
  139. tnfr/validation/__init__.py +25 -0
  140. tnfr/validation/__init__.pyi +17 -0
  141. tnfr/validation/compatibility.py +59 -0
  142. tnfr/validation/compatibility.pyi +8 -0
  143. tnfr/validation/grammar.py +149 -0
  144. tnfr/validation/grammar.pyi +11 -0
  145. tnfr/validation/rules.py +194 -0
  146. tnfr/validation/rules.pyi +18 -0
  147. tnfr/validation/syntax.py +151 -0
  148. tnfr/validation/syntax.pyi +7 -0
  149. tnfr-6.0.0.dist-info/METADATA +135 -0
  150. tnfr-6.0.0.dist-info/RECORD +157 -0
  151. tnfr/graph_utils.py +0 -84
  152. tnfr/import_utils.py +0 -228
  153. tnfr/logging_utils.py +0 -116
  154. tnfr/validators.py +0 -84
  155. tnfr/value_utils.py +0 -59
  156. tnfr-4.5.2.dist-info/METADATA +0 -379
  157. tnfr-4.5.2.dist-info/RECORD +0 -67
  158. {tnfr-4.5.2.dist-info → tnfr-6.0.0.dist-info}/WHEEL +0 -0
  159. {tnfr-4.5.2.dist-info → tnfr-6.0.0.dist-info}/entry_points.txt +0 -0
  160. {tnfr-4.5.2.dist-info → tnfr-6.0.0.dist-info}/licenses/LICENSE.md +0 -0
  161. {tnfr-4.5.2.dist-info → tnfr-6.0.0.dist-info}/top_level.txt +0 -0
tnfr/observers.pyi ADDED
@@ -0,0 +1,46 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from typing import Final
5
+
6
+ from .types import GlyphLoadDistribution, TNFRGraph
7
+
8
+ __all__: tuple[str, ...]
9
+
10
+ DEFAULT_GLYPH_LOAD_SPAN: Final[int]
11
+ DEFAULT_WBAR_SPAN: Final[int]
12
+
13
+
14
+ def _std_log(kind: str, G: TNFRGraph, ctx: Mapping[str, object]) -> None: ...
15
+
16
+
17
+ def attach_standard_observer(G: TNFRGraph) -> TNFRGraph: ...
18
+
19
+
20
+ def _ensure_nodes(G: TNFRGraph) -> bool: ...
21
+
22
+
23
+ def kuramoto_metrics(G: TNFRGraph) -> tuple[float, float]: ...
24
+
25
+
26
+ def phase_sync(
27
+ G: TNFRGraph,
28
+ R: float | None = ...,
29
+ psi: float | None = ...,
30
+ ) -> float: ...
31
+
32
+
33
+ def kuramoto_order(
34
+ G: TNFRGraph,
35
+ R: float | None = ...,
36
+ psi: float | None = ...,
37
+ ) -> float: ...
38
+
39
+
40
+ def glyph_load(
41
+ G: TNFRGraph,
42
+ window: int | None = ...,
43
+ ) -> GlyphLoadDistribution: ...
44
+
45
+
46
+ def wbar(G: TNFRGraph, window: int | None = ...) -> float: ...
tnfr/ontosim.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """Orchestrate the canonical simulation."""
2
2
 
3
3
  from __future__ import annotations
4
+
4
5
  from collections import deque
5
6
  from typing import TYPE_CHECKING
6
7
 
@@ -10,16 +11,16 @@ from .dynamics import step as _step, run as _run
10
11
  from .dynamics import default_compute_delta_nfr
11
12
  from .initialization import init_node_attrs
12
13
  from .glyph_history import append_metric
13
- from .import_utils import cached_import
14
+ from .utils import cached_import
14
15
 
15
16
  if TYPE_CHECKING: # pragma: no cover
16
- import networkx as nx # type: ignore[import-untyped]
17
+ import networkx as nx
17
18
 
18
- # API de alto nivel
19
- __all__ = ("preparar_red", "step", "run")
19
+ # High-level API exports
20
+ __all__ = ("prepare_network", "step", "run")
20
21
 
21
22
 
22
- def preparar_red(
23
+ def prepare_network(
23
24
  G: "nx.Graph",
24
25
  *,
25
26
  init_attrs: bool = True,
@@ -43,7 +44,7 @@ def preparar_red(
43
44
  from .constants import merge_overrides
44
45
 
45
46
  merge_overrides(G, **overrides)
46
- # Inicializaciones blandas
47
+ # Initialize history buffers
47
48
  ph_len = int(
48
49
  G.graph.get(
49
50
  "PHASE_HISTORY_MAXLEN", METRIC_DEFAULTS["PHASE_HISTORY_MAXLEN"]
@@ -59,7 +60,7 @@ def preparar_red(
59
60
  "sense_sigma_mag",
60
61
  "sense_sigma_angle",
61
62
  "iota",
62
- "glyph_load_estab",
63
+ "glyph_load_stabilizers",
63
64
  "glyph_load_disr",
64
65
  "Si_mean",
65
66
  "Si_hi_frac",
@@ -76,12 +77,12 @@ def preparar_red(
76
77
  "phase_disr": deque(maxlen=ph_len),
77
78
  }
78
79
  )
79
- G.graph.setdefault("history", history)
80
- # Memoria global de REMESH
80
+ history_ref = G.graph.setdefault("history", history)
81
+ # Global REMESH memory
81
82
  tau = int(get_param(G, "REMESH_TAU_GLOBAL"))
82
83
  maxlen = max(2 * tau + 5, 64)
83
84
  G.graph.setdefault("_epi_hist", deque(maxlen=maxlen))
84
- # Auto-attach del observador estándar si se pide
85
+ # Auto-attach the standard observer when requested
85
86
  if G.graph.get("ATTACH_STD_OBSERVER", False):
86
87
  attach_standard_observer = cached_import(
87
88
  "tnfr.observers",
@@ -95,7 +96,7 @@ def preparar_red(
95
96
  "_callback_errors",
96
97
  {"event": "attach_std_observer", "error": "ImportError"},
97
98
  )
98
- # Hook explícito para ΔNFR (se puede sustituir luego con
99
+ # Explicit hook for ΔNFR (can later be replaced with
99
100
  # dynamics.set_delta_nfr_hook)
100
101
  G.graph.setdefault("compute_delta_nfr", default_compute_delta_nfr)
101
102
  G.graph.setdefault("_dnfr_hook_name", "default_compute_delta_nfr")
@@ -110,8 +111,8 @@ def preparar_red(
110
111
  )
111
112
  G.graph.setdefault(
112
113
  "_CALLBACKS_DOC",
113
- "Interfaz Γ(R): registrar pares (name, func) con firma (G, ctx) "
114
- "en callbacks['before_step'|'after_step'|'on_remesh']",
114
+ "Γ(R) interface: register (name, func) pairs with signature (G, ctx) "
115
+ "in callbacks['before_step'|'after_step'|'on_remesh']",
115
116
  )
116
117
 
117
118
  if init_attrs:
tnfr/ontosim.pyi ADDED
@@ -0,0 +1,33 @@
1
+ from __future__ import annotations
2
+
3
+ from .types import TNFRConfigValue, TNFRGraph
4
+
5
+ __all__: tuple[str, ...]
6
+
7
+
8
+ def prepare_network(
9
+ G: TNFRGraph,
10
+ *,
11
+ init_attrs: bool = True,
12
+ override_defaults: bool = False,
13
+ **overrides: TNFRConfigValue,
14
+ ) -> TNFRGraph: ...
15
+
16
+
17
+ def step(
18
+ G: TNFRGraph,
19
+ *,
20
+ dt: float | None = None,
21
+ use_Si: bool = True,
22
+ apply_glyphs: bool = True,
23
+ ) -> None: ...
24
+
25
+
26
+ def run(
27
+ G: TNFRGraph,
28
+ steps: int,
29
+ *,
30
+ dt: float | None = None,
31
+ use_Si: bool = True,
32
+ apply_glyphs: bool = True,
33
+ ) -> None: ...
@@ -1,7 +1,9 @@
1
1
  """Network operators."""
2
2
 
3
3
  from __future__ import annotations
4
- from typing import Any, TYPE_CHECKING, Callable
4
+
5
+ from collections.abc import Callable, Iterator
6
+ from typing import Any, TYPE_CHECKING
5
7
  import math
6
8
  import heapq
7
9
  from itertools import islice
@@ -12,11 +14,12 @@ from ..constants import DEFAULTS, get_aliases, get_param
12
14
 
13
15
  from ..helpers.numeric import angle_diff
14
16
  from ..metrics.trig import neighbor_phase_mean
15
- from ..import_utils import get_nodonx
17
+ from ..utils import get_nodenx
16
18
  from ..rng import make_rng
17
19
  from tnfr import glyph_history
18
- from ..types import Glyph
20
+ from ..types import EPIValue, Glyph, NodeId, TNFRGraph
19
21
 
22
+ from . import definitions as _definitions
20
23
  from .jitter import (
21
24
  JitterCache,
22
25
  JitterCacheManager,
@@ -24,14 +27,38 @@ from .jitter import (
24
27
  reset_jitter_manager,
25
28
  random_jitter,
26
29
  )
30
+ from .registry import OPERATORS, discover_operators, get_operator_class
27
31
  from .remesh import (
28
32
  apply_network_remesh,
29
33
  apply_topological_remesh,
30
34
  apply_remesh_if_globally_stable,
31
35
  )
32
36
 
37
+ _remesh_doc = (
38
+ "Trigger a remesh once the stability window is satisfied.\n\n"
39
+ "Parameters\n----------\n"
40
+ "stable_step_window : int | None\n"
41
+ " Number of consecutive stable steps required before remeshing.\n"
42
+ " Only the English keyword 'stable_step_window' is supported."
43
+ )
44
+ if apply_remesh_if_globally_stable.__doc__:
45
+ apply_remesh_if_globally_stable.__doc__ += "\n\n" + _remesh_doc
46
+ else:
47
+ apply_remesh_if_globally_stable.__doc__ = _remesh_doc
48
+
49
+ discover_operators()
50
+
51
+ _DEFINITION_EXPORTS = {
52
+ name: getattr(_definitions, name)
53
+ for name in getattr(_definitions, "__all__", ())
54
+ }
55
+ globals().update(_DEFINITION_EXPORTS)
56
+
33
57
  if TYPE_CHECKING: # pragma: no cover - type checking only
34
- from ..node import NodoProtocol
58
+ from ..node import NodeProtocol
59
+
60
+ GlyphFactors = dict[str, Any]
61
+ GlyphOperation = Callable[["NodeProtocol", GlyphFactors], None]
35
62
 
36
63
  ALIAS_EPI = get_aliases("EPI")
37
64
 
@@ -49,15 +76,20 @@ __all__ = [
49
76
  "apply_network_remesh",
50
77
  "apply_topological_remesh",
51
78
  "apply_remesh_if_globally_stable",
79
+ "OPERATORS",
80
+ "discover_operators",
81
+ "get_operator_class",
52
82
  ]
53
83
 
84
+ __all__.extend(_DEFINITION_EXPORTS.keys())
85
+
54
86
 
55
- def get_glyph_factors(node: NodoProtocol) -> dict[str, Any]:
87
+ def get_glyph_factors(node: NodeProtocol) -> GlyphFactors:
56
88
  """Return glyph factors for ``node`` with defaults."""
57
89
  return node.graph.get("GLYPH_FACTORS", DEFAULTS["GLYPH_FACTORS"].copy())
58
90
 
59
91
 
60
- def get_factor(gf: dict[str, Any], key: str, default: float) -> float:
92
+ def get_factor(gf: GlyphFactors, key: str, default: float) -> float:
61
93
  """Return ``gf[key]`` as ``float`` with ``default`` fallback."""
62
94
  return float(gf.get(key, default))
63
95
 
@@ -67,7 +99,7 @@ def get_factor(gf: dict[str, Any], key: str, default: float) -> float:
67
99
  # -------------------------
68
100
 
69
101
 
70
- def get_neighbor_epi(node: NodoProtocol) -> tuple[list[NodoProtocol], float]:
102
+ def get_neighbor_epi(node: NodeProtocol) -> tuple[list[NodeProtocol], EPIValue]:
71
103
  """Return neighbour list and their mean ``EPI`` without mutating ``node``."""
72
104
 
73
105
  epi = node.EPI
@@ -98,11 +130,11 @@ def get_neighbor_epi(node: NodoProtocol) -> tuple[list[NodoProtocol], float]:
98
130
  return [], epi
99
131
  epi_bar = total / count if count else float(epi)
100
132
  if needs_conversion:
101
- NodoNX = get_nodonx()
102
- if NodoNX is None:
103
- raise ImportError("NodoNX is unavailable")
133
+ NodeNX = get_nodenx()
134
+ if NodeNX is None:
135
+ raise ImportError("NodeNX is unavailable")
104
136
  neigh = [
105
- v if hasattr(v, "EPI") else NodoNX.from_graph(node.G, v)
137
+ v if hasattr(v, "EPI") else NodeNX.from_graph(node.G, v)
106
138
  for v in neigh
107
139
  ]
108
140
  else:
@@ -115,7 +147,7 @@ def get_neighbor_epi(node: NodoProtocol) -> tuple[list[NodoProtocol], float]:
115
147
 
116
148
 
117
149
  def _determine_dominant(
118
- neigh: list[NodoProtocol], default_kind: str
150
+ neigh: list[NodeProtocol], default_kind: str
119
151
  ) -> tuple[str, float]:
120
152
  """Return dominant ``epi_kind`` among ``neigh`` and its absolute ``EPI``."""
121
153
  best_kind: str | None = None
@@ -131,7 +163,7 @@ def _determine_dominant(
131
163
 
132
164
 
133
165
  def _mix_epi_with_neighbors(
134
- node: NodoProtocol, mix: float, default_glyph: Glyph | str
166
+ node: NodeProtocol, mix: float, default_glyph: Glyph | str
135
167
  ) -> tuple[float, str]:
136
168
  """Mix ``EPI`` of ``node`` with the mean of its neighbours."""
137
169
  default_kind = (
@@ -156,22 +188,22 @@ def _mix_epi_with_neighbors(
156
188
  return epi_bar, final
157
189
 
158
190
 
159
- def _op_AL(node: NodoProtocol, gf: dict[str, Any]) -> None: # AL — Emisión
191
+ def _op_AL(node: NodeProtocol, gf: GlyphFactors) -> None: # AL — Emission
160
192
  f = get_factor(gf, "AL_boost", 0.05)
161
193
  node.EPI = node.EPI + f
162
194
 
163
195
 
164
- def _op_EN(node: NodoProtocol, gf: dict[str, Any]) -> None: # EN — Recepción
196
+ def _op_EN(node: NodeProtocol, gf: GlyphFactors) -> None: # EN — Reception
165
197
  mix = get_factor(gf, "EN_mix", 0.25)
166
198
  _mix_epi_with_neighbors(node, mix, Glyph.EN)
167
199
 
168
200
 
169
- def _op_IL(node: NodoProtocol, gf: dict[str, Any]) -> None: # IL — Coherencia
201
+ def _op_IL(node: NodeProtocol, gf: GlyphFactors) -> None: # IL — Coherence
170
202
  factor = get_factor(gf, "IL_dnfr_factor", 0.7)
171
203
  node.dnfr = factor * getattr(node, "dnfr", 0.0)
172
204
 
173
205
 
174
- def _op_OZ(node: NodoProtocol, gf: dict[str, Any]) -> None: # OZ — Disonancia
206
+ def _op_OZ(node: NodeProtocol, gf: GlyphFactors) -> None: # OZ — Dissonance
175
207
  factor = get_factor(gf, "OZ_dnfr_factor", 1.3)
176
208
  dnfr = getattr(node, "dnfr", 0.0)
177
209
  if bool(node.graph.get("OZ_NOISE_MODE", False)):
@@ -184,13 +216,13 @@ def _op_OZ(node: NodoProtocol, gf: dict[str, Any]) -> None: # OZ — Disonancia
184
216
  node.dnfr = factor * dnfr if abs(dnfr) > 1e-9 else 0.1
185
217
 
186
218
 
187
- def _um_candidate_iter(node: NodoProtocol):
219
+ def _um_candidate_iter(node: NodeProtocol) -> Iterator[NodeProtocol]:
188
220
  sample_ids = node.graph.get("_node_sample")
189
221
  if sample_ids is not None and hasattr(node, "G"):
190
- NodoNX = get_nodonx()
191
- if NodoNX is None:
192
- raise ImportError("NodoNX is unavailable")
193
- base = (NodoNX.from_graph(node.G, j) for j in sample_ids)
222
+ NodeNX = get_nodenx()
223
+ if NodeNX is None:
224
+ raise ImportError("NodeNX is unavailable")
225
+ base = (NodeNX.from_graph(node.G, j) for j in sample_ids)
194
226
  else:
195
227
  base = node.all_nodes()
196
228
  for j in base:
@@ -203,12 +235,12 @@ def _um_candidate_iter(node: NodoProtocol):
203
235
 
204
236
 
205
237
  def _um_select_candidates(
206
- node: NodoProtocol,
207
- candidates,
238
+ node: NodeProtocol,
239
+ candidates: Iterator[NodeProtocol],
208
240
  limit: int,
209
241
  mode: str,
210
242
  th: float,
211
- ):
243
+ ) -> list[NodeProtocol]:
212
244
  """Select a subset of ``candidates`` for UM coupling."""
213
245
  rng = make_rng(int(node.graph.get("RANDOM_SEED", 0)), node.offset(), node.G)
214
246
 
@@ -232,7 +264,7 @@ def _um_select_candidates(
232
264
  return reservoir
233
265
 
234
266
 
235
- def _op_UM(node: NodoProtocol, gf: dict[str, Any]) -> None: # UM — Coupling
267
+ def _op_UM(node: NodeProtocol, gf: GlyphFactors) -> None: # UM — Coupling
236
268
  k = get_factor(gf, "UM_theta_push", 0.25)
237
269
  th = node.theta
238
270
  thL = neighbor_phase_mean(node)
@@ -269,12 +301,12 @@ def _op_UM(node: NodoProtocol, gf: dict[str, Any]) -> None: # UM — Coupling
269
301
  node.add_edge(j, compat)
270
302
 
271
303
 
272
- def _op_RA(node: NodoProtocol, gf: dict[str, Any]) -> None: # RA — Resonancia
304
+ def _op_RA(node: NodeProtocol, gf: GlyphFactors) -> None: # RA — Resonance
273
305
  diff = get_factor(gf, "RA_epi_diff", 0.15)
274
306
  _mix_epi_with_neighbors(node, diff, Glyph.RA)
275
307
 
276
308
 
277
- def _op_SHA(node: NodoProtocol, gf: dict[str, Any]) -> None: # SHA — Silencio
309
+ def _op_SHA(node: NodeProtocol, gf: GlyphFactors) -> None: # SHA — Silence
278
310
  factor = get_factor(gf, "SHA_vf_factor", 0.85)
279
311
  node.vf = factor * node.vf
280
312
 
@@ -284,12 +316,12 @@ factor_nul = 0.85
284
316
  _SCALE_FACTORS = {Glyph.VAL: factor_val, Glyph.NUL: factor_nul}
285
317
 
286
318
 
287
- def _op_scale(node: NodoProtocol, factor: float) -> None:
319
+ def _op_scale(node: NodeProtocol, factor: float) -> None:
288
320
  node.vf *= factor
289
321
 
290
322
 
291
- def _make_scale_op(glyph: Glyph):
292
- def _op(node: NodoProtocol, gf: dict[str, Any]) -> None:
323
+ def _make_scale_op(glyph: Glyph) -> GlyphOperation:
324
+ def _op(node: NodeProtocol, gf: GlyphFactors) -> None:
293
325
  key = "VAL_scale" if glyph is Glyph.VAL else "NUL_scale"
294
326
  default = _SCALE_FACTORS[glyph]
295
327
  factor = get_factor(gf, key, default)
@@ -299,22 +331,22 @@ def _make_scale_op(glyph: Glyph):
299
331
 
300
332
 
301
333
  def _op_THOL(
302
- node: NodoProtocol, gf: dict[str, Any]
303
- ) -> None: # THOL — Autoorganización
334
+ node: NodeProtocol, gf: GlyphFactors
335
+ ) -> None: # THOL — Self-organization
304
336
  a = get_factor(gf, "THOL_accel", 0.10)
305
337
  node.dnfr = node.dnfr + a * getattr(node, "d2EPI", 0.0)
306
338
 
307
339
 
308
340
  def _op_ZHIR(
309
- node: NodoProtocol, gf: dict[str, Any]
310
- ) -> None: # ZHIR — Mutación
341
+ node: NodeProtocol, gf: GlyphFactors
342
+ ) -> None: # ZHIR — Mutation
311
343
  shift = get_factor(gf, "ZHIR_theta_shift", math.pi / 2)
312
344
  node.theta = node.theta + shift
313
345
 
314
346
 
315
347
  def _op_NAV(
316
- node: NodoProtocol, gf: dict[str, Any]
317
- ) -> None: # NAV — Transición
348
+ node: NodeProtocol, gf: GlyphFactors
349
+ ) -> None: # NAV — Transition
318
350
  dnfr = node.dnfr
319
351
  vf = node.vf
320
352
  eta = get_factor(gf, "NAV_eta", 0.5)
@@ -334,14 +366,14 @@ def _op_NAV(
334
366
 
335
367
 
336
368
  def _op_REMESH(
337
- node: NodoProtocol, gf: dict[str, Any] | None = None
338
- ) -> None: # REMESH — aviso
369
+ node: NodeProtocol, gf: GlyphFactors | None = None
370
+ ) -> None: # REMESH — advisory
339
371
  step_idx = glyph_history.current_step_idx(node)
340
372
  last_warn = node.graph.get("_remesh_warn_step", None)
341
373
  if last_warn != step_idx:
342
374
  msg = (
343
- "REMESH es a escala de red. Usa apply_remesh_if_globally_"
344
- "stable(G) o apply_network_remesh(G)."
375
+ "REMESH operates at network scale. Use apply_remesh_if_globally_"
376
+ "stable(G) or apply_network_remesh(G)."
345
377
  )
346
378
  hist = glyph_history.ensure_history(node)
347
379
  glyph_history.append_metric(
@@ -357,7 +389,7 @@ def _op_REMESH(
357
389
  # Dispatcher
358
390
  # -------------------------
359
391
 
360
- GLYPH_OPERATIONS: dict[Glyph, Callable[["NodoProtocol", dict[str, Any]], None]] = {
392
+ GLYPH_OPERATIONS: dict[Glyph, GlyphOperation] = {
361
393
  Glyph.AL: _op_AL,
362
394
  Glyph.EN: _op_EN,
363
395
  Glyph.IL: _op_IL,
@@ -375,9 +407,9 @@ GLYPH_OPERATIONS: dict[Glyph, Callable[["NodoProtocol", dict[str, Any]], None]]
375
407
 
376
408
 
377
409
  def apply_glyph_obj(
378
- node: NodoProtocol, glyph: Glyph | str, *, window: int | None = None
410
+ node: NodeProtocol, glyph: Glyph | str, *, window: int | None = None
379
411
  ) -> None:
380
- """Apply ``glyph`` to an object satisfying :class:`NodoProtocol`."""
412
+ """Apply ``glyph`` to an object satisfying :class:`NodeProtocol`."""
381
413
 
382
414
  try:
383
415
  g = glyph if isinstance(glyph, Glyph) else Glyph(str(glyph))
@@ -392,15 +424,15 @@ def apply_glyph_obj(
392
424
  {
393
425
  "step": step_idx,
394
426
  "node": getattr(node, "n", None),
395
- "msg": f"glyph desconocido: {glyph}",
427
+ "msg": f"unknown glyph: {glyph}",
396
428
  },
397
429
  ),
398
430
  )
399
- raise ValueError(f"glyph desconocido: {glyph}")
431
+ raise ValueError(f"unknown glyph: {glyph}")
400
432
 
401
433
  op = GLYPH_OPERATIONS.get(g)
402
434
  if op is None:
403
- raise ValueError(f"glyph sin operador: {g}")
435
+ raise ValueError(f"glyph has no registered operator: {g}")
404
436
  if window is None:
405
437
  window = int(get_param(node, "GLYPH_HYSTERESIS_WINDOW"))
406
438
  gf = get_glyph_factors(node)
@@ -410,11 +442,11 @@ def apply_glyph_obj(
410
442
 
411
443
 
412
444
  def apply_glyph(
413
- G, n, glyph: Glyph | str, *, window: int | None = None
445
+ G: TNFRGraph, n: NodeId, glyph: Glyph | str, *, window: int | None = None
414
446
  ) -> None:
415
447
  """Adapter to operate on ``networkx`` graphs."""
416
- NodoNX = get_nodonx()
417
- if NodoNX is None:
418
- raise ImportError("NodoNX is unavailable")
419
- node = NodoNX(G, n)
448
+ NodeNX = get_nodenx()
449
+ if NodeNX is None:
450
+ raise ImportError("NodeNX is unavailable")
451
+ node = NodeNX(G, n)
420
452
  apply_glyph_obj(node, glyph, window=window)
@@ -0,0 +1,31 @@
1
+ from typing import Any
2
+
3
+ Operator: Any
4
+ Emission: Any
5
+ Reception: Any
6
+ Coherence: Any
7
+ Dissonance: Any
8
+ Coupling: Any
9
+ Resonance: Any
10
+ Silence: Any
11
+ Expansion: Any
12
+ Contraction: Any
13
+ SelfOrganization: Any
14
+ Mutation: Any
15
+ Transition: Any
16
+ Recursivity: Any
17
+ GLYPH_OPERATIONS: Any
18
+ JitterCache: Any
19
+ JitterCacheManager: Any
20
+ OPERATORS: Any
21
+ apply_glyph: Any
22
+ apply_glyph_obj: Any
23
+ apply_network_remesh: Any
24
+ apply_remesh_if_globally_stable: Any
25
+ apply_topological_remesh: Any
26
+ discover_operators: Any
27
+ get_glyph_factors: Any
28
+ get_jitter_manager: Any
29
+ get_neighbor_epi: Any
30
+ random_jitter: Any
31
+ reset_jitter_manager: Any