tnfr 1.0__py3-none-any.whl → 3.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.

Potentially problematic release.


This version of tnfr might be problematic. Click here for more details.

tnfr/operators.py ADDED
@@ -0,0 +1,296 @@
1
+ # operators.py — TNFR canónica (ASCII-safe)
2
+ from __future__ import annotations
3
+ from typing import Dict, Any, Optional
4
+ import math
5
+ import random
6
+ import hashlib
7
+
8
+ from constants import DEFAULTS, ALIAS_VF, ALIAS_THETA, ALIAS_DNFR, ALIAS_EPI, ALIAS_D2EPI
9
+ from helpers import _get_attr, _set_attr, clamp, clamp01, list_mean, fase_media, push_glifo, invoke_callbacks
10
+
11
+ """
12
+ Este módulo implementa:
13
+ - Los 13 glifos como operadores locales suaves.
14
+ - Un dispatcher `aplicar_glifo` que mapea el nombre del glifo (con apóstrofo tipográfico) a su función.
15
+ - RE’MESH de red: `aplicar_remesh_red` y `aplicar_remesh_si_estabilizacion_global`.
16
+
17
+ Nota sobre α (alpha) de RE’MESH: se toma por prioridad de
18
+ 1) G.graph["GLYPH_FACTORS"]["REMESH_alpha"]
19
+ 2) G.graph["REMESH_ALPHA"]
20
+ 3) DEFAULTS["REMESH_ALPHA"]
21
+ """
22
+
23
+ # -------------------------
24
+ # Glifos (operadores locales)
25
+ # -------------------------
26
+
27
+ def op_AL(G, n): # A’L — Emisión
28
+ f = float(G.graph.get("GLYPH_FACTORS", DEFAULTS["GLYPH_FACTORS"]).get("AL_boost", 0.05))
29
+ nd = G.nodes[n]
30
+ epi = _get_attr(nd, ALIAS_EPI, 0.0)
31
+ _set_attr(nd, ALIAS_EPI, epi + f)
32
+
33
+
34
+ def op_EN(G, n): # E’N — Recepción
35
+ mix = float(G.graph.get("GLYPH_FACTORS", DEFAULTS["GLYPH_FACTORS"]).get("EN_mix", 0.25))
36
+ nd = G.nodes[n]
37
+ epi = _get_attr(nd, ALIAS_EPI, 0.0)
38
+ if G.degree(n) == 0:
39
+ return # sin vecinos no hay mezcla
40
+ epi_bar = list_mean(_get_attr(G.nodes[v], ALIAS_EPI, epi) for v in G.neighbors(n))
41
+ _set_attr(nd, ALIAS_EPI, (1 - mix) * epi + mix * epi_bar)
42
+
43
+
44
+ def op_IL(G, n): # I’L — Coherencia (reduce ΔNFR)
45
+ factor = float(G.graph.get("GLYPH_FACTORS", DEFAULTS["GLYPH_FACTORS"]).get("IL_dnfr_factor", 0.7))
46
+ nd = G.nodes[n]
47
+ dnfr = _get_attr(nd, ALIAS_DNFR, 0.0)
48
+ _set_attr(nd, ALIAS_DNFR, factor * dnfr)
49
+
50
+ def op_OZ(G, n): # O’Z — Disonancia (aumenta ΔNFR o añade ruido)
51
+ factor = float(G.graph.get("GLYPH_FACTORS", DEFAULTS["GLYPH_FACTORS"]).get("OZ_dnfr_factor", 1.3))
52
+ nd = G.nodes[n]
53
+ dnfr = _get_attr(nd, ALIAS_DNFR, 0.0)
54
+ if bool(G.graph.get("OZ_NOISE_MODE", False)):
55
+ base_seed = int(G.graph.get("RANDOM_SEED", 0))
56
+ step_idx = len(G.graph.get("history", {}).get("C_steps", []))
57
+ rnd = random.Random(base_seed + step_idx*1000003 + hash(("OZ", n)) % 1009)
58
+ sigma = float(G.graph.get("OZ_SIGMA", 0.1))
59
+ noise = sigma * (2.0 * rnd.random() - 1.0)
60
+ _set_attr(nd, ALIAS_DNFR, dnfr + noise)
61
+ else:
62
+ _set_attr(nd, ALIAS_DNFR, factor * dnfr if abs(dnfr) > 1e-9 else 0.1)
63
+
64
+ def op_UM(G, n): # U’M — Acoplamiento (empuja fase a la media local)
65
+ k = float(G.graph.get("GLYPH_FACTORS", DEFAULTS["GLYPH_FACTORS"]).get("UM_theta_push", 0.25))
66
+ nd = G.nodes[n]
67
+ th = _get_attr(nd, ALIAS_THETA, 0.0)
68
+ thL = fase_media(G, n)
69
+ d = ((thL - th + math.pi) % (2 * math.pi) - math.pi)
70
+ _set_attr(nd, ALIAS_THETA, th + k * d)
71
+
72
+
73
+ def op_RA(G, n): # R’A — Resonancia (difusión EPI)
74
+ diff = float(G.graph.get("GLYPH_FACTORS", DEFAULTS["GLYPH_FACTORS"]).get("RA_epi_diff", 0.15))
75
+ nd = G.nodes[n]
76
+ epi = _get_attr(nd, ALIAS_EPI, 0.0)
77
+ if G.degree(n) == 0:
78
+ return
79
+ epi_bar = list_mean(_get_attr(G.nodes[v], ALIAS_EPI, epi) for v in G.neighbors(n))
80
+ _set_attr(nd, ALIAS_EPI, epi + diff * (epi_bar - epi))
81
+
82
+
83
+ def op_SHA(G, n): # SH’A — Silencio (baja νf)
84
+ factor = float(G.graph.get("GLYPH_FACTORS", DEFAULTS["GLYPH_FACTORS"]).get("SHA_vf_factor", 0.85))
85
+ nd = G.nodes[n]
86
+ vf = _get_attr(nd, ALIAS_VF, 0.0)
87
+ _set_attr(nd, ALIAS_VF, factor * vf)
88
+
89
+
90
+ def op_VAL(G, n): # VA’L — Expansión (escala EPI)
91
+ s = float(G.graph.get("GLYPH_FACTORS", DEFAULTS["GLYPH_FACTORS"]).get("VAL_scale", 1.15))
92
+ nd = G.nodes[n]
93
+ epi = _get_attr(nd, ALIAS_EPI, 0.0)
94
+ _set_attr(nd, ALIAS_EPI, s * epi)
95
+
96
+
97
+ def op_NUL(G, n): # NU’L — Contracción (escala EPI)
98
+ s = float(G.graph.get("GLYPH_FACTORS", DEFAULTS["GLYPH_FACTORS"]).get("NUL_scale", 0.85))
99
+ nd = G.nodes[n]
100
+ epi = _get_attr(nd, ALIAS_EPI, 0.0)
101
+ _set_attr(nd, ALIAS_EPI, s * epi)
102
+
103
+
104
+ def op_THOL(G, n): # T’HOL — Autoorganización (inyecta aceleración)
105
+ a = float(G.graph.get("GLYPH_FACTORS", DEFAULTS["GLYPH_FACTORS"]).get("THOL_accel", 0.10))
106
+ nd = G.nodes[n]
107
+ d2 = _get_attr(nd, ALIAS_D2EPI, 0.0)
108
+ _set_attr(nd, ALIAS_DNFR, _get_attr(nd, ALIAS_DNFR, 0.0) + a * d2)
109
+
110
+
111
+ def op_ZHIR(G, n): # Z’HIR — Mutación (desplaza fase)
112
+ shift = float(G.graph.get("GLYPH_FACTORS", DEFAULTS["GLYPH_FACTORS"]).get("ZHIR_theta_shift", 1.57079632679))
113
+ nd = G.nodes[n]
114
+ th = _get_attr(nd, ALIAS_THETA, 0.0)
115
+ _set_attr(nd, ALIAS_THETA, th + shift)
116
+
117
+ def op_NAV(G, n): # NA’V — Transición (jitter suave de ΔNFR)
118
+ nd = G.nodes[n]
119
+ dnfr = _get_attr(nd, ALIAS_DNFR, 0.0)
120
+ j = float(G.graph.get("GLYPH_FACTORS", DEFAULTS["GLYPH_FACTORS"]).get("NAV_jitter", 0.05))
121
+ if bool(G.graph.get("NAV_RANDOM", True)):
122
+ # jitter uniforme en [-j, j] con semilla reproducible
123
+ base_seed = int(G.graph.get("RANDOM_SEED", 0))
124
+ # opcional: pequeño offset para evitar misma secuencia en todos los nodos/pasos
125
+ step_idx = len(G.graph.get("history", {}).get("C_steps", []))
126
+ rnd = random.Random(base_seed + step_idx*1000003 + hash(n) % 1009)
127
+ jitter = j * (2.0 * rnd.random() - 1.0)
128
+ else:
129
+ # comportamiento determinista (compatibilidad previa)
130
+ jitter = j * (1 if dnfr >= 0 else -1)
131
+ _set_attr(nd, ALIAS_DNFR, dnfr + jitter)
132
+
133
+ def op_REMESH(G, n): # RE’MESH — se realiza a escala de red (no-op local con aviso)
134
+ # Loguea solo 1 vez por paso para no spamear
135
+ step_idx = len(G.graph.get("history", {}).get("C_steps", []))
136
+ last_warn = G.graph.get("_remesh_warn_step", None)
137
+ if last_warn != step_idx:
138
+ msg = "RE’MESH es a escala de red. Usa aplicar_remesh_si_estabilizacion_global(G) o aplicar_remesh_red(G)."
139
+ G.graph.setdefault("history", {}).setdefault("events", []).append(("warn", {"step": step_idx, "node": n, "msg": msg}))
140
+ G.graph["_remesh_warn_step"] = step_idx
141
+ # no cambia estado local
142
+ return
143
+
144
+ # -------------------------
145
+ # Dispatcher
146
+ # -------------------------
147
+
148
+ _NAME_TO_OP = {
149
+ "A’L": op_AL, "E’N": op_EN, "I’L": op_IL, "O’Z": op_OZ, "U’M": op_UM,
150
+ "R’A": op_RA, "SH’A": op_SHA, "VA’L": op_VAL, "NU’L": op_NUL,
151
+ "T’HOL": op_THOL, "Z’HIR": op_ZHIR, "NA’V": op_NAV, "RE’MESH": op_REMESH,
152
+ }
153
+
154
+
155
+ def aplicar_glifo(G, n, glifo: str, *, window: Optional[int] = None) -> None:
156
+ glifo = str(glifo)
157
+ op = _NAME_TO_OP.get(glifo)
158
+ if not op:
159
+ return
160
+ if window is None:
161
+ window = int(G.graph.get("GLYPH_HYSTERESIS_WINDOW", DEFAULTS["GLYPH_HYSTERESIS_WINDOW"]))
162
+ push_glifo(G.nodes[n], glifo, window)
163
+ op(G, n)
164
+
165
+
166
+ # -------------------------
167
+ # RE’MESH de red (usa _epi_hist capturado en dynamics.step)
168
+ # -------------------------
169
+
170
+ def _remesh_alpha_info(G):
171
+ """Devuelve (alpha, source) con precedencia explícita:
172
+ 1) GLYPH_FACTORS["REMESH_alpha"] 2) G.graph["REMESH_ALPHA"] 3) DEFAULTS["REMESH_ALPHA"]"""
173
+ gf = G.graph.get("GLYPH_FACTORS", DEFAULTS["GLYPH_FACTORS"])
174
+ if "REMESH_alpha" in gf:
175
+ return float(gf["REMESH_alpha"]), "GLYPH_FACTORS"
176
+ if "REMESH_ALPHA" in G.graph:
177
+ return float(G.graph["REMESH_ALPHA"]), "G.graph"
178
+ return float(DEFAULTS["REMESH_ALPHA"]), "DEFAULTS"
179
+
180
+
181
+ def aplicar_remesh_red(G) -> None:
182
+ """
183
+ RE’MESH a escala de red usando _epi_hist capturado en dynamics.step.
184
+ Loguea meta con alpha/tau/step + topo_hash y checksums/medias de EPI antes/después.
185
+ Precedencia de alpha: GLYPH_FACTORS → G.graph → DEFAULTS.
186
+ """
187
+ tau = int(G.graph.get("REMESH_TAU", DEFAULTS["REMESH_TAU"]))
188
+ alpha, alpha_src = _remesh_alpha_info(G)
189
+ hist = G.graph.get("_epi_hist", [])
190
+ if len(hist) < tau + 1:
191
+ return
192
+
193
+ past = hist[-(tau + 1)]
194
+
195
+ # --- Topología + snapshot EPI (ANTES) ---
196
+ try:
197
+ n_nodes = G.number_of_nodes()
198
+ n_edges = G.number_of_edges()
199
+ degs = sorted(d for _, d in G.degree())
200
+ topo_str = f"n={n_nodes};m={n_edges};deg=" + ",".join(map(str, degs))
201
+ topo_hash = hashlib.sha1(topo_str.encode()).hexdigest()[:12]
202
+ except Exception:
203
+ topo_hash = None
204
+
205
+ def _epi_items():
206
+ for node in G.nodes():
207
+ yield node, _get_attr(G.nodes[node], ALIAS_EPI, 0.0)
208
+
209
+ epi_mean_before = list_mean(v for _, v in _epi_items())
210
+ epi_checksum_before = hashlib.sha1(
211
+ str(sorted((str(k), round(v, 6)) for k, v in _epi_items())).encode()
212
+ ).hexdigest()[:12]
213
+
214
+ # --- Mezcla (1-α)·now + α·old ---
215
+ for n in G.nodes():
216
+ nd = G.nodes[n]
217
+ epi_now = _get_attr(nd, ALIAS_EPI, 0.0)
218
+ epi_old = float(past.get(n, epi_now))
219
+ _set_attr(nd, ALIAS_EPI, (1 - alpha) * epi_now + alpha * epi_old)
220
+
221
+ # --- Snapshot EPI (DESPUÉS) ---
222
+ epi_mean_after = list_mean(_get_attr(G.nodes[n], ALIAS_EPI, 0.0) for n in G.nodes())
223
+ epi_checksum_after = hashlib.sha1(
224
+ str(sorted((str(n), round(_get_attr(G.nodes[n], ALIAS_EPI, 0.0), 6)) for n in G.nodes())).encode()
225
+ ).hexdigest()[:12]
226
+
227
+ # --- Metadatos y logging de evento ---
228
+ step_idx = len(G.graph.get("history", {}).get("C_steps", []))
229
+ meta = {
230
+ "alpha": alpha,
231
+ "alpha_source": alpha_src,
232
+ "tau": tau,
233
+ "step": step_idx,
234
+ # firmas
235
+ "topo_hash": topo_hash,
236
+ "epi_mean_before": float(epi_mean_before),
237
+ "epi_mean_after": float(epi_mean_after),
238
+ "epi_checksum_before": epi_checksum_before,
239
+ "epi_checksum_after": epi_checksum_after,
240
+ }
241
+
242
+ # Snapshot opcional de métricas recientes
243
+ h = G.graph.get("history", {})
244
+ if h:
245
+ if h.get("stable_frac"): meta["stable_frac_last"] = h["stable_frac"][-1]
246
+ if h.get("phase_sync"): meta["phase_sync_last"] = h["phase_sync"][-1]
247
+ if h.get("glyph_load_disr"): meta["glyph_disr_last"] = h["glyph_load_disr"][-1]
248
+
249
+ G.graph["_REMESH_META"] = meta
250
+ if G.graph.get("REMESH_LOG_EVENTS", DEFAULTS["REMESH_LOG_EVENTS"]):
251
+ ev = G.graph.setdefault("history", {}).setdefault("remesh_events", [])
252
+ ev.append(dict(meta))
253
+
254
+ # Callbacks Γ(R)
255
+ invoke_callbacks(G, "on_remesh", dict(meta))
256
+
257
+ def aplicar_remesh_si_estabilizacion_global(G, pasos_estables_consecutivos: Optional[int] = None) -> None:
258
+ # Ventanas y umbrales
259
+ w_estab = int(G.graph.get("REMESH_STABILITY_WINDOW", DEFAULTS["REMESH_STABILITY_WINDOW"]))
260
+ frac_req = float(G.graph.get("FRACTION_STABLE_REMESH", DEFAULTS["FRACTION_STABLE_REMESH"]))
261
+ req_extra = bool(G.graph.get("REMESH_REQUIRE_STABILITY", DEFAULTS["REMESH_REQUIRE_STABILITY"]))
262
+ min_sync = float(G.graph.get("REMESH_MIN_PHASE_SYNC", DEFAULTS["REMESH_MIN_PHASE_SYNC"]))
263
+ max_disr = float(G.graph.get("REMESH_MAX_GLYPH_DISR", DEFAULTS["REMESH_MAX_GLYPH_DISR"]))
264
+
265
+ hist = G.graph.setdefault("history", {"stable_frac": []})
266
+ sf = hist.get("stable_frac", [])
267
+ if len(sf) < w_estab:
268
+ return
269
+ # 1) Estabilidad por fracción de nodos estables
270
+ win_sf = sf[-w_estab:]
271
+ cond_sf = all(v >= frac_req for v in win_sf)
272
+ if not cond_sf:
273
+ return
274
+ # 2) Gating adicional (si está activado)
275
+ if req_extra:
276
+ # sincronía de fase (mayor mejor)
277
+ ps_ok = True
278
+ if "phase_sync" in hist and len(hist["phase_sync"]) >= w_estab:
279
+ win_ps = hist["phase_sync"][-w_estab:]
280
+ ps_ok = (sum(win_ps)/len(win_ps)) >= min_sync
281
+ # carga glífica disruptiva (menor mejor)
282
+ disr_ok = True
283
+ if "glyph_load_disr" in hist and len(hist["glyph_load_disr"]) >= w_estab:
284
+ win_disr = hist["glyph_load_disr"][-w_estab:]
285
+ disr_ok = (sum(win_disr)/len(win_disr)) <= max_disr
286
+ if not (ps_ok and disr_ok):
287
+ return
288
+ # 3) Cooldown
289
+ last = G.graph.get("_last_remesh_step", -10**9)
290
+ step_idx = len(sf)
291
+ cooldown = int(G.graph.get("REMESH_COOLDOWN_VENTANA", DEFAULTS["REMESH_COOLDOWN_VENTANA"]))
292
+ if step_idx - last < cooldown:
293
+ return
294
+ # 4) Aplicar y registrar
295
+ aplicar_remesh_red(G)
296
+ G.graph["_last_remesh_step"] = step_idx
@@ -0,0 +1,35 @@
1
+ Metadata-Version: 2.4
2
+ Name: tnfr
3
+ Version: 3.0.0
4
+ Summary: TNFR canónica: dinámica glífica modular sobre redes.
5
+ Author: Fer
6
+ License: MIT
7
+ Project-URL: Homepage, https://pypi.org/project/tnfr/
8
+ Project-URL: Repository, https://github.com/fermga/Teoria-de-la-naturaleza-fractal-resonante-TNFR-
9
+ Keywords: TNFR,fractal resonante,resonancia,glifos,networkx,dinámica,coherencia,EPI,Kuramoto
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Intended Audience :: Science/Research
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE.txt
25
+ Requires-Dist: networkx>=2.6
26
+ Dynamic: license-file
27
+
28
+ # tnfr
29
+
30
+
31
+ Paquete **tnfr** (canónica). Orquesta dinámica glífica sobre grafos `networkx`.
32
+
33
+
34
+ ```bash
35
+ pip install tnfr
@@ -0,0 +1,13 @@
1
+ tnfr/__init__.py,sha256=5djYn7YqfpOWrakVqJIBNqllDhTAe8xbo682SPC5rbc,1650
2
+ tnfr/constants.py,sha256=_775sPHussR9vgkWRCLC6dzwgk_1_lLnSlWT8sBWR3U,7677
3
+ tnfr/dynamics.py,sha256=7B38c9SVKLVFBrKHeJ1nXbghoRHfDs8Nl9CqUmCcAyI,23260
4
+ tnfr/helpers.py,sha256=tZJsDXc8k9HIfg8BA9cVUEFKBoX1Rfnuhurl2Fvxsy0,6017
5
+ tnfr/main.py,sha256=TEngteuC9MD7Ec9bNGuCC9ym-2ohbh202-HGArCR4tk,1506
6
+ tnfr/observers.py,sha256=MoC-xLJuMP-UYj8cpIVlgSbXDsE1Uj70Zy51PSH3AJY,5192
7
+ tnfr/ontosim.py,sha256=U0IeNVF8rFNhnmWWux91xDc0djTDZQkqRRosP6Z7FmE,5485
8
+ tnfr/operators.py,sha256=M6ahJL8IuB2y4qiEalge5EufCz0eEbhw-O4xfh3NpwE,12146
9
+ tnfr-3.0.0.dist-info/licenses/LICENSE.txt,sha256=xTjBNhy3N8pomFljrCkD1d34SmAEWv8hyJMMOjNMH0M,1071
10
+ tnfr-3.0.0.dist-info/METADATA,sha256=RP4ob4z-WMs8TeNV58to9266xivZh3gq5kHYsJZ--G8,1325
11
+ tnfr-3.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
12
+ tnfr-3.0.0.dist-info/top_level.txt,sha256=Q2HJnvc5Rt2VHwVvyBTnNPT4SfmJWnCj7XUxxEvQa7c,5
13
+ tnfr-3.0.0.dist-info/RECORD,,
@@ -0,0 +1 @@
1
+ tnfr