tnfr 4.5.1__py3-none-any.whl → 4.5.2__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.

Files changed (78) hide show
  1. tnfr/__init__.py +91 -90
  2. tnfr/alias.py +546 -0
  3. tnfr/cache.py +578 -0
  4. tnfr/callback_utils.py +388 -0
  5. tnfr/cli/__init__.py +75 -0
  6. tnfr/cli/arguments.py +177 -0
  7. tnfr/cli/execution.py +288 -0
  8. tnfr/cli/utils.py +36 -0
  9. tnfr/collections_utils.py +300 -0
  10. tnfr/config.py +19 -28
  11. tnfr/constants/__init__.py +174 -0
  12. tnfr/constants/core.py +159 -0
  13. tnfr/constants/init.py +31 -0
  14. tnfr/constants/metric.py +110 -0
  15. tnfr/constants_glyphs.py +98 -0
  16. tnfr/dynamics/__init__.py +658 -0
  17. tnfr/dynamics/dnfr.py +733 -0
  18. tnfr/dynamics/integrators.py +267 -0
  19. tnfr/dynamics/sampling.py +31 -0
  20. tnfr/execution.py +201 -0
  21. tnfr/flatten.py +283 -0
  22. tnfr/gamma.py +302 -88
  23. tnfr/glyph_history.py +290 -0
  24. tnfr/grammar.py +285 -96
  25. tnfr/graph_utils.py +84 -0
  26. tnfr/helpers/__init__.py +71 -0
  27. tnfr/helpers/numeric.py +87 -0
  28. tnfr/immutable.py +178 -0
  29. tnfr/import_utils.py +228 -0
  30. tnfr/initialization.py +197 -0
  31. tnfr/io.py +246 -0
  32. tnfr/json_utils.py +162 -0
  33. tnfr/locking.py +37 -0
  34. tnfr/logging_utils.py +116 -0
  35. tnfr/metrics/__init__.py +41 -0
  36. tnfr/metrics/coherence.py +829 -0
  37. tnfr/metrics/common.py +151 -0
  38. tnfr/metrics/core.py +101 -0
  39. tnfr/metrics/diagnosis.py +234 -0
  40. tnfr/metrics/export.py +137 -0
  41. tnfr/metrics/glyph_timing.py +189 -0
  42. tnfr/metrics/reporting.py +148 -0
  43. tnfr/metrics/sense_index.py +120 -0
  44. tnfr/metrics/trig.py +181 -0
  45. tnfr/metrics/trig_cache.py +109 -0
  46. tnfr/node.py +214 -159
  47. tnfr/observers.py +126 -136
  48. tnfr/ontosim.py +134 -134
  49. tnfr/operators/__init__.py +420 -0
  50. tnfr/operators/jitter.py +203 -0
  51. tnfr/operators/remesh.py +485 -0
  52. tnfr/presets.py +46 -14
  53. tnfr/rng.py +254 -0
  54. tnfr/selector.py +210 -0
  55. tnfr/sense.py +284 -131
  56. tnfr/structural.py +207 -79
  57. tnfr/tokens.py +60 -0
  58. tnfr/trace.py +329 -94
  59. tnfr/types.py +43 -17
  60. tnfr/validators.py +70 -24
  61. tnfr/value_utils.py +59 -0
  62. tnfr-4.5.2.dist-info/METADATA +379 -0
  63. tnfr-4.5.2.dist-info/RECORD +67 -0
  64. tnfr/cli.py +0 -322
  65. tnfr/constants.py +0 -277
  66. tnfr/dynamics.py +0 -814
  67. tnfr/helpers.py +0 -264
  68. tnfr/main.py +0 -47
  69. tnfr/metrics.py +0 -597
  70. tnfr/operators.py +0 -525
  71. tnfr/program.py +0 -176
  72. tnfr/scenarios.py +0 -34
  73. tnfr-4.5.1.dist-info/METADATA +0 -221
  74. tnfr-4.5.1.dist-info/RECORD +0 -28
  75. {tnfr-4.5.1.dist-info → tnfr-4.5.2.dist-info}/WHEEL +0 -0
  76. {tnfr-4.5.1.dist-info → tnfr-4.5.2.dist-info}/entry_points.txt +0 -0
  77. {tnfr-4.5.1.dist-info → tnfr-4.5.2.dist-info}/licenses/LICENSE.md +0 -0
  78. {tnfr-4.5.1.dist-info → tnfr-4.5.2.dist-info}/top_level.txt +0 -0
