tnfr 4.5.1__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 (170) hide show
  1. tnfr/__init__.py +270 -90
  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 +631 -0
  7. tnfr/alias.pyi +140 -0
  8. tnfr/cache.py +732 -0
  9. tnfr/cache.pyi +232 -0
  10. tnfr/callback_utils.py +381 -0
  11. tnfr/callback_utils.pyi +105 -0
  12. tnfr/cli/__init__.py +89 -0
  13. tnfr/cli/__init__.pyi +47 -0
  14. tnfr/cli/arguments.py +199 -0
  15. tnfr/cli/arguments.pyi +33 -0
  16. tnfr/cli/execution.py +322 -0
  17. tnfr/cli/execution.pyi +80 -0
  18. tnfr/cli/utils.py +34 -0
  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/init.py +36 -0
  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 +228 -0
  31. tnfr/constants/__init__.pyi +104 -0
  32. tnfr/constants/core.py +158 -0
  33. tnfr/constants/core.pyi +17 -0
  34. tnfr/constants/init.py +31 -0
  35. tnfr/constants/init.pyi +12 -0
  36. tnfr/constants/metric.py +102 -0
  37. tnfr/constants/metric.pyi +19 -0
  38. tnfr/constants_glyphs.py +16 -0
  39. tnfr/constants_glyphs.pyi +12 -0
  40. tnfr/dynamics/__init__.py +136 -0
  41. tnfr/dynamics/__init__.pyi +83 -0
  42. tnfr/dynamics/adaptation.py +201 -0
  43. tnfr/dynamics/aliases.py +22 -0
  44. tnfr/dynamics/coordination.py +343 -0
  45. tnfr/dynamics/dnfr.py +2315 -0
  46. tnfr/dynamics/dnfr.pyi +33 -0
  47. tnfr/dynamics/integrators.py +561 -0
  48. tnfr/dynamics/integrators.pyi +35 -0
  49. tnfr/dynamics/runtime.py +521 -0
  50. tnfr/dynamics/sampling.py +34 -0
  51. tnfr/dynamics/sampling.pyi +7 -0
  52. tnfr/dynamics/selectors.py +680 -0
  53. tnfr/execution.py +216 -0
  54. tnfr/execution.pyi +65 -0
  55. tnfr/flatten.py +283 -0
  56. tnfr/flatten.pyi +28 -0
  57. tnfr/gamma.py +320 -89
  58. tnfr/gamma.pyi +40 -0
  59. tnfr/glyph_history.py +337 -0
  60. tnfr/glyph_history.pyi +53 -0
  61. tnfr/grammar.py +23 -153
  62. tnfr/grammar.pyi +13 -0
  63. tnfr/helpers/__init__.py +151 -0
  64. tnfr/helpers/__init__.pyi +66 -0
  65. tnfr/helpers/numeric.py +88 -0
  66. tnfr/helpers/numeric.pyi +12 -0
  67. tnfr/immutable.py +214 -0
  68. tnfr/immutable.pyi +37 -0
  69. tnfr/initialization.py +199 -0
  70. tnfr/initialization.pyi +73 -0
  71. tnfr/io.py +311 -0
  72. tnfr/io.pyi +11 -0
  73. tnfr/locking.py +37 -0
  74. tnfr/locking.pyi +7 -0
  75. tnfr/metrics/__init__.py +41 -0
  76. tnfr/metrics/__init__.pyi +20 -0
  77. tnfr/metrics/coherence.py +1469 -0
  78. tnfr/metrics/common.py +149 -0
  79. tnfr/metrics/common.pyi +15 -0
  80. tnfr/metrics/core.py +259 -0
  81. tnfr/metrics/core.pyi +13 -0
  82. tnfr/metrics/diagnosis.py +840 -0
  83. tnfr/metrics/diagnosis.pyi +89 -0
  84. tnfr/metrics/export.py +151 -0
  85. tnfr/metrics/glyph_timing.py +369 -0
  86. tnfr/metrics/reporting.py +152 -0
  87. tnfr/metrics/reporting.pyi +12 -0
  88. tnfr/metrics/sense_index.py +294 -0
  89. tnfr/metrics/sense_index.pyi +9 -0
  90. tnfr/metrics/trig.py +216 -0
  91. tnfr/metrics/trig.pyi +12 -0
  92. tnfr/metrics/trig_cache.py +105 -0
  93. tnfr/metrics/trig_cache.pyi +10 -0
  94. tnfr/node.py +255 -177
  95. tnfr/node.pyi +161 -0
  96. tnfr/observers.py +154 -150
  97. tnfr/observers.pyi +46 -0
  98. tnfr/ontosim.py +135 -134
  99. tnfr/ontosim.pyi +33 -0
  100. tnfr/operators/__init__.py +452 -0
  101. tnfr/operators/__init__.pyi +31 -0
  102. tnfr/operators/definitions.py +181 -0
  103. tnfr/operators/definitions.pyi +92 -0
  104. tnfr/operators/jitter.py +266 -0
  105. tnfr/operators/jitter.pyi +11 -0
  106. tnfr/operators/registry.py +80 -0
  107. tnfr/operators/registry.pyi +15 -0
  108. tnfr/operators/remesh.py +569 -0
  109. tnfr/presets.py +10 -23
  110. tnfr/presets.pyi +7 -0
  111. tnfr/py.typed +0 -0
  112. tnfr/rng.py +440 -0
  113. tnfr/rng.pyi +14 -0
  114. tnfr/selector.py +217 -0
  115. tnfr/selector.pyi +19 -0
  116. tnfr/sense.py +307 -142
  117. tnfr/sense.pyi +30 -0
  118. tnfr/structural.py +69 -164
  119. tnfr/structural.pyi +46 -0
  120. tnfr/telemetry/__init__.py +13 -0
  121. tnfr/telemetry/verbosity.py +37 -0
  122. tnfr/tokens.py +61 -0
  123. tnfr/tokens.pyi +41 -0
  124. tnfr/trace.py +520 -95
  125. tnfr/trace.pyi +68 -0
  126. tnfr/types.py +382 -17
  127. tnfr/types.pyi +145 -0
  128. tnfr/utils/__init__.py +158 -0
  129. tnfr/utils/__init__.pyi +133 -0
  130. tnfr/utils/cache.py +755 -0
  131. tnfr/utils/cache.pyi +156 -0
  132. tnfr/utils/data.py +267 -0
  133. tnfr/utils/data.pyi +73 -0
  134. tnfr/utils/graph.py +87 -0
  135. tnfr/utils/graph.pyi +10 -0
  136. tnfr/utils/init.py +746 -0
  137. tnfr/utils/init.pyi +85 -0
  138. tnfr/utils/io.py +157 -0
  139. tnfr/utils/io.pyi +10 -0
  140. tnfr/utils/validators.py +130 -0
  141. tnfr/utils/validators.pyi +19 -0
  142. tnfr/validation/__init__.py +25 -0
  143. tnfr/validation/__init__.pyi +17 -0
  144. tnfr/validation/compatibility.py +59 -0
  145. tnfr/validation/compatibility.pyi +8 -0
  146. tnfr/validation/grammar.py +149 -0
  147. tnfr/validation/grammar.pyi +11 -0
  148. tnfr/validation/rules.py +194 -0
  149. tnfr/validation/rules.pyi +18 -0
  150. tnfr/validation/syntax.py +151 -0
  151. tnfr/validation/syntax.pyi +7 -0
  152. tnfr-6.0.0.dist-info/METADATA +135 -0
  153. tnfr-6.0.0.dist-info/RECORD +157 -0
  154. tnfr/cli.py +0 -322
  155. tnfr/config.py +0 -41
  156. tnfr/constants.py +0 -277
  157. tnfr/dynamics.py +0 -814
  158. tnfr/helpers.py +0 -264
  159. tnfr/main.py +0 -47
  160. tnfr/metrics.py +0 -597
  161. tnfr/operators.py +0 -525
  162. tnfr/program.py +0 -176
  163. tnfr/scenarios.py +0 -34
  164. tnfr/validators.py +0 -38
  165. tnfr-4.5.1.dist-info/METADATA +0 -221
  166. tnfr-4.5.1.dist-info/RECORD +0 -28
  167. {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/WHEEL +0 -0
  168. {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/entry_points.txt +0 -0
  169. {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/licenses/LICENSE.md +0 -0
  170. {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/top_level.txt +0 -0
tnfr/helpers.py DELETED
@@ -1,264 +0,0 @@
1
- """
2
- helpers.py — TNFR canónica
3
-
4
- Utilidades transversales + cálculo de Índice de sentido (Si).
5
- """
6
- from __future__ import annotations
7
- from typing import Iterable, Dict, Any
8
- import math
9
- from collections import deque
10
- from statistics import fmean, StatisticsError
11
-
12
- try:
13
- import networkx as nx # solo para tipos
14
- except Exception: # pragma: no cover
15
- nx = None # type: ignore
16
-
17
- from .constants import DEFAULTS, ALIAS_VF, ALIAS_THETA, ALIAS_DNFR, ALIAS_EPI, ALIAS_SI, ALIAS_EPI_KIND
18
-
19
- # -------------------------
20
- # Utilidades numéricas
21
- # -------------------------
22
-
23
- def clamp(x: float, a: float, b: float) -> float:
24
- """Constriñe ``x`` al intervalo cerrado [a, b]."""
25
- return a if x < a else b if x > b else x
26
-
27
-
28
- def clamp_abs(x: float, m: float) -> float:
29
- """Limita ``x`` al rango simétrico [-m, m] usando ``abs(m)``."""
30
- m = abs(m)
31
- return clamp(x, -m, m)
32
-
33
-
34
- def clamp01(x: float) -> float:
35
- """Ataja ``x`` a la banda [0, 1]."""
36
- return clamp(x, 0.0, 1.0)
37
-
38
-
39
- def list_mean(xs: Iterable[float], default: float = 0.0) -> float:
40
- """Promedio aritmético o ``default`` si ``xs`` está vacío."""
41
- try:
42
- return fmean(xs)
43
- except StatisticsError:
44
- return default
45
-
46
-
47
- def _wrap_angle(a: float) -> float:
48
- """Envuelve ángulo a (-π, π]."""
49
- pi = math.pi
50
- a = (a + pi) % (2 * pi) - pi
51
- return a
52
-
53
-
54
- def phase_distance(a: float, b: float) -> float:
55
- """Distancia de fase normalizada en [0,1]. 0 = misma fase, 1 = opuesta."""
56
- return abs(_wrap_angle(a - b)) / math.pi
57
-
58
-
59
- # -------------------------
60
- # Acceso a atributos con alias
61
- # -------------------------
62
-
63
- def _get_attr(d: Dict[str, Any], aliases: Iterable[str], default: float = 0.0) -> float:
64
- for k in aliases:
65
- if k in d:
66
- try:
67
- return float(d[k])
68
- except Exception:
69
- continue
70
- return float(default)
71
-
72
- def _set_attr(d, aliases, value: float) -> None:
73
- for k in aliases:
74
- if k in d:
75
- d[k] = float(value)
76
- return
77
- d[next(iter(aliases))] = float(value)
78
-
79
-
80
- def _get_attr_str(d: Dict[str, Any], aliases: Iterable[str], default: str = "") -> str:
81
- for k in aliases:
82
- if k in d:
83
- try:
84
- return str(d[k])
85
- except Exception:
86
- continue
87
- return str(default)
88
-
89
-
90
- def _set_attr_str(d, aliases, value: str) -> None:
91
- for k in aliases:
92
- if k in d:
93
- d[k] = str(value)
94
- return
95
- d[next(iter(aliases))] = str(value)
96
-
97
- # -------------------------
98
- # Estadísticos vecinales
99
- # -------------------------
100
-
101
- def media_vecinal(G, n, aliases: Iterable[str], default: float = 0.0) -> float:
102
- """Media del atributo indicado por ``aliases`` en los vecinos de ``n``."""
103
- vals = (_get_attr(G.nodes[v], aliases, default) for v in G.neighbors(n))
104
- return list_mean(vals, default)
105
-
106
-
107
- def fase_media(G, n) -> float:
108
- """Promedio circular de las fases de los vecinos."""
109
- x = y = 0.0
110
- count = 0
111
- for v in G.neighbors(n):
112
- th = _get_attr(G.nodes[v], ALIAS_THETA, 0.0)
113
- x += math.cos(th)
114
- y += math.sin(th)
115
- count += 1
116
- if count == 0:
117
- return _get_attr(G.nodes[n], ALIAS_THETA, 0.0)
118
- return math.atan2(y / count, x / count)
119
-
120
-
121
- # -------------------------
122
- # Historial de glifos por nodo
123
- # -------------------------
124
-
125
- def push_glifo(nd: Dict[str, Any], glifo: str, window: int) -> None:
126
- """Añade ``glifo`` al historial del nodo con tamaño máximo ``window``."""
127
- hist = nd.setdefault("hist_glifos", deque(maxlen=window))
128
- hist.append(str(glifo))
129
-
130
-
131
- def reciente_glifo(nd: Dict[str, Any], glifo: str, ventana: int) -> bool:
132
- """Indica si ``glifo`` apareció en las últimas ``ventana`` emisiones"""
133
- hist = nd.get("hist_glifos")
134
- gl = str(glifo)
135
- from itertools import islice
136
- if hist and any(g == gl for g in islice(reversed(hist), ventana)):
137
- return True
138
- # fallback al glifo dominante actual
139
- return _get_attr_str(nd, ALIAS_EPI_KIND, "") == gl
140
-
141
- # -------------------------
142
- # Utilidades de historial global
143
- # -------------------------
144
-
145
- def ensure_history(G) -> Dict[str, Any]:
146
- """Garantiza G.graph['history'] y la devuelve."""
147
- return G.graph.setdefault("history", {})
148
-
149
-
150
- def last_glifo(nd: Dict[str, Any]) -> str | None:
151
- """Retorna el glifo más reciente del nodo o ``None``."""
152
- kind = _get_attr_str(nd, ALIAS_EPI_KIND, "")
153
- if kind:
154
- return kind
155
- hist = nd.get("hist_glifos")
156
- if not hist:
157
- return None
158
- try:
159
- return hist[-1]
160
- except Exception:
161
- return None
162
-
163
- # -------------------------
164
- # Callbacks Γ(R)
165
- # -------------------------
166
-
167
- def _ensure_callbacks(G):
168
- """Garantiza la estructura de callbacks en G.graph."""
169
- cbs = G.graph.setdefault("callbacks", {
170
- "before_step": [],
171
- "after_step": [],
172
- "on_remesh": [],
173
- })
174
- # normaliza claves por si vienen incompletas
175
- for k in ("before_step", "after_step", "on_remesh"):
176
- cbs.setdefault(k, [])
177
- return cbs
178
-
179
- def register_callback(
180
- G,
181
- event: str | None = None,
182
- func=None,
183
- *,
184
- when: str | None = None,
185
- name: str | None = None,
186
- ):
187
- """Registra ``func`` como callback del ``event`` indicado.
188
-
189
- Permite tanto la forma posicional ``register_callback(G, "after_step", fn)``
190
- como la forma con palabras clave ``register_callback(G, when="after_step", func=fn)``.
191
- El parámetro ``name`` se acepta por compatibilidad pero actualmente no se
192
- utiliza.
193
- """
194
- event = event or when
195
- if event not in ("before_step", "after_step", "on_remesh"):
196
- raise ValueError(f"Evento desconocido: {event}")
197
- if func is None:
198
- raise TypeError("func es obligatorio")
199
- cbs = _ensure_callbacks(G)
200
- cbs[event].append(func)
201
- return func
202
-
203
- def invoke_callbacks(G, event: str, ctx: dict | None = None):
204
- """Invoca todos los callbacks registrados para `event` con el contexto `ctx`."""
205
- cbs = _ensure_callbacks(G).get(event, [])
206
- strict = bool(G.graph.get("CALLBACKS_STRICT", DEFAULTS["CALLBACKS_STRICT"]))
207
- ctx = ctx or {}
208
- for fn in list(cbs):
209
- try:
210
- fn(G, ctx)
211
- except Exception as e:
212
- if strict:
213
- raise
214
- G.graph.setdefault("_callback_errors", []).append({
215
- "event": event, "step": ctx.get("step"), "error": repr(e), "fn": repr(fn)
216
- })
217
-
218
- # -------------------------
219
- # Índice de sentido (Si)
220
- # -------------------------
221
-
222
- def compute_Si(G, *, inplace: bool = True) -> Dict[Any, float]:
223
- """Calcula Si por nodo y lo escribe en G.nodes[n]["Si"].
224
-
225
- Fórmula:
226
- Si = α·νf_norm + β·(1 - disp_fase_local) + γ·(1 - |ΔNFR|/max|ΔNFR|)
227
- También guarda en ``G.graph`` los pesos normalizados y la
228
- sensibilidad parcial (∂Si/∂componente).
229
- """
230
- alpha = float(G.graph.get("SI_WEIGHTS", DEFAULTS["SI_WEIGHTS"]).get("alpha", 0.34))
231
- beta = float(G.graph.get("SI_WEIGHTS", DEFAULTS["SI_WEIGHTS"]).get("beta", 0.33))
232
- gamma = float(G.graph.get("SI_WEIGHTS", DEFAULTS["SI_WEIGHTS"]).get("gamma", 0.33))
233
- s = alpha + beta + gamma
234
- if s <= 0:
235
- alpha = beta = gamma = 1/3
236
- else:
237
- alpha, beta, gamma = alpha/s, beta/s, gamma/s
238
- G.graph["_Si_weights"] = {"alpha": alpha, "beta": beta, "gamma": gamma}
239
- G.graph["_Si_sensitivity"] = {"dSi_dvf_norm": alpha, "dSi_ddisp_fase": -beta, "dSi_ddnfr_norm": -gamma}
240
-
241
- # Normalización de νf y ΔNFR en red
242
- vfmax = max((abs(_get_attr(G.nodes[n], ALIAS_VF, 0.0)) for n in G.nodes()), default=1.0)
243
- dnfrmax = max((abs(_get_attr(G.nodes[n], ALIAS_DNFR, 0.0)) for n in G.nodes()), default=1.0)
244
-
245
- out: Dict[Any, float] = {}
246
- for n in G.nodes():
247
- nd = G.nodes[n]
248
- vf = _get_attr(nd, ALIAS_VF, 0.0)
249
- vf_norm = 0.0 if vfmax == 0 else clamp01(abs(vf)/vfmax)
250
-
251
- # dispersión de fase local
252
- th_i = _get_attr(nd, ALIAS_THETA, 0.0)
253
- th_bar = fase_media(G, n)
254
- disp_fase = phase_distance(th_i, th_bar) # [0,1]
255
-
256
- dnfr = _get_attr(nd, ALIAS_DNFR, 0.0)
257
- dnfr_norm = 0.0 if dnfrmax == 0 else clamp01(abs(dnfr)/dnfrmax)
258
-
259
- Si = alpha*vf_norm + beta*(1.0 - disp_fase) + gamma*(1.0 - dnfr_norm)
260
- Si = clamp01(Si)
261
- out[n] = Si
262
- if inplace:
263
- _set_attr(nd, ALIAS_SI, Si)
264
- return out
tnfr/main.py DELETED
@@ -1,47 +0,0 @@
1
- from __future__ import annotations
2
- import argparse, sys
3
- import networkx as nx
4
- from . import preparar_red, run, __version__
5
- from .constants import merge_overrides, attach_defaults
6
- from .sense import register_sigma_callback
7
- from .metrics import register_metrics_callbacks
8
- from .trace import register_trace
9
-
10
- def main(argv: list[str] | None = None) -> None:
11
- p = argparse.ArgumentParser(
12
- prog="tnfr",
13
- description="TNFR canónica — demo CLI (red Erdős–Rényi + dinámica glífica)",
14
- )
15
- p.add_argument("--version", action="store_true", help="muestra versión y sale")
16
- p.add_argument("--n", type=int, default=30, help="nodos (Erdős–Rényi)")
17
- p.add_argument("--p", type=float, default=0.15, help="probabilidad de arista (Erdős–Rényi)")
18
- p.add_argument("--steps", type=int, default=100, help="pasos a simular")
19
- p.add_argument("--observer", action="store_true", help="adjunta observador estándar")
20
- args = p.parse_args(argv)
21
-
22
- if args.version:
23
- print(__version__)
24
- return
25
-
26
- G = nx.erdos_renyi_graph(args.n, args.p)
27
- preparar_red(G, ATTACH_STD_OBSERVER=bool(args.observer))
28
- attach_defaults(G)
29
- register_sigma_callback(G)
30
- register_metrics_callbacks(G)
31
- register_trace(G)
32
- # Ejemplo: activar Γi(R) lineal con β=0.2 y R0=0.5
33
- merge_overrides(G, GAMMA={"type": "kuramoto_linear", "beta": 0.2, "R0": 0.5})
34
- run(G, args.steps)
35
-
36
- h = G.graph.get("history", {})
37
- C = h.get("C_steps", [])[-1] if h.get("C_steps") else None
38
- stab = h.get("stable_frac", [])[-1] if h.get("stable_frac") else None
39
- R = h.get("kuramoto_R", [])[-1] if h.get("kuramoto_R") else None
40
-
41
- print("TNFR terminado:")
42
- if C is not None: print(f" C(t) ~ {C:.3f}")
43
- if stab is not None: print(f" estable ~ {stab:.3f}")
44
- if R is not None: print(f" R (Kuramoto) ~ {R:.3f}")
45
-
46
- if __name__ == "__main__":
47
- main(sys.argv[1:])