tnfr 3.0.2__py3-none-any.whl → 3.5.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/__init__.py +7 -36
- tnfr/dynamics.py +54 -27
- tnfr/helpers.py +6 -4
- tnfr/observers.py +19 -7
- tnfr/ontosim.py +12 -9
- tnfr/operators.py +11 -6
- tnfr-3.5.0.dist-info/METADATA +92 -0
- tnfr-3.5.0.dist-info/RECORD +14 -0
- tnfr-3.5.0.dist-info/entry_points.txt +2 -0
- tnfr-3.0.2.dist-info/licenses/LICENSE.txt → tnfr-3.5.0.dist-info/licenses/LICENSE.md +1 -1
- tnfr-3.0.2.dist-info/METADATA +0 -35
- tnfr-3.0.2.dist-info/RECORD +0 -13
- {tnfr-3.0.2.dist-info → tnfr-3.5.0.dist-info}/WHEEL +0 -0
- {tnfr-3.0.2.dist-info → tnfr-3.5.0.dist-info}/top_level.txt +0 -0
tnfr/__init__.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
from __future__ import annotations
|
|
2
3
|
"""
|
|
3
4
|
TNFR — Teoría de la Naturaleza Fractal Resonante
|
|
@@ -7,42 +8,12 @@ Ecuación nodal:
|
|
|
7
8
|
∂EPI/∂t = νf · ΔNFR(t)
|
|
8
9
|
"""
|
|
9
10
|
|
|
10
|
-
__version__ = "3.0.
|
|
11
|
-
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
# Luego: dynamics / ontosim (que dependen de los anteriores)
|
|
17
|
-
# -------------------------------------------------------------------
|
|
18
|
-
import sys as _sys
|
|
19
|
-
|
|
20
|
-
# 1.a constants (no depende de helpers/otros)
|
|
21
|
-
from . import constants as _constants
|
|
22
|
-
_sys.modules.setdefault("constants", _constants)
|
|
23
|
-
|
|
24
|
-
# 1.b helpers (usa: from constants import ...)
|
|
25
|
-
from . import helpers as _helpers
|
|
26
|
-
_sys.modules.setdefault("helpers", _helpers)
|
|
27
|
-
|
|
28
|
-
# 1.c operators (usa: from constants/helpers import ...)
|
|
29
|
-
from . import operators as _operators
|
|
30
|
-
_sys.modules.setdefault("operators", _operators)
|
|
31
|
-
|
|
32
|
-
# 1.d observers (usa: from constants/helpers import ...)
|
|
33
|
-
from . import observers as _observers
|
|
34
|
-
_sys.modules.setdefault("observers", _observers)
|
|
35
|
-
|
|
36
|
-
# 2) dynamics / ontosim (ya con alias creados)
|
|
37
|
-
from . import dynamics as _dynamics
|
|
38
|
-
from . import ontosim as _ontosim
|
|
39
|
-
_sys.modules.setdefault("dynamics", _dynamics)
|
|
40
|
-
_sys.modules.setdefault("ontosim", _ontosim)
|
|
41
|
-
|
|
42
|
-
# 3) Re-exports de la API pública
|
|
43
|
-
from .dynamics import step, run, set_delta_nfr_hook
|
|
44
|
-
from .ontosim import preparar_red
|
|
45
|
-
from .observers import attach_standard_observer, coherencia_global, orden_kuramoto
|
|
11
|
+
__version__ = "3.0.3"
|
|
12
|
+
|
|
13
|
+
# Re-exports de la API pública
|
|
14
|
+
from .dynamics import step, run, set_delta_nfr_hook
|
|
15
|
+
from .ontosim import preparar_red
|
|
16
|
+
from .observers import attach_standard_observer, coherencia_global, orden_kuramoto
|
|
46
17
|
|
|
47
18
|
__all__ = [
|
|
48
19
|
"preparar_red",
|
tnfr/dynamics.py
CHANGED
|
@@ -13,11 +13,13 @@ Incluye:
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
from typing import Dict, Any, Iterable
|
|
15
15
|
import math
|
|
16
|
+
from collections import deque
|
|
17
|
+
import networkx as nx
|
|
16
18
|
|
|
17
|
-
from observers import sincronía_fase, carga_glifica, orden_kuramoto, sigma_vector
|
|
18
|
-
from operators import aplicar_remesh_si_estabilizacion_global
|
|
19
|
-
from constants import DEFAULTS, ALIAS_VF, ALIAS_THETA, ALIAS_DNFR, ALIAS_EPI, ALIAS_SI, ALIAS_dEPI, ALIAS_D2EPI
|
|
20
|
-
from helpers import (
|
|
19
|
+
from .observers import sincronía_fase, carga_glifica, orden_kuramoto, sigma_vector
|
|
20
|
+
from .operators import aplicar_remesh_si_estabilizacion_global
|
|
21
|
+
from .constants import DEFAULTS, ALIAS_VF, ALIAS_THETA, ALIAS_DNFR, ALIAS_EPI, ALIAS_SI, ALIAS_dEPI, ALIAS_D2EPI
|
|
22
|
+
from .helpers import (
|
|
21
23
|
clamp, clamp01, list_mean, phase_distance,
|
|
22
24
|
_get_attr, _set_attr, media_vecinal, fase_media,
|
|
23
25
|
invoke_callbacks, reciente_glifo
|
|
@@ -120,8 +122,16 @@ def dnfr_epi_vf_mixed(G) -> None:
|
|
|
120
122
|
# -------------------------
|
|
121
123
|
|
|
122
124
|
def update_epi_via_nodal_equation(G, *, dt: float = None) -> None:
|
|
125
|
+
if not isinstance(G, (nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph)):
|
|
126
|
+
raise TypeError("G must be a networkx graph instance")
|
|
123
127
|
if dt is None:
|
|
124
128
|
dt = float(G.graph.get("DT", DEFAULTS["DT"]))
|
|
129
|
+
else:
|
|
130
|
+
if not isinstance(dt, (int, float)):
|
|
131
|
+
raise TypeError("dt must be a number")
|
|
132
|
+
if dt < 0:
|
|
133
|
+
raise ValueError("dt must be non-negative")
|
|
134
|
+
dt = float(dt)
|
|
125
135
|
for n in G.nodes():
|
|
126
136
|
nd = G.nodes[n]
|
|
127
137
|
vf = _get_attr(nd, ALIAS_VF, 0.0)
|
|
@@ -175,20 +185,30 @@ def coordinar_fase_global_vecinal(G, fuerza_global: float | None = None, fuerza_
|
|
|
175
185
|
Si no se pasan fuerzas explícitas, adapta kG/kL según estado (disonante / transición / estable).
|
|
176
186
|
Estado se decide por R (Kuramoto) y carga glífica disruptiva reciente.
|
|
177
187
|
"""
|
|
188
|
+
g = G.graph
|
|
189
|
+
defaults = DEFAULTS
|
|
178
190
|
# 0) Si hay fuerzas explícitas, usar y salir del modo adaptativo
|
|
179
191
|
if (fuerza_global is not None) or (fuerza_vecinal is not None):
|
|
180
|
-
kG = float(
|
|
181
|
-
|
|
192
|
+
kG = float(
|
|
193
|
+
fuerza_global
|
|
194
|
+
if fuerza_global is not None
|
|
195
|
+
else g.get("PHASE_K_GLOBAL", defaults["PHASE_K_GLOBAL"])
|
|
196
|
+
)
|
|
197
|
+
kL = float(
|
|
198
|
+
fuerza_vecinal
|
|
199
|
+
if fuerza_vecinal is not None
|
|
200
|
+
else g.get("PHASE_K_LOCAL", defaults["PHASE_K_LOCAL"])
|
|
201
|
+
)
|
|
182
202
|
else:
|
|
183
203
|
# 1) Lectura de configuración
|
|
184
|
-
cfg =
|
|
185
|
-
kG = float(
|
|
186
|
-
kL = float(
|
|
204
|
+
cfg = g.get("PHASE_ADAPT", defaults.get("PHASE_ADAPT", {}))
|
|
205
|
+
kG = float(g.get("PHASE_K_GLOBAL", defaults["PHASE_K_GLOBAL"]))
|
|
206
|
+
kL = float(g.get("PHASE_K_LOCAL", defaults["PHASE_K_LOCAL"]))
|
|
187
207
|
|
|
188
208
|
if bool(cfg.get("enabled", False)):
|
|
189
209
|
# 2) Métricas actuales (no dependemos de history)
|
|
190
210
|
R = orden_kuramoto(G)
|
|
191
|
-
win = int(
|
|
211
|
+
win = int(g.get("GLYPH_LOAD_WINDOW", defaults["GLYPH_LOAD_WINDOW"]))
|
|
192
212
|
dist = carga_glifica(G, window=win)
|
|
193
213
|
disr = float(dist.get("_disruptivos", 0.0)) if dist else 0.0
|
|
194
214
|
|
|
@@ -216,7 +236,9 @@ def coordinar_fase_global_vecinal(G, fuerza_global: float | None = None, fuerza_
|
|
|
216
236
|
kG_t = 0.5 * (kG_min + kG_max)
|
|
217
237
|
kL_t = 0.5 * (kL_min + kL_max)
|
|
218
238
|
|
|
219
|
-
up = float(cfg.get("up", 0.10))
|
|
239
|
+
up = float(cfg.get("up", 0.10))
|
|
240
|
+
down = float(cfg.get("down", 0.07))
|
|
241
|
+
|
|
220
242
|
def _step(curr, target, mn, mx):
|
|
221
243
|
gain = up if target > curr else down
|
|
222
244
|
nxt = curr + gain * (target - curr)
|
|
@@ -226,14 +248,19 @@ def coordinar_fase_global_vecinal(G, fuerza_global: float | None = None, fuerza_
|
|
|
226
248
|
kL = _step(kL, kL_t, kL_min, kL_max)
|
|
227
249
|
|
|
228
250
|
# 5) Persistir en G.graph y log de serie
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
hist =
|
|
232
|
-
hist.setdefault("phase_kG", [])
|
|
233
|
-
hist.setdefault("phase_kL", [])
|
|
234
|
-
hist.setdefault("phase_state", [])
|
|
235
|
-
hist.setdefault("phase_R", [])
|
|
236
|
-
hist.setdefault("phase_disr", [])
|
|
251
|
+
g["PHASE_K_GLOBAL"] = kG
|
|
252
|
+
g["PHASE_K_LOCAL"] = kL
|
|
253
|
+
hist = g.setdefault("history", {})
|
|
254
|
+
hist_kG = hist.setdefault("phase_kG", [])
|
|
255
|
+
hist_kL = hist.setdefault("phase_kL", [])
|
|
256
|
+
hist_state = hist.setdefault("phase_state", [])
|
|
257
|
+
hist_R = hist.setdefault("phase_R", [])
|
|
258
|
+
hist_disr = hist.setdefault("phase_disr", [])
|
|
259
|
+
hist_kG.append(float(kG))
|
|
260
|
+
hist_kL.append(float(kL))
|
|
261
|
+
hist_state.append(state)
|
|
262
|
+
hist_R.append(float(R))
|
|
263
|
+
hist_disr.append(float(disr))
|
|
237
264
|
|
|
238
265
|
# 6) Fase GLOBAL (centroide) para empuje
|
|
239
266
|
X = list(math.cos(_get_attr(G.nodes[n], ALIAS_THETA, 0.0)) for n in G.nodes())
|
|
@@ -394,7 +421,7 @@ def step(G, *, dt: float | None = None, use_Si: bool = True, apply_glyphs: bool
|
|
|
394
421
|
|
|
395
422
|
# 2) (opcional) Si
|
|
396
423
|
if use_Si:
|
|
397
|
-
from helpers import compute_Si
|
|
424
|
+
from .helpers import compute_Si
|
|
398
425
|
compute_Si(G, inplace=True)
|
|
399
426
|
|
|
400
427
|
# 2b) Normalizadores para selector paramétrico (por paso)
|
|
@@ -403,7 +430,7 @@ def step(G, *, dt: float | None = None, use_Si: bool = True, apply_glyphs: bool
|
|
|
403
430
|
# 3) Selección glífica + aplicación
|
|
404
431
|
if apply_glyphs:
|
|
405
432
|
selector = G.graph.get("glyph_selector", default_glyph_selector)
|
|
406
|
-
from operators import aplicar_glifo
|
|
433
|
+
from .operators import aplicar_glifo
|
|
407
434
|
window = int(G.graph.get("GLYPH_HYSTERESIS_WINDOW", DEFAULTS["GLYPH_HYSTERESIS_WINDOW"]))
|
|
408
435
|
for n in G.nodes():
|
|
409
436
|
g = selector(G, n)
|
|
@@ -422,13 +449,13 @@ def step(G, *, dt: float | None = None, use_Si: bool = True, apply_glyphs: bool
|
|
|
422
449
|
# 7) Observadores ligeros
|
|
423
450
|
_update_history(G)
|
|
424
451
|
# dynamics.py — dentro de step(), justo antes del punto 8)
|
|
425
|
-
epi_hist = G.graph.setdefault("_epi_hist", [])
|
|
426
|
-
epi_hist.append({n: _get_attr(G.nodes[n], ALIAS_EPI, 0.0) for n in G.nodes()})
|
|
427
|
-
# recorta el buffer para que no crezca sin límite
|
|
428
452
|
tau = int(G.graph.get("REMESH_TAU", DEFAULTS["REMESH_TAU"]))
|
|
429
|
-
maxlen = max(2*tau + 5, 64)
|
|
430
|
-
|
|
431
|
-
|
|
453
|
+
maxlen = max(2 * tau + 5, 64)
|
|
454
|
+
epi_hist = G.graph.get("_epi_hist")
|
|
455
|
+
if not isinstance(epi_hist, deque) or epi_hist.maxlen != maxlen:
|
|
456
|
+
epi_hist = deque(list(epi_hist or [])[-maxlen:], maxlen=maxlen)
|
|
457
|
+
G.graph["_epi_hist"] = epi_hist
|
|
458
|
+
epi_hist.append({n: _get_attr(G.nodes[n], ALIAS_EPI, 0.0) for n in G.nodes()})
|
|
432
459
|
|
|
433
460
|
# 8) RE’MESH condicionado
|
|
434
461
|
aplicar_remesh_si_estabilizacion_global(G)
|
tnfr/helpers.py
CHANGED
|
@@ -7,13 +7,14 @@ from __future__ import annotations
|
|
|
7
7
|
from typing import Iterable, Dict, Any, Tuple, List
|
|
8
8
|
import math
|
|
9
9
|
from collections import deque
|
|
10
|
+
from statistics import fmean, StatisticsError
|
|
10
11
|
|
|
11
12
|
try:
|
|
12
13
|
import networkx as nx # solo para tipos
|
|
13
14
|
except Exception: # pragma: no cover
|
|
14
15
|
nx = None # type: ignore
|
|
15
16
|
|
|
16
|
-
from constants import DEFAULTS, ALIAS_VF, ALIAS_THETA, ALIAS_DNFR, ALIAS_EPI, ALIAS_SI
|
|
17
|
+
from .constants import DEFAULTS, ALIAS_VF, ALIAS_THETA, ALIAS_DNFR, ALIAS_EPI, ALIAS_SI
|
|
17
18
|
|
|
18
19
|
# -------------------------
|
|
19
20
|
# Utilidades numéricas
|
|
@@ -33,8 +34,10 @@ def clamp01(x: float) -> float:
|
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
def list_mean(xs: Iterable[float], default: float = 0.0) -> float:
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
try:
|
|
38
|
+
return fmean(xs)
|
|
39
|
+
except StatisticsError:
|
|
40
|
+
return default
|
|
38
41
|
|
|
39
42
|
|
|
40
43
|
def _wrap_angle(a: float) -> float:
|
|
@@ -82,7 +85,6 @@ def media_vecinal(G, n, aliases: Iterable[str], default: float = 0.0) -> float:
|
|
|
82
85
|
|
|
83
86
|
def fase_media(G, n) -> float:
|
|
84
87
|
"""Promedio circular de las fases de los vecinos."""
|
|
85
|
-
import math
|
|
86
88
|
x = 0.0
|
|
87
89
|
y = 0.0
|
|
88
90
|
count = 0
|
tnfr/observers.py
CHANGED
|
@@ -8,8 +8,8 @@ from collections import Counter
|
|
|
8
8
|
from typing import Dict, Any
|
|
9
9
|
import math
|
|
10
10
|
|
|
11
|
-
from constants import ALIAS_DNFR, ALIAS_EPI, ALIAS_THETA, ALIAS_dEPI
|
|
12
|
-
from helpers import _get_attr, list_mean, register_callback
|
|
11
|
+
from .constants import ALIAS_DNFR, ALIAS_EPI, ALIAS_THETA, ALIAS_dEPI
|
|
12
|
+
from .helpers import _get_attr, list_mean, register_callback
|
|
13
13
|
|
|
14
14
|
# -------------------------
|
|
15
15
|
# Observador estándar Γ(R)
|
|
@@ -44,15 +44,27 @@ def coherencia_global(G) -> float:
|
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
def sincronía_fase(G) -> float:
|
|
47
|
-
X =
|
|
48
|
-
Y =
|
|
47
|
+
X = [math.cos(_get_attr(G.nodes[n], ALIAS_THETA, 0.0)) for n in G.nodes()]
|
|
48
|
+
Y = [math.sin(_get_attr(G.nodes[n], ALIAS_THETA, 0.0)) for n in G.nodes()]
|
|
49
49
|
if not X:
|
|
50
50
|
return 1.0
|
|
51
|
-
|
|
52
|
-
th = math.atan2(sum(Y)/len(Y), sum(X)/len(X))
|
|
51
|
+
th = math.atan2(sum(Y) / len(Y), sum(X) / len(X))
|
|
53
52
|
# varianza angular aproximada (0 = muy sincronizado)
|
|
54
53
|
import statistics as st
|
|
55
|
-
var =
|
|
54
|
+
var = (
|
|
55
|
+
st.pvariance(
|
|
56
|
+
[
|
|
57
|
+
(
|
|
58
|
+
(_get_attr(G.nodes[n], ALIAS_THETA, 0.0) - th + math.pi)
|
|
59
|
+
% (2 * math.pi)
|
|
60
|
+
- math.pi
|
|
61
|
+
)
|
|
62
|
+
for n in G.nodes()
|
|
63
|
+
]
|
|
64
|
+
)
|
|
65
|
+
if len(X) > 1
|
|
66
|
+
else 0.0
|
|
67
|
+
)
|
|
56
68
|
return 1.0 / (1.0 + var)
|
|
57
69
|
|
|
58
70
|
def orden_kuramoto(G) -> float:
|
tnfr/ontosim.py
CHANGED
|
@@ -5,20 +5,21 @@ Módulo de orquestación mínima que encadena:
|
|
|
5
5
|
ΔNFR (campo) → Si → glifos → ecuación nodal → clamps → U’M → observadores → RE’MESH
|
|
6
6
|
"""
|
|
7
7
|
from __future__ import annotations
|
|
8
|
-
import networkx as nx
|
|
9
|
-
import math
|
|
10
|
-
import random
|
|
8
|
+
import networkx as nx
|
|
9
|
+
import math
|
|
10
|
+
import random
|
|
11
|
+
from collections import deque
|
|
11
12
|
|
|
12
|
-
from constants import DEFAULTS, attach_defaults
|
|
13
|
-
from dynamics import step as _step, run as _run
|
|
14
|
-
from dynamics import default_compute_delta_nfr
|
|
13
|
+
from .constants import DEFAULTS, attach_defaults
|
|
14
|
+
from .dynamics import step as _step, run as _run
|
|
15
|
+
from .dynamics import default_compute_delta_nfr
|
|
15
16
|
|
|
16
17
|
# API de alto nivel
|
|
17
18
|
|
|
18
19
|
def preparar_red(G: nx.Graph, *, override_defaults: bool = False, **overrides) -> nx.Graph:
|
|
19
20
|
attach_defaults(G, override=override_defaults)
|
|
20
21
|
if overrides:
|
|
21
|
-
from constants import merge_overrides
|
|
22
|
+
from .constants import merge_overrides
|
|
22
23
|
merge_overrides(G, **overrides)
|
|
23
24
|
# Inicializaciones blandas
|
|
24
25
|
G.graph.setdefault("history", {
|
|
@@ -43,11 +44,13 @@ def preparar_red(G: nx.Graph, *, override_defaults: bool = False, **overrides) -
|
|
|
43
44
|
"phase_R": [],
|
|
44
45
|
"phase_disr": [],
|
|
45
46
|
})
|
|
46
|
-
G.graph.
|
|
47
|
+
tau = int(G.graph.get("REMESH_TAU", DEFAULTS["REMESH_TAU"]))
|
|
48
|
+
maxlen = max(2 * tau + 5, 64)
|
|
49
|
+
G.graph.setdefault("_epi_hist", deque(maxlen=maxlen))
|
|
47
50
|
# Auto-attach del observador estándar si se pide
|
|
48
51
|
if G.graph.get("ATTACH_STD_OBSERVER", False):
|
|
49
52
|
try:
|
|
50
|
-
from observers import attach_standard_observer
|
|
53
|
+
from .observers import attach_standard_observer
|
|
51
54
|
attach_standard_observer(G)
|
|
52
55
|
except Exception as e:
|
|
53
56
|
G.graph.setdefault("_callback_errors", []).append(
|
tnfr/operators.py
CHANGED
|
@@ -5,14 +5,15 @@ import math
|
|
|
5
5
|
import random
|
|
6
6
|
import hashlib
|
|
7
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
|
|
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
|
+
from collections import deque
|
|
10
11
|
|
|
11
12
|
"""
|
|
12
13
|
Este módulo implementa:
|
|
13
14
|
- Los 13 glifos como operadores locales suaves.
|
|
14
15
|
- 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 `
|
|
16
|
+
- RE’MESH de red: `aplicar_remesh_red` y `aplicar_remesh_si_estabilización_global`.
|
|
16
17
|
|
|
17
18
|
Nota sobre α (alpha) de RE’MESH: se toma por prioridad de
|
|
18
19
|
1) G.graph["GLYPH_FACTORS"]["REMESH_alpha"]
|
|
@@ -186,7 +187,7 @@ def aplicar_remesh_red(G) -> None:
|
|
|
186
187
|
"""
|
|
187
188
|
tau = int(G.graph.get("REMESH_TAU", DEFAULTS["REMESH_TAU"]))
|
|
188
189
|
alpha, alpha_src = _remesh_alpha_info(G)
|
|
189
|
-
hist = G.graph.get("_epi_hist",
|
|
190
|
+
hist = G.graph.get("_epi_hist", deque())
|
|
190
191
|
if len(hist) < tau + 1:
|
|
191
192
|
return
|
|
192
193
|
|
|
@@ -256,7 +257,11 @@ def aplicar_remesh_red(G) -> None:
|
|
|
256
257
|
|
|
257
258
|
def aplicar_remesh_si_estabilizacion_global(G, pasos_estables_consecutivos: Optional[int] = None) -> None:
|
|
258
259
|
# Ventanas y umbrales
|
|
259
|
-
w_estab =
|
|
260
|
+
w_estab = (
|
|
261
|
+
pasos_estables_consecutivos
|
|
262
|
+
if pasos_estables_consecutivos is not None
|
|
263
|
+
else int(G.graph.get("REMESH_STABILITY_WINDOW", DEFAULTS["REMESH_STABILITY_WINDOW"]))
|
|
264
|
+
)
|
|
260
265
|
frac_req = float(G.graph.get("FRACTION_STABLE_REMESH", DEFAULTS["FRACTION_STABLE_REMESH"]))
|
|
261
266
|
req_extra = bool(G.graph.get("REMESH_REQUIRE_STABILITY", DEFAULTS["REMESH_REQUIRE_STABILITY"]))
|
|
262
267
|
min_sync = float(G.graph.get("REMESH_MIN_PHASE_SYNC", DEFAULTS["REMESH_MIN_PHASE_SYNC"]))
|
|
@@ -293,4 +298,4 @@ def aplicar_remesh_si_estabilizacion_global(G, pasos_estables_consecutivos: Opti
|
|
|
293
298
|
return
|
|
294
299
|
# 4) Aplicar y registrar
|
|
295
300
|
aplicar_remesh_red(G)
|
|
296
|
-
G.graph["_last_remesh_step"] = step_idx
|
|
301
|
+
G.graph["_last_remesh_step"] = step_idx
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tnfr
|
|
3
|
+
Version: 3.5.0
|
|
4
|
+
Summary: TNFR canónica: dinámica glífica modular sobre redes.
|
|
5
|
+
Author: fmg
|
|
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.md
|
|
25
|
+
Requires-Dist: networkx>=2.6
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# General Project Structure
|
|
29
|
+
|
|
30
|
+
* **Package entry point.** `__init__.py` registers modules under short names to avoid circular imports and exposes the public API: `preparar_red`, `step`, `run`, and observation utilities.
|
|
31
|
+
|
|
32
|
+
* **Configuration & constants.** `constants.py` centralizes default parameters (discretization, EPI and νf ranges, mixing weights, re-mesh limits, etc.) and provides utilities to inject them into the network (`attach_defaults`, `merge_overrides`), along with standardized aliases for node attributes.
|
|
33
|
+
|
|
34
|
+
* **Cross-cutting utilities.** `helpers.py` offers core numeric helpers, alias-based attribute accessors, neighborhood statistics, glyph history, a callback system, and computation of the sense index `Si` for each node.
|
|
35
|
+
|
|
36
|
+
* **Dynamics engine.** `dynamics.py` implements the simulation loop: ΔNFR field computation, nodal equation integration, glyph selection/application, clamps, phase coordination, history updates, and conditional re-mesh (`step` and `run`).
|
|
37
|
+
|
|
38
|
+
* **Glyph operators.** `operators.py` defines the 13 glyphs as local transformations, a dispatcher `aplicar_glifo`, and both direct and stability-conditioned re-mesh utilities.
|
|
39
|
+
|
|
40
|
+
* **Observers & metrics.** `observers.py` registers standard callbacks and computes global coherence, phase synchrony, Kuramoto order, glyph distribution, and the sense vector `Σ⃗`, among others.
|
|
41
|
+
|
|
42
|
+
* **Simulation orchestration.** `ontosim.py` prepares a NetworkX graph, attaches configuration, and initializes attributes (EPI, phases, frequencies) before delegating dynamics to `dynamics.step`/`run`.
|
|
43
|
+
|
|
44
|
+
* **Demo CLI.** `main.py` generates an Erdős–Rényi network, lets you tweak basic parameters, and runs the simulation while displaying final metrics.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Key Concepts to Grasp
|
|
49
|
+
|
|
50
|
+
* **Aliased dependency tree.** Modules import each other via global aliases to simplify access and prevent cycles—essential for navigating the code unambiguously.
|
|
51
|
+
|
|
52
|
+
* **Normalized node attributes.** All data (EPI, phase `θ`, frequency `νf`, `ΔNFR`, etc.) live in `G.nodes[n]` under compatible alias names, making extensions and custom hooks straightforward.
|
|
53
|
+
|
|
54
|
+
* **Sense Index (`Si`).** Combines normalized frequency, phase dispersion, and field magnitude to evaluate each node’s “sense,” influencing glyph selection.
|
|
55
|
+
|
|
56
|
+
* **Step-wise engine.** `dynamics.step` orchestrates eight phases: field computation, `Si`, glyph selection & application, integration, clamps, phase coordination, history update, and conditioned re-mesh.
|
|
57
|
+
|
|
58
|
+
* **Glyphs as operators.** Each glyph applies a smooth transformation to node attributes (emission, diffusion, coupling, dissonance, etc.), dispatched by a configurable, typographic name.
|
|
59
|
+
|
|
60
|
+
* **Network re-mesh.** Mixes the current state with a past one (memory `τ`) to stabilize the network, with clear precedence for `α` and conditions based on recent stability and synchrony history.
|
|
61
|
+
|
|
62
|
+
* **Callbacks & observers.** The `Γ(R)` system lets you hook functions before/after each step and after re-mesh, enabling monitoring or external intervention.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Recommendations for Going Deeper
|
|
67
|
+
|
|
68
|
+
* **NetworkX & the Graph API.** Get comfortable with how NetworkX handles attributes and topology; all dynamics operate on `Graph` objects and their properties.
|
|
69
|
+
|
|
70
|
+
* **Extending the ΔNFR field.** Explore `set_delta_nfr_hook` to implement alternative nodal fields and learn how metadata and mixing weights are recorded.
|
|
71
|
+
|
|
72
|
+
* **Designing new glyphs.** Review `operators.py` to add operators or adjust factors in `DEFAULTS['GLYPH_FACTORS']`.
|
|
73
|
+
|
|
74
|
+
* **Custom observers.** Implement your own metrics via `register_callback` or by extending `observers.py` to measure phenomena specific to your study.
|
|
75
|
+
|
|
76
|
+
* **Theoretical reading.** For conceptual background, see the included PDFs (`TNFR.pdf`, *El Pulso que nos Atraviesa*), which deepen the fractal-resonant framework.
|
|
77
|
+
|
|
78
|
+
* **Advanced parameters.** Experiment with adaptive phase coordination, stability criteria, and the glyph grammar to observe their impact on network self-organization.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
**Mastering these pieces will let you extend the simulation, build analysis pipelines and connect the theory with computational applications.**
|
|
83
|
+
|
|
84
|
+
## Testing
|
|
85
|
+
|
|
86
|
+
Install the dependencies and project in editable mode before running the test suite with `pytest`:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
pip install networkx
|
|
90
|
+
pip install -e .
|
|
91
|
+
pytest
|
|
92
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
tnfr/__init__.py,sha256=LYIpDLjPnC4MxrviM2-Lg8KudbzXM_56aepRg_BEJG4,581
|
|
2
|
+
tnfr/constants.py,sha256=_775sPHussR9vgkWRCLC6dzwgk_1_lLnSlWT8sBWR3U,7677
|
|
3
|
+
tnfr/dynamics.py,sha256=btDyC3jIOmND1c1qqA2RDo0f3NraJKZwggO5BqBAmCA,23967
|
|
4
|
+
tnfr/helpers.py,sha256=3DT7nb2FBrJxSEFcZ_Q9qiR9JGbjCNJOzA49bzN6hbM,6067
|
|
5
|
+
tnfr/main.py,sha256=TEngteuC9MD7Ec9bNGuCC9ym-2ohbh202-HGArCR4tk,1506
|
|
6
|
+
tnfr/observers.py,sha256=PTw3hxk7KD-Yx_CvCIU09icuhyYD6uNU6SvF80UvP-Y,5354
|
|
7
|
+
tnfr/ontosim.py,sha256=9GfEtiLIdJOPJUTufcq_MssAA9J8AfChHU6HKb3DIJY,5628
|
|
8
|
+
tnfr/operators.py,sha256=ONUC0tzB7y0Ad4MppLjFc8bmlTjPol-l_xdEB_84uaw,12293
|
|
9
|
+
tnfr-3.5.0.dist-info/licenses/LICENSE.md,sha256=SRvvhXLrKtseuK6DARbuJffuXOXqAyk3wvF2n0t1SWA,1109
|
|
10
|
+
tnfr-3.5.0.dist-info/METADATA,sha256=N420pHD6T9d8kkDwi202Klyh7mu7oIx3-CmwYZo_CwA,5576
|
|
11
|
+
tnfr-3.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
+
tnfr-3.5.0.dist-info/entry_points.txt,sha256=6ZYrYA3Z-kKvqiYddIAj9c8ZcoVlK7pj-qCBvlNtuLE,40
|
|
13
|
+
tnfr-3.5.0.dist-info/top_level.txt,sha256=Q2HJnvc5Rt2VHwVvyBTnNPT4SfmJWnCj7XUxxEvQa7c,5
|
|
14
|
+
tnfr-3.5.0.dist-info/RECORD,,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2025 TNFR - Teoría de la naturaleza fractral resonante
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
tnfr-3.0.2.dist-info/METADATA
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: tnfr
|
|
3
|
-
Version: 3.0.2
|
|
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
|
tnfr-3.0.2.dist-info/RECORD
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
tnfr/__init__.py,sha256=RBQO7L4qQQWMK0S6S9j2Ev52U6GOH2iNTaH2d8o5Zj8,1798
|
|
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.2.dist-info/licenses/LICENSE.txt,sha256=xTjBNhy3N8pomFljrCkD1d34SmAEWv8hyJMMOjNMH0M,1071
|
|
10
|
-
tnfr-3.0.2.dist-info/METADATA,sha256=IjrYafbG2gRYW4LcsQMsKFG4VqT3zw-H5qHolXKuk3g,1325
|
|
11
|
-
tnfr-3.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
-
tnfr-3.0.2.dist-info/top_level.txt,sha256=Q2HJnvc5Rt2VHwVvyBTnNPT4SfmJWnCj7XUxxEvQa7c,5
|
|
13
|
-
tnfr-3.0.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|