tnfr 4.0.0__py3-none-any.whl → 4.1.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 +59 -57
- tnfr/cli.py +17 -1
- tnfr/constants.py +10 -7
- tnfr/dynamics.py +35 -26
- tnfr/gamma.py +10 -1
- tnfr/grammar.py +6 -0
- tnfr/helpers.py +6 -1
- tnfr/operators.py +22 -5
- tnfr/presets.py +2 -1
- tnfr/program.py +8 -0
- tnfr/trace.py +11 -6
- {tnfr-4.0.0.dist-info → tnfr-4.1.0.dist-info}/METADATA +3 -1
- tnfr-4.1.0.dist-info/RECORD +24 -0
- tnfr-4.0.0.dist-info/RECORD +0 -24
- {tnfr-4.0.0.dist-info → tnfr-4.1.0.dist-info}/WHEEL +0 -0
- {tnfr-4.0.0.dist-info → tnfr-4.1.0.dist-info}/entry_points.txt +0 -0
- {tnfr-4.0.0.dist-info → tnfr-4.1.0.dist-info}/licenses/LICENSE.md +0 -0
- {tnfr-4.0.0.dist-info → tnfr-4.1.0.dist-info}/top_level.txt +0 -0
tnfr/__init__.py
CHANGED
|
@@ -1,57 +1,59 @@
|
|
|
1
|
-
|
|
2
|
-
from __future__ import annotations
|
|
3
|
-
"""
|
|
4
|
-
TNFR — Teoría de la Naturaleza Fractal Resonante
|
|
5
|
-
API pública del paquete.
|
|
6
|
-
|
|
7
|
-
Ecuación nodal:
|
|
8
|
-
∂EPI/∂t = νf · ΔNFR(t)
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
__version__ = "4.
|
|
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
|
|
17
|
-
from .gamma import GAMMA_REGISTRY, eval_gamma, kuramoto_R_psi
|
|
18
|
-
from .grammar import enforce_canonical_grammar, on_applied_glifo
|
|
19
|
-
from .sense import (
|
|
20
|
-
GLYPHS_CANONICAL, glyph_angle, glyph_unit,
|
|
21
|
-
sigma_vector_node, sigma_vector_global,
|
|
22
|
-
push_sigma_snapshot, sigma_series, sigma_rose,
|
|
23
|
-
register_sigma_callback,
|
|
24
|
-
)
|
|
25
|
-
from .metrics import (
|
|
26
|
-
register_metrics_callbacks,
|
|
27
|
-
Tg_global, Tg_by_node,
|
|
28
|
-
latency_series, glifogram_series,
|
|
29
|
-
glyph_top, glyph_dwell_stats,
|
|
30
|
-
)
|
|
31
|
-
from .trace import register_trace
|
|
32
|
-
from .program import play, seq, block, target, wait, THOL, TARGET, WAIT
|
|
33
|
-
from .cli import main as cli_main
|
|
34
|
-
from .scenarios import build_graph
|
|
35
|
-
from .presets import get_preset
|
|
36
|
-
from .types import NodeState
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
1
|
+
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
"""
|
|
4
|
+
TNFR — Teoría de la Naturaleza Fractal Resonante
|
|
5
|
+
API pública del paquete.
|
|
6
|
+
|
|
7
|
+
Ecuación nodal:
|
|
8
|
+
∂EPI/∂t = νf · ΔNFR(t)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
__version__ = "4.1.0"
|
|
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
|
|
17
|
+
from .gamma import GAMMA_REGISTRY, eval_gamma, kuramoto_R_psi
|
|
18
|
+
from .grammar import enforce_canonical_grammar, on_applied_glifo
|
|
19
|
+
from .sense import (
|
|
20
|
+
GLYPHS_CANONICAL, glyph_angle, glyph_unit,
|
|
21
|
+
sigma_vector_node, sigma_vector_global,
|
|
22
|
+
push_sigma_snapshot, sigma_series, sigma_rose,
|
|
23
|
+
register_sigma_callback,
|
|
24
|
+
)
|
|
25
|
+
from .metrics import (
|
|
26
|
+
register_metrics_callbacks,
|
|
27
|
+
Tg_global, Tg_by_node,
|
|
28
|
+
latency_series, glifogram_series,
|
|
29
|
+
glyph_top, glyph_dwell_stats,
|
|
30
|
+
)
|
|
31
|
+
from .trace import register_trace
|
|
32
|
+
from .program import play, seq, block, target, wait, THOL, TARGET, WAIT, ejemplo_canonico_basico
|
|
33
|
+
from .cli import main as cli_main
|
|
34
|
+
from .scenarios import build_graph
|
|
35
|
+
from .presets import get_preset
|
|
36
|
+
from .types import NodeState
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
"preparar_red",
|
|
41
|
+
"step", "run", "set_delta_nfr_hook",
|
|
42
|
+
|
|
43
|
+
"attach_standard_observer", "coherencia_global", "orden_kuramoto",
|
|
44
|
+
"GAMMA_REGISTRY", "eval_gamma", "kuramoto_R_psi",
|
|
45
|
+
"enforce_canonical_grammar", "on_applied_glifo",
|
|
46
|
+
"GLYPHS_CANONICAL", "glyph_angle", "glyph_unit",
|
|
47
|
+
"sigma_vector_node", "sigma_vector_global",
|
|
48
|
+
"push_sigma_snapshot", "sigma_series", "sigma_rose",
|
|
49
|
+
"register_sigma_callback",
|
|
50
|
+
"register_metrics_callbacks",
|
|
51
|
+
"register_trace",
|
|
52
|
+
"Tg_global", "Tg_by_node",
|
|
53
|
+
"latency_series", "glifogram_series",
|
|
54
|
+
"glyph_top", "glyph_dwell_stats",
|
|
55
|
+
"play", "seq", "block", "target", "wait", "THOL", "TARGET", "WAIT",
|
|
56
|
+
"cli_main", "build_graph", "get_preset", "NodeState",
|
|
57
|
+
"ejemplo_canonico_basico",
|
|
58
|
+
"__version__",
|
|
59
|
+
]
|
tnfr/cli.py
CHANGED
|
@@ -21,7 +21,8 @@ from .metrics import (
|
|
|
21
21
|
)
|
|
22
22
|
from .trace import register_trace
|
|
23
23
|
from .program import play, seq, block, wait, target
|
|
24
|
-
from .dynamics import step, _update_history
|
|
24
|
+
from .dynamics import step, _update_history, default_glyph_selector, parametric_glyph_selector
|
|
25
|
+
from .gamma import GAMMA_REGISTRY
|
|
25
26
|
from .scenarios import build_graph
|
|
26
27
|
from .presets import get_preset
|
|
27
28
|
|
|
@@ -72,6 +73,9 @@ def _attach_callbacks(G: nx.Graph) -> None:
|
|
|
72
73
|
def cmd_run(args: argparse.Namespace) -> int:
|
|
73
74
|
G = build_graph(n=args.nodes, topology=args.topology, seed=args.seed)
|
|
74
75
|
_attach_callbacks(G)
|
|
76
|
+
G.graph.setdefault("GRAMMAR_CANON", DEFAULTS["GRAMMAR_CANON"]).update({"enabled": bool(args.grammar_canon)})
|
|
77
|
+
G.graph["glyph_selector"] = default_glyph_selector if args.selector == "basic" else parametric_glyph_selector
|
|
78
|
+
G.graph["GAMMA"] = {"type": args.gamma}
|
|
75
79
|
|
|
76
80
|
if args.preset:
|
|
77
81
|
program = get_preset(args.preset)
|
|
@@ -96,6 +100,9 @@ def cmd_run(args: argparse.Namespace) -> int:
|
|
|
96
100
|
def cmd_sequence(args: argparse.Namespace) -> int:
|
|
97
101
|
G = build_graph(n=args.nodes, topology=args.topology, seed=args.seed)
|
|
98
102
|
_attach_callbacks(G)
|
|
103
|
+
G.graph.setdefault("GRAMMAR_CANON", DEFAULTS["GRAMMAR_CANON"]).update({"enabled": bool(args.grammar_canon)})
|
|
104
|
+
G.graph["glyph_selector"] = default_glyph_selector if args.selector == "basic" else parametric_glyph_selector
|
|
105
|
+
G.graph["GAMMA"] = {"type": args.gamma}
|
|
99
106
|
|
|
100
107
|
if args.preset:
|
|
101
108
|
program = get_preset(args.preset)
|
|
@@ -114,6 +121,9 @@ def cmd_sequence(args: argparse.Namespace) -> int:
|
|
|
114
121
|
def cmd_metrics(args: argparse.Namespace) -> int:
|
|
115
122
|
G = build_graph(n=args.nodes, topology=args.topology, seed=args.seed)
|
|
116
123
|
_attach_callbacks(G)
|
|
124
|
+
G.graph.setdefault("GRAMMAR_CANON", DEFAULTS["GRAMMAR_CANON"]).update({"enabled": bool(args.grammar_canon)})
|
|
125
|
+
G.graph["glyph_selector"] = default_glyph_selector if args.selector == "basic" else parametric_glyph_selector
|
|
126
|
+
G.graph["GAMMA"] = {"type": args.gamma}
|
|
117
127
|
for _ in range(int(args.steps or 200)):
|
|
118
128
|
step(G)
|
|
119
129
|
|
|
@@ -147,6 +157,9 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
147
157
|
p_run.add_argument("--preset", type=str, default=None)
|
|
148
158
|
p_run.add_argument("--save-history", dest="save_history", type=str, default=None)
|
|
149
159
|
p_run.add_argument("--summary", action="store_true")
|
|
160
|
+
p_run.add_argument("--no-canon", dest="grammar_canon", action="store_false", default=True, help="Desactiva gramática canónica")
|
|
161
|
+
p_run.add_argument("--selector", choices=["basic", "param"], default="basic")
|
|
162
|
+
p_run.add_argument("--gamma", choices=list(GAMMA_REGISTRY.keys()), default="none")
|
|
150
163
|
p_run.set_defaults(func=cmd_run)
|
|
151
164
|
|
|
152
165
|
p_seq = sub.add_parser("sequence", help="Ejecutar una secuencia (preset o YAML/JSON)")
|
|
@@ -163,6 +176,9 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
163
176
|
p_met.add_argument("--topology", choices=["ring", "complete", "erdos"], default="ring")
|
|
164
177
|
p_met.add_argument("--steps", type=int, default=300)
|
|
165
178
|
p_met.add_argument("--seed", type=int, default=1)
|
|
179
|
+
p_met.add_argument("--no-canon", dest="grammar_canon", action="store_false", default=True, help="Desactiva gramática canónica")
|
|
180
|
+
p_met.add_argument("--selector", choices=["basic", "param"], default="basic")
|
|
181
|
+
p_met.add_argument("--gamma", choices=list(GAMMA_REGISTRY.keys()), default="none")
|
|
166
182
|
p_met.add_argument("--save", type=str, default=None)
|
|
167
183
|
p_met.set_defaults(func=cmd_metrics)
|
|
168
184
|
|
tnfr/constants.py
CHANGED
|
@@ -72,8 +72,9 @@ DEFAULTS: Dict[str, Any] = {
|
|
|
72
72
|
# Criterios de estabilidad (para activar RE’MESH de red)
|
|
73
73
|
"EPS_DNFR_STABLE": 1e-3,
|
|
74
74
|
"EPS_DEPI_STABLE": 1e-3,
|
|
75
|
-
"FRACTION_STABLE_REMESH": 0.80, # fracción de nodos estables requerida
|
|
76
|
-
"REMESH_COOLDOWN_VENTANA": 20, # pasos mínimos entre RE’MESH
|
|
75
|
+
"FRACTION_STABLE_REMESH": 0.80, # fracción de nodos estables requerida
|
|
76
|
+
"REMESH_COOLDOWN_VENTANA": 20, # pasos mínimos entre RE’MESH
|
|
77
|
+
"REMESH_COOLDOWN_TS": 0.0, # cooldown adicional por tiempo simulado
|
|
77
78
|
# Gating adicional basado en observadores (conmutador + ventana)
|
|
78
79
|
"REMESH_REQUIRE_STABILITY": False, # si True, exige ventana de estabilidad multi-métrica
|
|
79
80
|
"REMESH_STABILITY_WINDOW": 25, # tamaño de ventana para evaluar estabilidad
|
|
@@ -82,8 +83,9 @@ DEFAULTS: Dict[str, Any] = {
|
|
|
82
83
|
"REMESH_LOG_EVENTS": True, # guarda eventos y metadatos del RE’MESH
|
|
83
84
|
|
|
84
85
|
# RE’MESH: memoria τ y mezcla α
|
|
85
|
-
"REMESH_TAU": 8, # pasos hacia atrás
|
|
86
|
-
"REMESH_ALPHA": 0.5, # mezcla con pasado
|
|
86
|
+
"REMESH_TAU": 8, # pasos hacia atrás
|
|
87
|
+
"REMESH_ALPHA": 0.5, # mezcla con pasado
|
|
88
|
+
"REMESH_ALPHA_HARD": False, # si True ignora GLYPH_FACTORS['REMESH_alpha']
|
|
87
89
|
|
|
88
90
|
# Histéresis glífica
|
|
89
91
|
"GLYPH_HYSTERESIS_WINDOW": 7,
|
|
@@ -92,13 +94,13 @@ DEFAULTS: Dict[str, Any] = {
|
|
|
92
94
|
"GLYPH_SELECTOR_MARGIN": 0.05,
|
|
93
95
|
|
|
94
96
|
# Ventana para estimar la carga glífica en history/plots
|
|
95
|
-
"GLYPH_LOAD_WINDOW": 50,
|
|
97
|
+
"GLYPH_LOAD_WINDOW": 50,
|
|
96
98
|
|
|
97
99
|
# Tamaño de ventana para coherencia promedio W̄
|
|
98
100
|
"WBAR_WINDOW": 25,
|
|
99
101
|
|
|
100
102
|
# Factores suaves por glifo (operadores)
|
|
101
|
-
"GLYPH_FACTORS": {
|
|
103
|
+
"GLYPH_FACTORS": {
|
|
102
104
|
"AL_boost": 0.05, # A’L — pequeña emisión
|
|
103
105
|
"EN_mix": 0.25, # E’N — mezcla con vecindad
|
|
104
106
|
"IL_dnfr_factor": 0.7, # I’L — reduce ΔNFR
|
|
@@ -107,7 +109,7 @@ DEFAULTS: Dict[str, Any] = {
|
|
|
107
109
|
"RA_epi_diff": 0.15, # R’A — difusión EPI
|
|
108
110
|
"SHA_vf_factor": 0.85, # SH’A — baja νf
|
|
109
111
|
"VAL_scale": 1.15, # VA’L — expande EPI
|
|
110
|
-
"NUL_scale": 0.85, # NU’L — contrae EPI
|
|
112
|
+
"NUL_scale": 0.85, # NU’L — contrae EPI
|
|
111
113
|
"THOL_accel": 0.10, # T’HOL — acelera (seg. deriv.) si hay umbral
|
|
112
114
|
"ZHIR_theta_shift": 1.57079632679, # Z’HIR — desplazamiento ~π/2
|
|
113
115
|
"NAV_jitter": 0.05, # NA’V — pequeña inestabilidad creativa
|
|
@@ -154,6 +156,7 @@ DEFAULTS: Dict[str, Any] = {
|
|
|
154
156
|
"R0": 0.0,
|
|
155
157
|
},
|
|
156
158
|
"CALLBACKS_STRICT": False, # si True, un error en callback detiene; si False, se loguea y continúa
|
|
159
|
+
"VALIDATORS_STRICT": False, # si True, alerta si se clampa fuera de rango
|
|
157
160
|
}
|
|
158
161
|
|
|
159
162
|
# Gramática glífica canónica
|
tnfr/dynamics.py
CHANGED
|
@@ -187,7 +187,7 @@ def integrar_epi_euler(G, dt: float | None = None) -> None:
|
|
|
187
187
|
update_epi_via_nodal_equation(G, dt=dt)
|
|
188
188
|
|
|
189
189
|
|
|
190
|
-
def aplicar_clamps_canonicos(nd: Dict[str, Any], G=None) -> None:
|
|
190
|
+
def aplicar_clamps_canonicos(nd: Dict[str, Any], G=None, node=None) -> None:
|
|
191
191
|
eps_min = float((G.graph.get("EPI_MIN") if G is not None else DEFAULTS["EPI_MIN"]))
|
|
192
192
|
eps_max = float((G.graph.get("EPI_MAX") if G is not None else DEFAULTS["EPI_MAX"]))
|
|
193
193
|
vf_min = float((G.graph.get("VF_MIN") if G is not None else DEFAULTS["VF_MIN"]))
|
|
@@ -197,6 +197,14 @@ def aplicar_clamps_canonicos(nd: Dict[str, Any], G=None) -> None:
|
|
|
197
197
|
vf = _get_attr(nd, ALIAS_VF, 0.0)
|
|
198
198
|
th = _get_attr(nd, ALIAS_THETA, 0.0)
|
|
199
199
|
|
|
200
|
+
strict = bool((G.graph.get("VALIDATORS_STRICT") if G is not None else DEFAULTS.get("VALIDATORS_STRICT", False)))
|
|
201
|
+
if strict and G is not None:
|
|
202
|
+
hist = G.graph.setdefault("history", {}).setdefault("clamp_alerts", [])
|
|
203
|
+
if epi < eps_min or epi > eps_max:
|
|
204
|
+
hist.append({"node": node, "attr": "EPI", "value": float(epi)})
|
|
205
|
+
if vf < vf_min or vf > vf_max:
|
|
206
|
+
hist.append({"node": node, "attr": "VF", "value": float(vf)})
|
|
207
|
+
|
|
200
208
|
_set_attr(nd, ALIAS_EPI, clamp(epi, eps_min, eps_max))
|
|
201
209
|
_set_attr(nd, ALIAS_VF, clamp(vf, vf_min, vf_max))
|
|
202
210
|
if (G.graph.get("THETA_WRAP") if G is not None else DEFAULTS["THETA_WRAP"]):
|
|
@@ -212,6 +220,10 @@ def coordinar_fase_global_vecinal(G, fuerza_global: float | None = None, fuerza_
|
|
|
212
220
|
"""
|
|
213
221
|
g = G.graph
|
|
214
222
|
defaults = DEFAULTS
|
|
223
|
+
hist = g.setdefault("history", {})
|
|
224
|
+
hist_state = hist.setdefault("phase_state", [])
|
|
225
|
+
hist_R = hist.setdefault("phase_R", [])
|
|
226
|
+
hist_disr = hist.setdefault("phase_disr", [])
|
|
215
227
|
# 0) Si hay fuerzas explícitas, usar y salir del modo adaptativo
|
|
216
228
|
if (fuerza_global is not None) or (fuerza_vecinal is not None):
|
|
217
229
|
kG = float(
|
|
@@ -273,20 +285,15 @@ def coordinar_fase_global_vecinal(G, fuerza_global: float | None = None, fuerza_
|
|
|
273
285
|
kL = _step(kL, kL_t, kL_min, kL_max)
|
|
274
286
|
|
|
275
287
|
# 5) Persistir en G.graph y log de serie
|
|
276
|
-
g["PHASE_K_GLOBAL"] = kG
|
|
277
|
-
g["PHASE_K_LOCAL"] = kL
|
|
278
|
-
hist = g.setdefault("history", {})
|
|
279
|
-
hist_kG = hist.setdefault("phase_kG", [])
|
|
280
|
-
hist_kL = hist.setdefault("phase_kL", [])
|
|
281
|
-
hist_state = hist.setdefault("phase_state", [])
|
|
282
|
-
hist_R = hist.setdefault("phase_R", [])
|
|
283
|
-
hist_disr = hist.setdefault("phase_disr", [])
|
|
284
|
-
hist_kG.append(float(kG))
|
|
285
|
-
hist_kL.append(float(kL))
|
|
286
288
|
hist_state.append(state)
|
|
287
289
|
hist_R.append(float(R))
|
|
288
290
|
hist_disr.append(float(disr))
|
|
289
291
|
|
|
292
|
+
g["PHASE_K_GLOBAL"] = kG
|
|
293
|
+
g["PHASE_K_LOCAL"] = kL
|
|
294
|
+
hist.setdefault("phase_kG", []).append(float(kG))
|
|
295
|
+
hist.setdefault("phase_kL", []).append(float(kL))
|
|
296
|
+
|
|
290
297
|
# 6) Fase GLOBAL (centroide) para empuje
|
|
291
298
|
X = list(math.cos(_get_attr(G.nodes[n], ALIAS_THETA, 0.0)) for n in G.nodes())
|
|
292
299
|
Y = list(math.sin(_get_attr(G.nodes[n], ALIAS_THETA, 0.0)) for n in G.nodes())
|
|
@@ -345,6 +352,21 @@ def _norms_para_selector(G) -> dict:
|
|
|
345
352
|
G.graph["_sel_norms"] = norms
|
|
346
353
|
return norms
|
|
347
354
|
|
|
355
|
+
|
|
356
|
+
def _soft_grammar_prefilter(G, n, cand, dnfr, accel):
|
|
357
|
+
"""Gramática suave: evita repeticiones antes de la canónica."""
|
|
358
|
+
gram = G.graph.get("GRAMMAR", DEFAULTS.get("GRAMMAR", {}))
|
|
359
|
+
gwin = int(gram.get("window", 3))
|
|
360
|
+
avoid = set(gram.get("avoid_repeats", []))
|
|
361
|
+
force_dn = float(gram.get("force_dnfr", 0.60))
|
|
362
|
+
force_ac = float(gram.get("force_accel", 0.60))
|
|
363
|
+
fallbacks = gram.get("fallbacks", {})
|
|
364
|
+
nd = G.nodes[n]
|
|
365
|
+
if cand in avoid and reciente_glifo(nd, cand, gwin):
|
|
366
|
+
if not (dnfr >= force_dn or accel >= force_ac):
|
|
367
|
+
cand = fallbacks.get(cand, cand)
|
|
368
|
+
return cand
|
|
369
|
+
|
|
348
370
|
def parametric_glyph_selector(G, n) -> str:
|
|
349
371
|
"""Multiobjetivo: combina Si, |ΔNFR|_norm y |accel|_norm + histéresis.
|
|
350
372
|
Reglas base:
|
|
@@ -412,22 +434,9 @@ def parametric_glyph_selector(G, n) -> str:
|
|
|
412
434
|
elif score <= 0.33 and cand in ("NA’V","R’A","I’L"):
|
|
413
435
|
cand = "O’Z" if dnfr >= dnfr_lo else "Z’HIR"
|
|
414
436
|
except NameError:
|
|
415
|
-
# por si 'score' no se definió (robustez), no forzamos nada
|
|
416
437
|
pass
|
|
417
438
|
|
|
418
|
-
|
|
419
|
-
gram = G.graph.get("GRAMMAR", DEFAULTS.get("GRAMMAR", {}))
|
|
420
|
-
gwin = int(gram.get("window", 3))
|
|
421
|
-
avoid = set(gram.get("avoid_repeats", []))
|
|
422
|
-
force_dn = float(gram.get("force_dnfr", 0.60))
|
|
423
|
-
force_ac = float(gram.get("force_accel", 0.60))
|
|
424
|
-
fallbacks = gram.get("fallbacks", {})
|
|
425
|
-
|
|
426
|
-
if cand in avoid and reciente_glifo(nd, cand, gwin):
|
|
427
|
-
# Solo permitimos repetir si el campo "insiste": dnfr o accel altos (ya normalizados)
|
|
428
|
-
if not (dnfr >= force_dn or accel >= force_ac):
|
|
429
|
-
cand = fallbacks.get(cand, "R’A")
|
|
430
|
-
|
|
439
|
+
cand = _soft_grammar_prefilter(G, n, cand, dnfr, accel)
|
|
431
440
|
return cand
|
|
432
441
|
|
|
433
442
|
# -------------------------
|
|
@@ -470,7 +479,7 @@ def step(G, *, dt: float | None = None, use_Si: bool = True, apply_glyphs: bool
|
|
|
470
479
|
|
|
471
480
|
# 5) Clamps
|
|
472
481
|
for n in G.nodes():
|
|
473
|
-
aplicar_clamps_canonicos(G.nodes[n], G)
|
|
482
|
+
aplicar_clamps_canonicos(G.nodes[n], G, n)
|
|
474
483
|
|
|
475
484
|
# 6) Coordinación de fase
|
|
476
485
|
coordinar_fase_global_vecinal(G, None, None)
|
tnfr/gamma.py
CHANGED
|
@@ -3,10 +3,19 @@
|
|
|
3
3
|
Γi(R): acoplamientos de red para la ecuación nodal extendida
|
|
4
4
|
∂EPI/∂t = νf · ΔNFR(t) + Γi(R)
|
|
5
5
|
|
|
6
|
+
`Γ` suma un término de acoplamiento dependiente del orden global de fase
|
|
7
|
+
`R`. La especificación se toma de ``G.graph['GAMMA']`` (ver
|
|
8
|
+
``DEFAULTS['GAMMA']``) con parámetros como:
|
|
9
|
+
|
|
10
|
+
* ``type`` – modo de acoplamiento (``none``, ``kuramoto_linear``,
|
|
11
|
+
``kuramoto_bandpass``)
|
|
12
|
+
* ``beta`` – ganancia del acoplamiento
|
|
13
|
+
* ``R0`` – umbral de activación (solo lineal)
|
|
14
|
+
|
|
6
15
|
Provee:
|
|
7
16
|
- kuramoto_R_psi(G): (R, ψ) orden de Kuramoto en la red
|
|
8
17
|
- GAMMA_REGISTRY: registro de acoplamientos canónicos
|
|
9
|
-
- eval_gamma(G, node, t): evalúa Γ para cada nodo según
|
|
18
|
+
- eval_gamma(G, node, t): evalúa Γ para cada nodo según la config
|
|
10
19
|
"""
|
|
11
20
|
from __future__ import annotations
|
|
12
21
|
from typing import Dict, Any, Tuple
|
tnfr/grammar.py
CHANGED
|
@@ -142,6 +142,12 @@ def on_applied_glifo(G, n, applied: str) -> None:
|
|
|
142
142
|
# -------------------------
|
|
143
143
|
|
|
144
144
|
def select_and_apply_with_grammar(G, n, selector, window: int) -> None:
|
|
145
|
+
"""Aplica gramática canónica sobre la propuesta del selector.
|
|
146
|
+
|
|
147
|
+
El selector puede incluir una gramática **suave** (pre–filtro) como
|
|
148
|
+
`parametric_glyph_selector`; la presente función garantiza que la
|
|
149
|
+
gramática canónica tenga precedencia final.
|
|
150
|
+
"""
|
|
145
151
|
from .operators import aplicar_glifo
|
|
146
152
|
cand = selector(G, n)
|
|
147
153
|
cand = enforce_canonical_grammar(G, n, cand)
|
tnfr/helpers.py
CHANGED
|
@@ -186,7 +186,10 @@ def invoke_callbacks(G, event: str, ctx: dict | None = None):
|
|
|
186
186
|
def compute_Si(G, *, inplace: bool = True) -> Dict[Any, float]:
|
|
187
187
|
"""Calcula Si por nodo y lo escribe en G.nodes[n]["Si"].
|
|
188
188
|
|
|
189
|
-
|
|
189
|
+
Fórmula:
|
|
190
|
+
Si = α·νf_norm + β·(1 - disp_fase_local) + γ·(1 - |ΔNFR|/max|ΔNFR|)
|
|
191
|
+
También guarda en ``G.graph`` los pesos normalizados y la
|
|
192
|
+
sensibilidad parcial (∂Si/∂componente).
|
|
190
193
|
"""
|
|
191
194
|
alpha = float(G.graph.get("SI_WEIGHTS", DEFAULTS["SI_WEIGHTS"]).get("alpha", 0.34))
|
|
192
195
|
beta = float(G.graph.get("SI_WEIGHTS", DEFAULTS["SI_WEIGHTS"]).get("beta", 0.33))
|
|
@@ -196,6 +199,8 @@ def compute_Si(G, *, inplace: bool = True) -> Dict[Any, float]:
|
|
|
196
199
|
alpha = beta = gamma = 1/3
|
|
197
200
|
else:
|
|
198
201
|
alpha, beta, gamma = alpha/s, beta/s, gamma/s
|
|
202
|
+
G.graph["_Si_weights"] = {"alpha": alpha, "beta": beta, "gamma": gamma}
|
|
203
|
+
G.graph["_Si_sensitivity"] = {"dSi_dvf_norm": alpha, "dSi_ddisp_fase": -beta, "dSi_ddnfr_norm": -gamma}
|
|
199
204
|
|
|
200
205
|
# Normalización de νf en red
|
|
201
206
|
vfs = [abs(_get_attr(G.nodes[n], ALIAS_VF, 0.0)) for n in G.nodes()]
|
tnfr/operators.py
CHANGED
|
@@ -21,6 +21,15 @@ Nota sobre α (alpha) de RE’MESH: se toma por prioridad de
|
|
|
21
21
|
3) DEFAULTS["REMESH_ALPHA"]
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
+
|
|
25
|
+
def _node_offset(G, n) -> int:
|
|
26
|
+
"""Deterministic node index used for jitter seeds."""
|
|
27
|
+
mapping = G.graph.get("_node_offset_map")
|
|
28
|
+
if mapping is None or len(mapping) != G.number_of_nodes():
|
|
29
|
+
mapping = {node: idx for idx, node in enumerate(sorted(G.nodes(), key=lambda x: str(x)))}
|
|
30
|
+
G.graph["_node_offset_map"] = mapping
|
|
31
|
+
return int(mapping.get(n, 0))
|
|
32
|
+
|
|
24
33
|
# -------------------------
|
|
25
34
|
# Glifos (operadores locales)
|
|
26
35
|
# -------------------------
|
|
@@ -55,7 +64,7 @@ def op_OZ(G, n): # O’Z — Disonancia (aumenta ΔNFR o añade ruido)
|
|
|
55
64
|
if bool(G.graph.get("OZ_NOISE_MODE", False)):
|
|
56
65
|
base_seed = int(G.graph.get("RANDOM_SEED", 0))
|
|
57
66
|
step_idx = len(G.graph.get("history", {}).get("C_steps", []))
|
|
58
|
-
rnd = random.Random(base_seed + step_idx*1000003 +
|
|
67
|
+
rnd = random.Random(base_seed + step_idx*1000003 + _node_offset(G, n) % 1009)
|
|
59
68
|
sigma = float(G.graph.get("OZ_SIGMA", 0.1))
|
|
60
69
|
noise = sigma * (2.0 * rnd.random() - 1.0)
|
|
61
70
|
_set_attr(nd, ALIAS_DNFR, dnfr + noise)
|
|
@@ -124,7 +133,7 @@ def op_NAV(G, n): # NA’V — Transición (jitter suave de ΔNFR)
|
|
|
124
133
|
base_seed = int(G.graph.get("RANDOM_SEED", 0))
|
|
125
134
|
# opcional: pequeño offset para evitar misma secuencia en todos los nodos/pasos
|
|
126
135
|
step_idx = len(G.graph.get("history", {}).get("C_steps", []))
|
|
127
|
-
rnd = random.Random(base_seed + step_idx*1000003 +
|
|
136
|
+
rnd = random.Random(base_seed + step_idx*1000003 + _node_offset(G, n) % 1009)
|
|
128
137
|
jitter = j * (2.0 * rnd.random() - 1.0)
|
|
129
138
|
else:
|
|
130
139
|
# comportamiento determinista (compatibilidad previa)
|
|
@@ -181,13 +190,15 @@ def aplicar_glifo(G, n, glifo: str, *, window: Optional[int] = None) -> None:
|
|
|
181
190
|
# -------------------------
|
|
182
191
|
|
|
183
192
|
def _remesh_alpha_info(G):
|
|
184
|
-
"""Devuelve (alpha, source) con precedencia explícita
|
|
185
|
-
|
|
193
|
+
"""Devuelve (alpha, source) con precedencia explícita."""
|
|
194
|
+
hard = bool(G.graph.get("REMESH_ALPHA_HARD", DEFAULTS.get("REMESH_ALPHA_HARD", False)))
|
|
186
195
|
gf = G.graph.get("GLYPH_FACTORS", DEFAULTS["GLYPH_FACTORS"])
|
|
187
|
-
if "REMESH_alpha" in gf:
|
|
196
|
+
if not hard and "REMESH_alpha" in gf:
|
|
188
197
|
return float(gf["REMESH_alpha"]), "GLYPH_FACTORS"
|
|
189
198
|
if "REMESH_ALPHA" in G.graph:
|
|
190
199
|
return float(G.graph["REMESH_ALPHA"]), "G.graph"
|
|
200
|
+
if "REMESH_alpha" in gf:
|
|
201
|
+
return float(gf["REMESH_alpha"]), "GLYPH_FACTORS"
|
|
191
202
|
return float(DEFAULTS["REMESH_ALPHA"]), "DEFAULTS"
|
|
192
203
|
|
|
193
204
|
|
|
@@ -308,6 +319,12 @@ def aplicar_remesh_si_estabilizacion_global(G, pasos_estables_consecutivos: Opti
|
|
|
308
319
|
cooldown = int(G.graph.get("REMESH_COOLDOWN_VENTANA", DEFAULTS["REMESH_COOLDOWN_VENTANA"]))
|
|
309
320
|
if step_idx - last < cooldown:
|
|
310
321
|
return
|
|
322
|
+
t_now = float(G.graph.get("_t", 0.0))
|
|
323
|
+
last_ts = float(G.graph.get("_last_remesh_ts", -1e12))
|
|
324
|
+
cooldown_ts = float(G.graph.get("REMESH_COOLDOWN_TS", DEFAULTS.get("REMESH_COOLDOWN_TS", 0.0)))
|
|
325
|
+
if cooldown_ts > 0 and (t_now - last_ts) < cooldown_ts:
|
|
326
|
+
return
|
|
311
327
|
# 4) Aplicar y registrar
|
|
312
328
|
aplicar_remesh_red(G)
|
|
313
329
|
G.graph["_last_remesh_step"] = step_idx
|
|
330
|
+
G.graph["_last_remesh_ts"] = t_now
|
tnfr/presets.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from .program import seq, block, wait
|
|
2
|
+
from .program import seq, block, wait, ejemplo_canonico_basico
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
_PRESETS = {
|
|
@@ -15,6 +15,7 @@ _PRESETS = {
|
|
|
15
15
|
"R’A",
|
|
16
16
|
"SH’A",
|
|
17
17
|
),
|
|
18
|
+
"ejemplo_canonico": ejemplo_canonico_basico(),
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
|
tnfr/program.py
CHANGED
|
@@ -166,3 +166,11 @@ def target(nodes: Optional[Iterable[Node]] = None) -> TARGET:
|
|
|
166
166
|
|
|
167
167
|
def wait(steps: int = 1) -> WAIT:
|
|
168
168
|
return WAIT(steps=max(1, int(steps)))
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def ejemplo_canonico_basico() -> List[Token]:
|
|
172
|
+
"""Secuencia canónica de referencia.
|
|
173
|
+
|
|
174
|
+
SH’A → A’L → R’A → Z’HIR → NU’L → T’HOL
|
|
175
|
+
"""
|
|
176
|
+
return seq("SH’A", "A’L", "R’A", "Z’HIR", "NU’L", "T’HOL")
|
tnfr/trace.py
CHANGED
|
@@ -22,7 +22,7 @@ except Exception: # pragma: no cover
|
|
|
22
22
|
# -------------------------
|
|
23
23
|
DEFAULTS.setdefault("TRACE", {
|
|
24
24
|
"enabled": True,
|
|
25
|
-
"capture": ["gamma", "grammar", "selector", "
|
|
25
|
+
"capture": ["gamma", "grammar", "selector", "dnfr_weights", "si_weights", "callbacks", "thol_state", "sigma", "kuramoto", "glifo_counts"],
|
|
26
26
|
"history_key": "trace_meta",
|
|
27
27
|
})
|
|
28
28
|
|
|
@@ -70,10 +70,14 @@ def _trace_before(G, *args, **kwargs):
|
|
|
70
70
|
sel = G.graph.get("glyph_selector")
|
|
71
71
|
meta["selector"] = getattr(sel, "__name__", str(sel)) if sel else None
|
|
72
72
|
|
|
73
|
-
if "
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
if "dnfr_weights" in capture:
|
|
74
|
+
mix = G.graph.get("DNFR_WEIGHTS")
|
|
75
|
+
if isinstance(mix, dict):
|
|
76
|
+
meta["dnfr_weights"] = dict(mix)
|
|
77
|
+
|
|
78
|
+
if "si_weights" in capture:
|
|
79
|
+
meta["si_weights"] = dict(G.graph.get("_Si_weights", {}))
|
|
80
|
+
meta["si_sensitivity"] = dict(G.graph.get("_Si_sensitivity", {}))
|
|
77
81
|
|
|
78
82
|
if "callbacks" in capture:
|
|
79
83
|
# si el motor guarda los callbacks, exponer nombres por fase
|
|
@@ -134,7 +138,8 @@ def register_trace(G) -> None:
|
|
|
134
138
|
- gamma: especificación activa de Γi(R)
|
|
135
139
|
- grammar: configuración de gramática canónica
|
|
136
140
|
- selector: nombre del selector glífico
|
|
137
|
-
-
|
|
141
|
+
- dnfr_weights: mezcla ΔNFR declarada en el motor
|
|
142
|
+
- si_weights: pesos α/β/γ y sensibilidad de Si
|
|
138
143
|
- callbacks: callbacks registrados por fase (si están en G.graph['_callbacks'])
|
|
139
144
|
- thol_open_nodes: cuántos nodos tienen bloque T’HOL abierto
|
|
140
145
|
- kuramoto: (R, ψ) de la red
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tnfr
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.1.0
|
|
4
4
|
Summary: TNFR canónica: dinámica glífica modular sobre redes.
|
|
5
5
|
Author: fmg
|
|
6
6
|
License: MIT
|
|
@@ -59,6 +59,8 @@ Dynamic: license-file
|
|
|
59
59
|
|
|
60
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
61
|
|
|
62
|
+
* **Γ(R) coupling.** Optional network term added to the nodal equation, parameterized by global phase order `R` with gain `β` and threshold `R0` (see `DEFAULTS["GAMMA"]`).
|
|
63
|
+
|
|
62
64
|
* **Callbacks & observers.** The `Γ(R)` system lets you hook functions before/after each step and after re-mesh, enabling monitoring or external intervention.
|
|
63
65
|
|
|
64
66
|
---
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
tnfr/__init__.py,sha256=93fkcY9lteZyKkZuWJAB-hvDEE2q0ZYgk16qCBXe50A,1988
|
|
2
|
+
tnfr/cli.py,sha256=PVU95Iik_3bn-4UrLr8H7HHGme1X0qYnTuVv_AOp9FA,7484
|
|
3
|
+
tnfr/constants.py,sha256=EPFW7howB2IsyeGRc8hyzO3s6sgRkM78lWB7S3BJXq4,9147
|
|
4
|
+
tnfr/dynamics.py,sha256=SIf6cTj6_Wd6DRKJRYjE-iBuaxfDRpHu-j14wzlB-J0,25598
|
|
5
|
+
tnfr/gamma.py,sha256=Ccsr2yKA7f8VlVqKgckQpGAEISlBw8i3FBeNPxfS-AQ,3048
|
|
6
|
+
tnfr/grammar.py,sha256=vz5F0P3IfvA6HassRcoD327hBP5vCUw-xPSTsPmqwhQ,5363
|
|
7
|
+
tnfr/helpers.py,sha256=CPEBkmFY6S4DKBf91EqW5vW_jTokOzg3HojCx28KxTU,7327
|
|
8
|
+
tnfr/main.py,sha256=XqjI1YEdF-OqRzTMa5dYIxCig4qyAR-l1FPcyxpC8WY,1926
|
|
9
|
+
tnfr/metrics.py,sha256=XusywHo1-GgLqYy1Jz43Xc4zRsggrn_DwoaFWfAy82M,6640
|
|
10
|
+
tnfr/observers.py,sha256=PTw3hxk7KD-Yx_CvCIU09icuhyYD6uNU6SvF80UvP-Y,5354
|
|
11
|
+
tnfr/ontosim.py,sha256=9GfEtiLIdJOPJUTufcq_MssAA9J8AfChHU6HKb3DIJY,5628
|
|
12
|
+
tnfr/operators.py,sha256=QYsGf9R3lr8WdR0kanLqGYqZ-Lw-RWzjqKKTczgtyEw,13741
|
|
13
|
+
tnfr/presets.py,sha256=rN6kDnI59DfYCL0YuYQr0yEu8jZaCFezGBnIWn6HFn8,737
|
|
14
|
+
tnfr/program.py,sha256=eim7D8zsbbkGDWbODag-0VKG44jEYioX4Sl6KRwgVtw,6038
|
|
15
|
+
tnfr/scenarios.py,sha256=FgsXzA6ENRkalENnG5ZuBNFguEGaKZTM6y0quW5fJI4,808
|
|
16
|
+
tnfr/sense.py,sha256=1yVlphcs4o_uOQ51MDqW9dphydMiDNKv1Z1EdgbJgpU,6778
|
|
17
|
+
tnfr/trace.py,sha256=bY0GU5I5yvHKSDOUQ0m5ICaXScYHzCM5xOWqTO68VO0,5026
|
|
18
|
+
tnfr/types.py,sha256=xyFHp0PptEqPNUekAFH6DcAnyMx4bCQutMyFUXMd2sA,457
|
|
19
|
+
tnfr-4.1.0.dist-info/licenses/LICENSE.md,sha256=SRvvhXLrKtseuK6DARbuJffuXOXqAyk3wvF2n0t1SWA,1109
|
|
20
|
+
tnfr-4.1.0.dist-info/METADATA,sha256=cuR46do-ZJA5_OiZy4MNCwwbsEAi6DYD0HcRVTTYIz8,6075
|
|
21
|
+
tnfr-4.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
22
|
+
tnfr-4.1.0.dist-info/entry_points.txt,sha256=j4-QRHqeT2WnchHe_mvK7npGTLjlyfLpvRONFe9Z4MU,39
|
|
23
|
+
tnfr-4.1.0.dist-info/top_level.txt,sha256=Q2HJnvc5Rt2VHwVvyBTnNPT4SfmJWnCj7XUxxEvQa7c,5
|
|
24
|
+
tnfr-4.1.0.dist-info/RECORD,,
|
tnfr-4.0.0.dist-info/RECORD
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
tnfr/__init__.py,sha256=nN8P2Xm26kvJvqDhmRITnimew3SgBgw4fSUHPOAiYts,1880
|
|
2
|
-
tnfr/cli.py,sha256=fdzmIOVerg6J1j6cBuu7m2-nKFG_WqiNu-e2Wg8eECQ,5980
|
|
3
|
-
tnfr/constants.py,sha256=PUhps2gBXeh_rJIrCF9HpWRdi02sfRXvVp066AOgkyQ,8912
|
|
4
|
-
tnfr/dynamics.py,sha256=FeFv7h4m9e8qhbU9tIRss3K6P2uz4Hfmtbldf2AhfyQ,25286
|
|
5
|
-
tnfr/gamma.py,sha256=1hGqYTBYIYirgmSSSbvSuFWPVy_KGzPNH3eD7wmRUu0,2689
|
|
6
|
-
tnfr/grammar.py,sha256=X50-BzTzaYkFIi-ljcgB_XXC6c2srvqzDqOKa91nsuA,5090
|
|
7
|
-
tnfr/helpers.py,sha256=foJgwyC__6lQeOpPY02slFRggY3tCy1CxeFlEQcI-gY,7014
|
|
8
|
-
tnfr/main.py,sha256=XqjI1YEdF-OqRzTMa5dYIxCig4qyAR-l1FPcyxpC8WY,1926
|
|
9
|
-
tnfr/metrics.py,sha256=XusywHo1-GgLqYy1Jz43Xc4zRsggrn_DwoaFWfAy82M,6640
|
|
10
|
-
tnfr/observers.py,sha256=PTw3hxk7KD-Yx_CvCIU09icuhyYD6uNU6SvF80UvP-Y,5354
|
|
11
|
-
tnfr/ontosim.py,sha256=9GfEtiLIdJOPJUTufcq_MssAA9J8AfChHU6HKb3DIJY,5628
|
|
12
|
-
tnfr/operators.py,sha256=Q2QvtK2oQUuMsq_CPdfaK9eCrhniI1NaWEfwQH08zSk,12936
|
|
13
|
-
tnfr/presets.py,sha256=vaqgqz1pVU93hEOITQn-2CUGfMbN2n47A9GBFmjWRpo,661
|
|
14
|
-
tnfr/program.py,sha256=qGqj74qBqXILm-Yrw73__0uIlx2gJiSF9dDHxOGTbCs,5798
|
|
15
|
-
tnfr/scenarios.py,sha256=FgsXzA6ENRkalENnG5ZuBNFguEGaKZTM6y0quW5fJI4,808
|
|
16
|
-
tnfr/sense.py,sha256=1yVlphcs4o_uOQ51MDqW9dphydMiDNKv1Z1EdgbJgpU,6778
|
|
17
|
-
tnfr/trace.py,sha256=YtqSKJM2tCH5eLa22nObPLnYgsn4Bm6inipY0n_mwXY,4887
|
|
18
|
-
tnfr/types.py,sha256=xyFHp0PptEqPNUekAFH6DcAnyMx4bCQutMyFUXMd2sA,457
|
|
19
|
-
tnfr-4.0.0.dist-info/licenses/LICENSE.md,sha256=SRvvhXLrKtseuK6DARbuJffuXOXqAyk3wvF2n0t1SWA,1109
|
|
20
|
-
tnfr-4.0.0.dist-info/METADATA,sha256=xzcC2N34E82NmkgYly3jpfgzLVgI9fQLNnMiXLWk2RQ,5898
|
|
21
|
-
tnfr-4.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
22
|
-
tnfr-4.0.0.dist-info/entry_points.txt,sha256=j4-QRHqeT2WnchHe_mvK7npGTLjlyfLpvRONFe9Z4MU,39
|
|
23
|
-
tnfr-4.0.0.dist-info/top_level.txt,sha256=Q2HJnvc5Rt2VHwVvyBTnNPT4SfmJWnCj7XUxxEvQa7c,5
|
|
24
|
-
tnfr-4.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|