tnfr/cli.py DELETED
@@ -1,322 +0,0 @@
1
- from __future__ import annotations
2
- import argparse
3
- import json
4
- from typing import Any, Dict, List, Optional
5
-
6
- try: # pragma: no cover - opcional
7
- import yaml # type: ignore
8
- except Exception: # pragma: no cover - yaml es opcional
9
- yaml = None
10
-
11
- import networkx as nx
12
-
13
- from .constants import inject_defaults, DEFAULTS
14
- from .sense import register_sigma_callback, sigma_series, sigma_rose
15
- from .metrics import (
16
- register_metrics_callbacks,
17
- Tg_global,
18
- latency_series,
19
- glifogram_series,
20
- glyph_top,
21
- export_history,
22
- )
23
- from .trace import register_trace
24
- from .program import play, seq, block, wait, target
25
- from .dynamics import step, _update_history, default_glyph_selector, parametric_glyph_selector, validate_canon
26
- from .gamma import GAMMA_REGISTRY
27
- from .scenarios import build_graph
28
- from .presets import get_preset
29
- from .config import apply_config
30
-
31
-
32
- def _save_json(path: str, data: Any) -> None:
33
- with open(path, "w", encoding="utf-8") as f:
34
- json.dump(data, f, ensure_ascii=False, indent=2)
35
-
36
-
37
- def _str2bool(s: str) -> bool:
38
- s = s.lower()
39
- if s in {"true", "1", "yes", "y"}:
40
- return True
41
- if s in {"false", "0", "no", "n"}:
42
- return False
43
- raise argparse.ArgumentTypeError("expected true/false")
44
-
45
-
46
- def _args_to_dict(args: argparse.Namespace, prefix: str) -> Dict[str, Any]:
47
- out: Dict[str, Any] = {}
48
- pref = prefix.replace(".", "_")
49
- for k, v in vars(args).items():
50
- if k.startswith(pref) and v is not None:
51
- out[k[len(pref):]] = v
52
- return out
53
-
54
-
55
- def _load_sequence(path: str) -> List[Any]:
56
- with open(path, "r", encoding="utf-8") as f:
57
- text = f.read()
58
- if path.endswith(".yaml") or path.endswith(".yml"):
59
- if not yaml:
60
- raise RuntimeError("pyyaml no está instalado, usa JSON o instala pyyaml")
61
- data = yaml.safe_load(text)
62
- else:
63
- data = json.loads(text)
64
-
65
- def parse_token(tok: Any):
66
- if isinstance(tok, str):
67
- return tok
68
- if isinstance(tok, dict):
69
- if "WAIT" in tok:
70
- return wait(int(tok["WAIT"]))
71
- if "TARGET" in tok:
72
- return target(tok["TARGET"])
73
- if "THOL" in tok:
74
- spec = tok["THOL"] or {}
75
- b = [_parse_inner(x) for x in spec.get("body", [])]
76
- return block(*b, repeat=int(spec.get("repeat", 1)), close=spec.get("close"))
77
- raise ValueError(f"Token inválido: {tok}")
78
-
79
- def _parse_inner(x: Any):
80
- return parse_token(x)
81
-
82
- return [parse_token(t) for t in data]
83
-
84
-
85
- def _attach_callbacks(G: nx.Graph) -> None:
86
- inject_defaults(G, DEFAULTS)
87
- register_sigma_callback(G)
88
- register_metrics_callbacks(G)
89
- register_trace(G)
90
- _update_history(G)
91
-
92
-
93
- def cmd_run(args: argparse.Namespace) -> int:
94
- G = build_graph(n=args.nodes, topology=args.topology, seed=args.seed)
95
- if getattr(args, "config", None):
96
- apply_config(G, args.config)
97
- _attach_callbacks(G)
98
- validate_canon(G)
99
- if args.dt is not None:
100
- G.graph["DT"] = float(args.dt)
101
- if args.integrator is not None:
102
- G.graph["INTEGRATOR_METHOD"] = str(args.integrator)
103
- if getattr(args, "remesh_mode", None):
104
- G.graph["REMESH_MODE"] = str(args.remesh_mode)
105
- gcanon = dict(DEFAULTS["GRAMMAR_CANON"])
106
- gcanon.update(_args_to_dict(args, prefix="grammar."))
107
- if hasattr(args, "grammar_canon") and args.grammar_canon is not None:
108
- gcanon["enabled"] = bool(args.grammar_canon)
109
- G.graph.setdefault("GRAMMAR_CANON", {}).update(gcanon)
110
- if args.glyph_hysteresis_window is not None:
111
- G.graph["GLYPH_HYSTERESIS_WINDOW"] = int(args.glyph_hysteresis_window)
112
- G.graph["glyph_selector"] = default_glyph_selector if args.selector == "basic" else parametric_glyph_selector
113
- G.graph["GAMMA"] = {
114
- "type": args.gamma_type,
115
- "beta": args.gamma_beta,
116
- "R0": args.gamma_R0,
117
- }
118
-
119
- if args.preset:
120
- program = get_preset(args.preset)
121
- play(G, program)
122
- else:
123
- steps = int(args.steps or 100)
124
- for _ in range(steps):
125
- step(G)
126
-
127
- if args.save_history:
128
- _save_json(args.save_history, G.graph.get("history", {}))
129
- if args.export_history_base:
130
- export_history(G, args.export_history_base, fmt=args.export_format)
131
-
132
- # Resúmenes rápidos (si están activados)
133
- if G.graph.get("COHERENCE", DEFAULTS["COHERENCE"]).get("enabled", True):
134
- Wstats = G.graph.get("history", {}).get(
135
- G.graph.get("COHERENCE", DEFAULTS["COHERENCE"]).get("stats_history_key", "W_stats"), []
136
- )
137
- if Wstats:
138
- print("[COHERENCE] último paso:", Wstats[-1])
139
- if G.graph.get("DIAGNOSIS", DEFAULTS["DIAGNOSIS"]).get("enabled", True):
140
- last_diag = G.graph.get("history", {}).get(
141
- G.graph.get("DIAGNOSIS", DEFAULTS["DIAGNOSIS"]).get("history_key", "nodal_diag"), []
142
- )
143
- if last_diag:
144
- sample = list(last_diag[-1].values())[:3]
145
- print("[DIAGNOSIS] ejemplo:", sample)
146
-
147
- if args.summary:
148
- tg = Tg_global(G, normalize=True)
149
- lat = latency_series(G)
150
- print("Top operadores por Tg:", glyph_top(G, k=5))
151
- if lat["value"]:
152
- print("Latencia media:", sum(lat["value"]) / max(1, len(lat["value"])) )
153
- return 0
154
-
155
-
156
- def cmd_sequence(args: argparse.Namespace) -> int:
157
- G = build_graph(n=args.nodes, topology=args.topology, seed=args.seed)
158
- if getattr(args, "config", None):
159
- apply_config(G, args.config)
160
- _attach_callbacks(G)
161
- validate_canon(G)
162
- if args.dt is not None:
163
- G.graph["DT"] = float(args.dt)
164
- if args.integrator is not None:
165
- G.graph["INTEGRATOR_METHOD"] = str(args.integrator)
166
- if getattr(args, "remesh_mode", None):
167
- G.graph["REMESH_MODE"] = str(args.remesh_mode)
168
- gcanon = dict(DEFAULTS["GRAMMAR_CANON"])
169
- gcanon.update(_args_to_dict(args, prefix="grammar."))
170
- if hasattr(args, "grammar_canon") and args.grammar_canon is not None:
171
- gcanon["enabled"] = bool(args.grammar_canon)
172
- G.graph.setdefault("GRAMMAR_CANON", {}).update(gcanon)
173
- if args.glyph_hysteresis_window is not None:
174
- G.graph["GLYPH_HYSTERESIS_WINDOW"] = int(args.glyph_hysteresis_window)
175
- G.graph["glyph_selector"] = default_glyph_selector if args.selector == "basic" else parametric_glyph_selector
176
- G.graph["GAMMA"] = {
177
- "type": args.gamma_type,
178
- "beta": args.gamma_beta,
179
- "R0": args.gamma_R0,
180
- }
181
-
182
- if args.preset:
183
- program = get_preset(args.preset)
184
- elif args.sequence_file:
185
- program = _load_sequence(args.sequence_file)
186
- else:
187
- program = seq("A’L", "E’N", "I’L", block("O’Z", "Z’HIR", "I’L", repeat=1), "R’A", "SH’A")
188
-
189
- play(G, program)
190
-
191
- if args.save_history:
192
- _save_json(args.save_history, G.graph.get("history", {}))
193
- if args.export_history_base:
194
- export_history(G, args.export_history_base, fmt=args.export_format)
195
- return 0
196
-
197
-
198
- def cmd_metrics(args: argparse.Namespace) -> int:
199
- G = build_graph(n=args.nodes, topology=args.topology, seed=args.seed)
200
- if getattr(args, "config", None):
201
- apply_config(G, args.config)
202
- _attach_callbacks(G)
203
- validate_canon(G)
204
- if args.dt is not None:
205
- G.graph["DT"] = float(args.dt)
206
- if args.integrator is not None:
207
- G.graph["INTEGRATOR_METHOD"] = str(args.integrator)
208
- if getattr(args, "remesh_mode", None):
209
- G.graph["REMESH_MODE"] = str(args.remesh_mode)
210
- G.graph.setdefault("GRAMMAR_CANON", DEFAULTS["GRAMMAR_CANON"]).update({"enabled": bool(args.grammar_canon)})
211
- G.graph["glyph_selector"] = default_glyph_selector if args.selector == "basic" else parametric_glyph_selector
212
- G.graph["GAMMA"] = {
213
- "type": args.gamma_type,
214
- "beta": args.gamma_beta,
215
- "R0": args.gamma_R0,
216
- }
217
- for _ in range(int(args.steps or 200)):
218
- step(G)
219
-
220
- tg = Tg_global(G, normalize=True)
221
- lat = latency_series(G)
222
- rose = sigma_rose(G)
223
- glifo = glifogram_series(G)
224
-
225
- out = {
226
- "Tg_global": tg,
227
- "latency_mean": (sum(lat["value"]) / max(1, len(lat["value"])) ) if lat["value"] else 0.0,
228
- "rose": rose,
229
- "glifogram": {k: v[:10] for k, v in glifo.items()},
230
- }
231
- if args.save:
232
- _save_json(args.save, out)
233
- else:
234
- print(json.dumps(out, ensure_ascii=False, indent=2))
235
- return 0
236
-
237
-
238
- def main(argv: Optional[List[str]] = None) -> int:
239
- p = argparse.ArgumentParser(prog="tnfr")
240
- sub = p.add_subparsers(dest="cmd")
241
-
242
- p_run = sub.add_parser("run", help="Correr escenario libre o preset y opcionalmente exportar history")
243
- p_run.add_argument("--nodes", type=int, default=24)
244
- p_run.add_argument("--topology", choices=["ring", "complete", "erdos"], default="ring")
245
- p_run.add_argument("--steps", type=int, default=200)
246
- p_run.add_argument("--seed", type=int, default=1)
247
- p_run.add_argument("--preset", type=str, default=None)
248
- p_run.add_argument("--config", type=str, default=None)
249
- p_run.add_argument("--dt", type=float, default=None)
250
- p_run.add_argument("--integrator", choices=["euler", "rk4"], default=None)
251
- p_run.add_argument("--save-history", dest="save_history", type=str, default=None)
252
- p_run.add_argument("--export-history-base", dest="export_history_base", type=str, default=None)
253
- p_run.add_argument("--export-format", dest="export_format", choices=["csv", "json"], default="json")
254
- p_run.add_argument("--summary", action="store_true")
255
- p_run.add_argument("--remesh-mode", choices=["knn", "mst", "community"], default=None)
256
- p_run.add_argument("--no-canon", dest="grammar_canon", action="store_false", default=True, help="Desactiva gramática canónica")
257
- p_run.add_argument("--grammar.enabled", dest="grammar_enabled", type=_str2bool, default=None)
258
- p_run.add_argument("--grammar.zhir_requires_oz_window", dest="grammar_zhir_requires_oz_window", type=int, default=None)
259
- p_run.add_argument("--grammar.zhir_dnfr_min", dest="grammar_zhir_dnfr_min", type=float, default=None)
260
- p_run.add_argument("--grammar.thol_min_len", dest="grammar_thol_min_len", type=int, default=None)
261
- p_run.add_argument("--grammar.thol_max_len", dest="grammar_thol_max_len", type=int, default=None)
262
- p_run.add_argument("--grammar.thol_close_dnfr", dest="grammar_thol_close_dnfr", type=float, default=None)
263
- p_run.add_argument("--grammar.si_high", dest="grammar_si_high", type=float, default=None)
264
- p_run.add_argument("--glyph.hysteresis_window", dest="glyph_hysteresis_window", type=int, default=None)
265
- p_run.add_argument("--selector", choices=["basic", "param"], default="basic")
266
- p_run.add_argument("--gamma-type", choices=list(GAMMA_REGISTRY.keys()), default="none")
267
- p_run.add_argument("--gamma-beta", type=float, default=0.0)
268
- p_run.add_argument("--gamma-R0", type=float, default=0.0)
269
- p_run.set_defaults(func=cmd_run)
270
-
271
- p_seq = sub.add_parser("sequence", help="Ejecutar una secuencia (preset o YAML/JSON)")
272
- p_seq.add_argument("--nodes", type=int, default=24)
273
- p_seq.add_argument("--topology", choices=["ring", "complete", "erdos"], default="ring")
274
- p_seq.add_argument("--seed", type=int, default=1)
275
- p_seq.add_argument("--preset", type=str, default=None)
276
- p_seq.add_argument("--sequence-file", type=str, default=None)
277
- p_seq.add_argument("--config", type=str, default=None)
278
- p_seq.add_argument("--dt", type=float, default=None)
279
- p_seq.add_argument("--integrator", choices=["euler", "rk4"], default=None)
280
- p_seq.add_argument("--save-history", dest="save_history", type=str, default=None)
281
- p_seq.add_argument("--export-history-base", dest="export_history_base", type=str, default=None)
282
- p_seq.add_argument("--export-format", dest="export_format", choices=["csv", "json"], default="json")
283
- p_seq.add_argument("--remesh-mode", choices=["knn", "mst", "community"], default=None)
284
- p_seq.add_argument("--gamma-type", choices=list(GAMMA_REGISTRY.keys()), default="none")
285
- p_seq.add_argument("--gamma-beta", type=float, default=0.0)
286
- p_seq.add_argument("--gamma-R0", type=float, default=0.0)
287
- p_seq.add_argument("--grammar.enabled", dest="grammar_enabled", type=_str2bool, default=None)
288
- p_seq.add_argument("--grammar.zhir_requires_oz_window", dest="grammar_zhir_requires_oz_window", type=int, default=None)
289
- p_seq.add_argument("--grammar.zhir_dnfr_min", dest="grammar_zhir_dnfr_min", type=float, default=None)
290
- p_seq.add_argument("--grammar.thol_min_len", dest="grammar_thol_min_len", type=int, default=None)
291
- p_seq.add_argument("--grammar.thol_max_len", dest="grammar_thol_max_len", type=int, default=None)
292
- p_seq.add_argument("--grammar.thol_close_dnfr", dest="grammar_thol_close_dnfr", type=float, default=None)
293
- p_seq.add_argument("--grammar.si_high", dest="grammar_si_high", type=float, default=None)
294
- p_seq.add_argument("--glyph.hysteresis_window", dest="glyph_hysteresis_window", type=int, default=None)
295
- p_seq.set_defaults(func=cmd_sequence)
296
-
297
- p_met = sub.add_parser("metrics", help="Correr breve y volcar métricas clave")
298
- p_met.add_argument("--nodes", type=int, default=24)
299
- p_met.add_argument("--topology", choices=["ring", "complete", "erdos"], default="ring")
300
- p_met.add_argument("--steps", type=int, default=300)
301
- p_met.add_argument("--seed", type=int, default=1)
302
- p_met.add_argument("--dt", type=float, default=None)
303
- p_met.add_argument("--integrator", choices=["euler", "rk4"], default=None)
304
- p_met.add_argument("--no-canon", dest="grammar_canon", action="store_false", default=True, help="Desactiva gramática canónica")
305
- p_met.add_argument("--selector", choices=["basic", "param"], default="basic")
306
- p_met.add_argument("--gamma-type", choices=list(GAMMA_REGISTRY.keys()), default="none")
307
- p_met.add_argument("--gamma-beta", type=float, default=0.0)
308
- p_met.add_argument("--gamma-R0", type=float, default=0.0)
309
- p_met.add_argument("--remesh-mode", choices=["knn", "mst", "community"], default=None)
310
- p_met.add_argument("--save", type=str, default=None)
311
- p_met.add_argument("--config", type=str, default=None)
312
- p_met.set_defaults(func=cmd_metrics)
313
-
314
- args = p.parse_args(argv)
315
- if not hasattr(args, "func"):
316
- p.print_help()
317
- return 1
318
- return int(args.func(args))
319
-
320
-
321
- if __name__ == "__main__": # pragma: no cover
322
- raise SystemExit(main())
tnfr/constants.py DELETED
@@ -1,277 +0,0 @@
1
- """
2
- constants.py — TNFR canónica
3
-
4
- Centraliza parámetros por defecto y nombres que usan el resto de módulos.
5
- Provee utilidades para inyectarlos en G.graph.
6
- """
7
- from __future__ import annotations
8
- from typing import Dict, Any
9
-
10
- # -------------------------
11
- # Parámetros canónicos
12
- # -------------------------
13
- DEFAULTS: Dict[str, Any] = {
14
- # Discretización
15
- "DT": 1.0,
16
- "INTEGRATOR_METHOD": "euler",
17
- "DT_MIN": 0.1,
18
-
19
- # Rango de EPI (estructura primaria)
20
- "EPI_MIN": -1.0,
21
- "EPI_MAX": 1.0,
22
- # Alias histórico usado por algunos proyectos
23
- "EPI_MAX_GLOBAL": 1.0,
24
-
25
- # Rango de frecuencia estructural νf
26
- "VF_MIN": 0.0,
27
- "VF_MAX": 1.0,
28
-
29
- # Envolvente de fase
30
- "THETA_WRAP": True,
31
-
32
- # --- Inicialización (evitar simetrías) ---
33
- "INIT_RANDOM_PHASE": True, # si True, θ ~ U[-π, π]
34
- "INIT_THETA_MIN": -3.141592653589793,
35
- "INIT_THETA_MAX": 3.141592653589793,
36
-
37
- "INIT_VF_MODE": "uniform", # "uniform" | "normal"
38
- # para uniform:
39
- "INIT_VF_MIN": None, # si None → usa VF_MIN
40
- "INIT_VF_MAX": None, # si None → usa VF_MAX
41
- # para normal:
42
- "INIT_VF_MEAN": 0.5,
43
- "INIT_VF_STD": 0.15,
44
- # clamp de νf a límites canónicos
45
- "INIT_VF_CLAMP_TO_LIMITS": True,
46
-
47
-
48
- # Mezcla para ΔNFR (campo nodal)
49
- # phase: dispersión de fase local; epi: gradiente de EPI; vf: desajuste de νf;
50
- # topo: término topológico (p. ej., centralidad). Pesos se normalizan.
51
- "DNFR_WEIGHTS": {"phase": 0.34, "epi": 0.33, "vf": 0.33, "topo": 0.0},
52
-
53
- # Índice de sentido Si = α·νf_norm + β·(1 - disp_fase) + γ·(1 - |ΔNFR|/max)
54
- "SI_WEIGHTS": {"alpha": 0.34, "beta": 0.33, "gamma": 0.33},
55
-
56
- # Coordinación de fase (U’M) global/vecinal por paso
57
- "PHASE_K_GLOBAL": 0.05,
58
- "PHASE_K_LOCAL": 0.15,
59
-
60
- # Coordinación de fase adaptativa (kG/kL dinámicos por estado)
61
- "PHASE_ADAPT": {
62
- "enabled": True, # activar adaptación automática si no se pasan fuerzas explícitas a la función
63
- "R_hi": 0.90, # sincronía alta (Kuramoto R) => estado ESTABLE
64
- "R_lo": 0.60, # sincronía baja => estado DISONANTE
65
- "disr_hi": 0.50, # carga glífica disruptiva alta => DISONANTE
66
- "disr_lo": 0.25, # carga glífica disruptiva baja + R alta => ESTABLE
67
- "kG_min": 0.01, "kG_max": 0.20,
68
- "kL_min": 0.05, "kL_max": 0.25,
69
- "up": 0.10, # velocidad de subida hacia el objetivo
70
- "down": 0.07 # velocidad de bajada hacia el objetivo
71
- },
72
-
73
- "STOP_EARLY": {"enabled": False, "window": 25, "fraction": 0.90},
74
-
75
- # Criterios de estabilidad (para activar RE’MESH de red)
76
- "EPS_DNFR_STABLE": 1e-3,
77
- "EPS_DEPI_STABLE": 1e-3,
78
- "FRACTION_STABLE_REMESH": 0.80, # fracción de nodos estables requerida
79
- "REMESH_COOLDOWN_VENTANA": 20, # pasos mínimos entre RE’MESH
80
- "REMESH_COOLDOWN_TS": 0.0, # cooldown adicional por tiempo simulado
81
- # Gating adicional basado en observadores (conmutador + ventana)
82
- "REMESH_REQUIRE_STABILITY": True, # si True, exige ventana de estabilidad multi-métrica
83
- "REMESH_STABILITY_WINDOW": 25, # tamaño de ventana para evaluar estabilidad
84
- "REMESH_MIN_PHASE_SYNC": 0.85, # media mínima de sincronía de fase en ventana
85
- "REMESH_MAX_GLYPH_DISR": 0.35, # media máxima de carga glífica disruptiva en ventana
86
- "REMESH_MIN_SIGMA_MAG": 0.50, # magnitud mínima de σ en ventana
87
- "REMESH_MIN_KURAMOTO_R": 0.80, # R de Kuramoto mínimo en ventana
88
- "REMESH_MIN_SI_HI_FRAC": 0.50, # fracción mínima de nodos con Si alto
89
- "REMESH_LOG_EVENTS": True, # guarda eventos y metadatos del RE’MESH
90
- "REMESH_MODE": "knn", # modo de remallado topológico
91
- "REMESH_COMMUNITY_K": 2, # conexiones por comunidad
92
-
93
- # RE’MESH: memoria τ y mezcla α (global/local)
94
- "REMESH_TAU": 8, # compatibilidad: tau global por defecto
95
- "REMESH_TAU_GLOBAL": 8, # pasos hacia atrás (escala global)
96
- "REMESH_TAU_LOCAL": 4, # pasos hacia atrás (escala local)
97
- "REMESH_ALPHA": 0.5, # mezcla con pasado
98
- "REMESH_ALPHA_HARD": False, # si True ignora GLYPH_FACTORS['REMESH_alpha']
99
-
100
- # Soporte y norma de la EPI
101
- "EPI_SUPPORT_THR": 0.05, # umbral para Supp(EPI)
102
-
103
- # U'M — compatibilidad mínima para crear/reforzar enlaces funcionales
104
- "UM_COMPAT_THRESHOLD": 0.75,
105
-
106
- # Histéresis glífica
107
- "GLYPH_HYSTERESIS_WINDOW": 7,
108
-
109
- # Lags máximos sin emisión (A’L) y recepción (E’N)
110
- "AL_MAX_LAG": 5,
111
- "EN_MAX_LAG": 3,
112
-
113
- # Margen de histéresis del selector (cuánto "aguanta" sin cambiar glifo si está cerca de un umbral)
114
- "GLYPH_SELECTOR_MARGIN": 0.05,
115
-
116
- # Ventana para estimar la carga glífica en history/plots
117
- "GLYPH_LOAD_WINDOW": 50,
118
-
119
- # Tamaño de ventana para coherencia promedio W̄
120
- "WBAR_WINDOW": 25,
121
-
122
- # Adaptación de frecuencia estructural por coherencia
123
- "VF_ADAPT_TAU": 5, # pasos estables antes de ajustar νf
124
- "VF_ADAPT_MU": 0.1, # velocidad de ajuste hacia la media vecinal
125
-
126
- # Factores suaves por glifo (operadores)
127
- "GLYPH_FACTORS": {
128
- "AL_boost": 0.05, # A’L — pequeña emisión
129
- "EN_mix": 0.25, # E’N — mezcla con vecindad
130
- "IL_dnfr_factor": 0.7, # I’L — reduce ΔNFR
131
- "OZ_dnfr_factor": 1.3, # O’Z — aumenta ΔNFR
132
- "UM_theta_push": 0.25, # U’M — empuje adicional de fase local
133
- "RA_epi_diff": 0.15, # R’A — difusión EPI
134
- "SHA_vf_factor": 0.85, # SH’A — baja νf
135
- "VAL_scale": 1.15, # VA’L — expande EPI
136
- "NUL_scale": 0.85, # NU’L — contrae EPI
137
- "THOL_accel": 0.10, # T’HOL — acelera (seg. deriv.) si hay umbral
138
- "ZHIR_theta_shift": 1.57079632679, # Z’HIR — desplazamiento ~π/2
139
- "NAV_jitter": 0.05, # NA’V — pequeña inestabilidad creativa
140
- "NAV_eta": 0.5, # NA’V — peso de convergencia hacia νf
141
- "REMESH_alpha": 0.5, # RE’MESH — mezcla si no se usa REMESH_ALPHA
142
- },
143
-
144
- # Umbrales para el selector glífico por defecto
145
- "GLYPH_THRESHOLDS": {"hi": 0.66, "lo": 0.33, "dnfr": 1e-3},
146
-
147
- # Comportamiento NA’V
148
- "NAV_RANDOM": True, # si True, usa jitter aleatorio en [-j, j]; si False, jitter determinista por signo
149
- "NAV_STRICT": False, # si True, fuerza ΔNFR ← νf (sin mezcla)
150
- "RANDOM_SEED": 0, # semilla base para reproducibilidad del jitter
151
-
152
- # Modo ruido para O’Z
153
- "OZ_NOISE_MODE": False, # si True, añade ruido aditivo en ΔNFR
154
- "OZ_SIGMA": 0.1, # amplitud del ruido uniforme [-σ, σ]
155
-
156
- # Gramática glífica (suave): evita repetir ciertos glifos salvo que el campo lo exija
157
- "GRAMMAR": {
158
- "window": 3, # cuántos pasos recientes miramos por nodo
159
- "avoid_repeats": ["Z’HIR", "O’Z", "T’HOL"],
160
- "force_dnfr": 0.60, # si |ΔNFR|_norm ≥ este valor, se permite repetir
161
- "force_accel": 0.60, # o si |accel|_norm ≥ este valor
162
- "fallbacks": { # a qué glifo caer si se bloquea el candidato
163
- "Z’HIR": "NA’V",
164
- "O’Z": "Z’HIR",
165
- "T’HOL": "NA’V"
166
- }
167
- },
168
-
169
- # --- Selector multiobjetivo ---
170
- # Ponderaciones (se reescalan a 1 automáticamente)
171
- "SELECTOR_WEIGHTS": {"w_si": 0.5, "w_dnfr": 0.3, "w_accel": 0.2},
172
- # Umbrales hi/lo para decidir categorías (trabajan con |ΔNFR|_norm y |accel|_norm)
173
- "SELECTOR_THRESHOLDS": {
174
- "si_hi": 0.66, "si_lo": 0.33,
175
- "dnfr_hi": 0.50, "dnfr_lo": 0.10,
176
- "accel_hi": 0.50, "accel_lo": 0.10
177
- },
178
- # Callbacks Γ(R)
179
- "GAMMA": {
180
- "type": "none", # 'none' | 'kuramoto_linear' | 'kuramoto_bandpass'
181
- "beta": 0.0,
182
- "R0": 0.0,
183
- },
184
- "CALLBACKS_STRICT": False, # si True, un error en callback detiene; si False, se loguea y continúa
185
- "VALIDATORS_STRICT": False, # si True, alerta si se clampa fuera de rango
186
- }
187
-
188
- # Gramática glífica canónica
189
- DEFAULTS.setdefault("GRAMMAR_CANON", {
190
- "enabled": True, # activar la gramática canónica
191
- "zhir_requires_oz_window": 3, # cuántos pasos atrás buscamos O’Z
192
- "zhir_dnfr_min": 0.05, # si |ΔNFR|_norm < este valor, no permitimos Z’HIR sin O’Z
193
- "thol_min_len": 2,
194
- "thol_max_len": 6,
195
- "thol_close_dnfr": 0.15, # si el campo calma, cerramos con SH’A/NU’L
196
- "si_high": 0.66, # umbral para elegir NU’L vs SH’A al cerrar
197
- })
198
-
199
- # --- Coherencia (W) ---
200
- DEFAULTS.setdefault("COHERENCE", {
201
- "enabled": True,
202
- "scope": "neighbors", # "neighbors" | "all"
203
- "weights": {
204
- "phase": 0.34,
205
- "epi": 0.33,
206
- "vf": 0.20,
207
- "si": 0.13,
208
- },
209
- "self_on_diag": True, # W_ii = 1.0
210
- "store_mode": "sparse", # "sparse" | "dense"
211
- "threshold": 0.0,
212
- "history_key": "W_sparse",
213
- "Wi_history_key": "W_i",
214
- "stats_history_key": "W_stats",
215
- })
216
-
217
- # --- Diagnóstico nodal ---
218
- DEFAULTS.setdefault("DIAGNOSIS", {
219
- "enabled": True,
220
- "window": 16,
221
- "history_key": "nodal_diag",
222
- "stable": {"Rloc_hi": 0.80, "dnfr_lo": 0.20, "persist": 3},
223
- "dissonance": {"Rloc_lo": 0.40, "dnfr_hi": 0.50, "persist": 3},
224
- "transition": {"persist": 2},
225
- "compute_symmetry": True,
226
- "include_typology": False,
227
- "advice": {
228
- "stable": ["Coherencia", "Acoplamiento", "Resonancia"],
229
- "transition": ["Transición", "Resonancia", "Autoorganización"],
230
- "dissonant": ["Silencio", "Contracción", "Mutación"],
231
- },
232
- })
233
-
234
-
235
- # -------------------------
236
- # Utilidades
237
- # -------------------------
238
-
239
- def attach_defaults(G, override: bool = False) -> None:
240
- """Escribe DEFAULTS en G.graph (sin sobreescribir si override=False)."""
241
- inject_defaults(G, DEFAULTS, override=override)
242
-
243
-
244
- def inject_defaults(G, defaults: Dict[str, Any] = DEFAULTS, override: bool = False) -> None:
245
- """Alias de conveniencia para inyectar ``DEFAULTS`` en ``G.graph``.
246
-
247
- Permite pasar un diccionario de *defaults* alternativo y mantiene la
248
- semántica de ``attach_defaults`` existente. Si ``override`` es ``True`` se
249
- sobreescriben valores ya presentes.
250
- """
251
- G.graph.setdefault("_tnfr_defaults_attached", False)
252
- for k, v in defaults.items():
253
- if override or k not in G.graph:
254
- G.graph[k] = v
255
- G.graph["_tnfr_defaults_attached"] = True
256
-
257
-
258
- def merge_overrides(G, **overrides) -> None:
259
- """Aplica cambios puntuales a G.graph.
260
- Útil para ajustar pesos sin tocar DEFAULTS globales.
261
- """
262
- for k, v in overrides.items():
263
- G.graph[k] = v
264
-
265
-
266
- # Alias exportados por conveniencia (evita imports circulares)
267
- ALIAS_VF = ("νf", "nu_f", "nu-f", "nu", "freq", "frequency")
268
- ALIAS_THETA = ("θ", "theta", "fase", "phi", "phase")
269
- ALIAS_DNFR = ("ΔNFR", "delta_nfr", "dnfr")
270
- ALIAS_EPI = ("EPI", "psi", "PSI", "value")
271
- ALIAS_EPI_KIND = ("EPI_kind", "epi_kind", "source_glifo")
272
- ALIAS_SI = ("Si", "sense_index", "S_i", "sense", "meaning_index")
273
- ALIAS_dEPI = ("dEPI_dt", "dpsi_dt", "dEPI", "velocity")
274
- ALIAS_D2EPI = ("d2EPI_dt2", "d2psi_dt2", "d2EPI", "accel")
275
- ALIAS_dVF = ("dνf_dt", "dvf_dt", "dnu_dt", "dvf")
276
- ALIAS_D2VF = ("d2νf_dt2", "d2vf_dt2", "d2nu_dt2", "B")
277
- ALIAS_dSI = ("δSi", "delta_Si", "dSi")