tnfr 4.1.0__py3-none-any.whl → 4.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 +34 -4
- tnfr/cli.py +138 -9
- tnfr/config.py +41 -0
- tnfr/constants.py +102 -41
- tnfr/dynamics.py +255 -49
- tnfr/gamma.py +35 -8
- tnfr/helpers.py +50 -17
- tnfr/metrics.py +416 -30
- tnfr/node.py +202 -0
- tnfr/operators.py +341 -146
- tnfr/presets.py +3 -0
- tnfr/scenarios.py +9 -3
- tnfr/sense.py +6 -21
- tnfr/structural.py +201 -0
- tnfr/trace.py +4 -20
- tnfr/types.py +2 -1
- tnfr/validators.py +38 -0
- {tnfr-4.1.0.dist-info → tnfr-4.5.0.dist-info}/METADATA +10 -4
- tnfr-4.5.0.dist-info/RECORD +28 -0
- tnfr-4.1.0.dist-info/RECORD +0 -24
- {tnfr-4.1.0.dist-info → tnfr-4.5.0.dist-info}/WHEEL +0 -0
- {tnfr-4.1.0.dist-info → tnfr-4.5.0.dist-info}/entry_points.txt +0 -0
- {tnfr-4.1.0.dist-info → tnfr-4.5.0.dist-info}/licenses/LICENSE.md +0 -0
- {tnfr-4.1.0.dist-info → tnfr-4.5.0.dist-info}/top_level.txt +0 -0
tnfr/__init__.py
CHANGED
|
@@ -8,10 +8,10 @@ Ecuación nodal:
|
|
|
8
8
|
∂EPI/∂t = νf · ΔNFR(t)
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
__version__ = "4.
|
|
11
|
+
__version__ = "4.5.0"
|
|
12
12
|
|
|
13
13
|
# Re-exports de la API pública
|
|
14
|
-
from .dynamics import step, run, set_delta_nfr_hook
|
|
14
|
+
from .dynamics import step, run, set_delta_nfr_hook, validate_canon
|
|
15
15
|
from .ontosim import preparar_red
|
|
16
16
|
from .observers import attach_standard_observer, coherencia_global, orden_kuramoto
|
|
17
17
|
from .gamma import GAMMA_REGISTRY, eval_gamma, kuramoto_R_psi
|
|
@@ -26,19 +26,40 @@ from .metrics import (
|
|
|
26
26
|
register_metrics_callbacks,
|
|
27
27
|
Tg_global, Tg_by_node,
|
|
28
28
|
latency_series, glifogram_series,
|
|
29
|
-
glyph_top, glyph_dwell_stats,
|
|
29
|
+
glyph_top, glyph_dwell_stats, export_history,
|
|
30
30
|
)
|
|
31
|
+
from .operators import aplicar_remesh_red_topologico
|
|
31
32
|
from .trace import register_trace
|
|
32
33
|
from .program import play, seq, block, target, wait, THOL, TARGET, WAIT, ejemplo_canonico_basico
|
|
33
34
|
from .cli import main as cli_main
|
|
34
35
|
from .scenarios import build_graph
|
|
35
36
|
from .presets import get_preset
|
|
36
37
|
from .types import NodeState
|
|
38
|
+
from .structural import (
|
|
39
|
+
create_nfr,
|
|
40
|
+
Operador,
|
|
41
|
+
Emision,
|
|
42
|
+
Recepcion,
|
|
43
|
+
Coherencia,
|
|
44
|
+
Disonancia,
|
|
45
|
+
Acoplamiento,
|
|
46
|
+
Resonancia,
|
|
47
|
+
Silencio,
|
|
48
|
+
Expansion,
|
|
49
|
+
Contraccion,
|
|
50
|
+
Autoorganizacion,
|
|
51
|
+
Mutacion,
|
|
52
|
+
Transicion,
|
|
53
|
+
Recursividad,
|
|
54
|
+
OPERADORES,
|
|
55
|
+
validate_sequence,
|
|
56
|
+
run_sequence,
|
|
57
|
+
)
|
|
37
58
|
|
|
38
59
|
|
|
39
60
|
__all__ = [
|
|
40
61
|
"preparar_red",
|
|
41
|
-
"step", "run", "set_delta_nfr_hook",
|
|
62
|
+
"step", "run", "set_delta_nfr_hook", "validate_canon",
|
|
42
63
|
|
|
43
64
|
"attach_standard_observer", "coherencia_global", "orden_kuramoto",
|
|
44
65
|
"GAMMA_REGISTRY", "eval_gamma", "kuramoto_R_psi",
|
|
@@ -52,8 +73,17 @@ __all__ = [
|
|
|
52
73
|
"Tg_global", "Tg_by_node",
|
|
53
74
|
"latency_series", "glifogram_series",
|
|
54
75
|
"glyph_top", "glyph_dwell_stats",
|
|
76
|
+
"export_history",
|
|
77
|
+
"aplicar_remesh_red_topologico",
|
|
55
78
|
"play", "seq", "block", "target", "wait", "THOL", "TARGET", "WAIT",
|
|
56
79
|
"cli_main", "build_graph", "get_preset", "NodeState",
|
|
57
80
|
"ejemplo_canonico_basico",
|
|
81
|
+
"create_nfr",
|
|
82
|
+
"Operador", "Emision", "Recepcion", "Coherencia", "Disonancia",
|
|
83
|
+
"Acoplamiento", "Resonancia", "Silencio", "Expansion", "Contraccion",
|
|
84
|
+
"Autoorganizacion", "Mutacion", "Transicion", "Recursividad",
|
|
85
|
+
"OPERADORES", "validate_sequence", "run_sequence",
|
|
58
86
|
"__version__",
|
|
59
87
|
]
|
|
88
|
+
|
|
89
|
+
|
tnfr/cli.py
CHANGED
|
@@ -18,13 +18,15 @@ from .metrics import (
|
|
|
18
18
|
latency_series,
|
|
19
19
|
glifogram_series,
|
|
20
20
|
glyph_top,
|
|
21
|
+
export_history,
|
|
21
22
|
)
|
|
22
23
|
from .trace import register_trace
|
|
23
24
|
from .program import play, seq, block, wait, target
|
|
24
|
-
from .dynamics import step, _update_history, default_glyph_selector, parametric_glyph_selector
|
|
25
|
+
from .dynamics import step, _update_history, default_glyph_selector, parametric_glyph_selector, validate_canon
|
|
25
26
|
from .gamma import GAMMA_REGISTRY
|
|
26
27
|
from .scenarios import build_graph
|
|
27
28
|
from .presets import get_preset
|
|
29
|
+
from .config import apply_config
|
|
28
30
|
|
|
29
31
|
|
|
30
32
|
def _save_json(path: str, data: Any) -> None:
|
|
@@ -32,6 +34,24 @@ def _save_json(path: str, data: Any) -> None:
|
|
|
32
34
|
json.dump(data, f, ensure_ascii=False, indent=2)
|
|
33
35
|
|
|
34
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
|
+
|
|
35
55
|
def _load_sequence(path: str) -> List[Any]:
|
|
36
56
|
with open(path, "r", encoding="utf-8") as f:
|
|
37
57
|
text = f.read()
|
|
@@ -72,10 +92,29 @@ def _attach_callbacks(G: nx.Graph) -> None:
|
|
|
72
92
|
|
|
73
93
|
def cmd_run(args: argparse.Namespace) -> int:
|
|
74
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)
|
|
75
97
|
_attach_callbacks(G)
|
|
76
|
-
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)
|
|
77
112
|
G.graph["glyph_selector"] = default_glyph_selector if args.selector == "basic" else parametric_glyph_selector
|
|
78
|
-
G.graph["GAMMA"] = {
|
|
113
|
+
G.graph["GAMMA"] = {
|
|
114
|
+
"type": args.gamma_type,
|
|
115
|
+
"beta": args.gamma_beta,
|
|
116
|
+
"R0": args.gamma_R0,
|
|
117
|
+
}
|
|
79
118
|
|
|
80
119
|
if args.preset:
|
|
81
120
|
program = get_preset(args.preset)
|
|
@@ -87,11 +126,28 @@ def cmd_run(args: argparse.Namespace) -> int:
|
|
|
87
126
|
|
|
88
127
|
if args.save_history:
|
|
89
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)
|
|
90
146
|
|
|
91
147
|
if args.summary:
|
|
92
148
|
tg = Tg_global(G, normalize=True)
|
|
93
149
|
lat = latency_series(G)
|
|
94
|
-
print("Top
|
|
150
|
+
print("Top operadores por Tg:", glyph_top(G, k=5))
|
|
95
151
|
if lat["value"]:
|
|
96
152
|
print("Latencia media:", sum(lat["value"]) / max(1, len(lat["value"])) )
|
|
97
153
|
return 0
|
|
@@ -99,10 +155,29 @@ def cmd_run(args: argparse.Namespace) -> int:
|
|
|
99
155
|
|
|
100
156
|
def cmd_sequence(args: argparse.Namespace) -> int:
|
|
101
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)
|
|
102
160
|
_attach_callbacks(G)
|
|
103
|
-
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)
|
|
104
175
|
G.graph["glyph_selector"] = default_glyph_selector if args.selector == "basic" else parametric_glyph_selector
|
|
105
|
-
G.graph["GAMMA"] = {
|
|
176
|
+
G.graph["GAMMA"] = {
|
|
177
|
+
"type": args.gamma_type,
|
|
178
|
+
"beta": args.gamma_beta,
|
|
179
|
+
"R0": args.gamma_R0,
|
|
180
|
+
}
|
|
106
181
|
|
|
107
182
|
if args.preset:
|
|
108
183
|
program = get_preset(args.preset)
|
|
@@ -115,15 +190,30 @@ def cmd_sequence(args: argparse.Namespace) -> int:
|
|
|
115
190
|
|
|
116
191
|
if args.save_history:
|
|
117
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)
|
|
118
195
|
return 0
|
|
119
196
|
|
|
120
197
|
|
|
121
198
|
def cmd_metrics(args: argparse.Namespace) -> int:
|
|
122
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)
|
|
123
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)
|
|
124
210
|
G.graph.setdefault("GRAMMAR_CANON", DEFAULTS["GRAMMAR_CANON"]).update({"enabled": bool(args.grammar_canon)})
|
|
125
211
|
G.graph["glyph_selector"] = default_glyph_selector if args.selector == "basic" else parametric_glyph_selector
|
|
126
|
-
G.graph["GAMMA"] = {
|
|
212
|
+
G.graph["GAMMA"] = {
|
|
213
|
+
"type": args.gamma_type,
|
|
214
|
+
"beta": args.gamma_beta,
|
|
215
|
+
"R0": args.gamma_R0,
|
|
216
|
+
}
|
|
127
217
|
for _ in range(int(args.steps or 200)):
|
|
128
218
|
step(G)
|
|
129
219
|
|
|
@@ -155,11 +245,27 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
155
245
|
p_run.add_argument("--steps", type=int, default=200)
|
|
156
246
|
p_run.add_argument("--seed", type=int, default=1)
|
|
157
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)
|
|
158
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")
|
|
159
254
|
p_run.add_argument("--summary", action="store_true")
|
|
255
|
+
p_run.add_argument("--remesh-mode", choices=["knn", "mst", "community"], default=None)
|
|
160
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)
|
|
161
265
|
p_run.add_argument("--selector", choices=["basic", "param"], default="basic")
|
|
162
|
-
p_run.add_argument("--gamma", choices=list(GAMMA_REGISTRY.keys()), default="none")
|
|
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)
|
|
163
269
|
p_run.set_defaults(func=cmd_run)
|
|
164
270
|
|
|
165
271
|
p_seq = sub.add_parser("sequence", help="Ejecutar una secuencia (preset o YAML/JSON)")
|
|
@@ -168,7 +274,24 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
168
274
|
p_seq.add_argument("--seed", type=int, default=1)
|
|
169
275
|
p_seq.add_argument("--preset", type=str, default=None)
|
|
170
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)
|
|
171
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)
|
|
172
295
|
p_seq.set_defaults(func=cmd_sequence)
|
|
173
296
|
|
|
174
297
|
p_met = sub.add_parser("metrics", help="Correr breve y volcar métricas clave")
|
|
@@ -176,10 +299,16 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
176
299
|
p_met.add_argument("--topology", choices=["ring", "complete", "erdos"], default="ring")
|
|
177
300
|
p_met.add_argument("--steps", type=int, default=300)
|
|
178
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)
|
|
179
304
|
p_met.add_argument("--no-canon", dest="grammar_canon", action="store_false", default=True, help="Desactiva gramática canónica")
|
|
180
305
|
p_met.add_argument("--selector", choices=["basic", "param"], default="basic")
|
|
181
|
-
p_met.add_argument("--gamma", choices=list(GAMMA_REGISTRY.keys()), default="none")
|
|
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)
|
|
182
310
|
p_met.add_argument("--save", type=str, default=None)
|
|
311
|
+
p_met.add_argument("--config", type=str, default=None)
|
|
183
312
|
p_met.set_defaults(func=cmd_metrics)
|
|
184
313
|
|
|
185
314
|
args = p.parse_args(argv)
|
tnfr/config.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Carga e inyección de configuraciones externas.
|
|
2
|
+
|
|
3
|
+
Permite definir parámetros en JSON o YAML y aplicarlos sobre ``G.graph``
|
|
4
|
+
reutilizando :func:`tnfr.constants.inject_defaults`.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
from typing import Any, Dict
|
|
9
|
+
import json
|
|
10
|
+
|
|
11
|
+
try: # pragma: no cover - dependencia opcional
|
|
12
|
+
import yaml # type: ignore
|
|
13
|
+
except Exception: # pragma: no cover
|
|
14
|
+
yaml = None
|
|
15
|
+
|
|
16
|
+
from .constants import inject_defaults
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def load_config(path: str) -> Dict[str, Any]:
|
|
20
|
+
"""Lee un archivo JSON/YAML y devuelve un ``dict`` con los parámetros."""
|
|
21
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
22
|
+
text = f.read()
|
|
23
|
+
if path.endswith((".yaml", ".yml")):
|
|
24
|
+
if not yaml: # pragma: no cover - fallo en entorno sin pyyaml
|
|
25
|
+
raise RuntimeError("pyyaml no está instalado")
|
|
26
|
+
data = yaml.safe_load(text)
|
|
27
|
+
else:
|
|
28
|
+
data = json.loads(text)
|
|
29
|
+
if not isinstance(data, dict):
|
|
30
|
+
raise ValueError("El archivo de configuración debe contener un objeto")
|
|
31
|
+
return data
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def apply_config(G, path: str) -> None:
|
|
35
|
+
"""Inyecta parámetros desde ``path`` sobre ``G.graph``.
|
|
36
|
+
|
|
37
|
+
Se reutiliza :func:`inject_defaults` para mantener la semántica de los
|
|
38
|
+
*defaults* canónicos.
|
|
39
|
+
"""
|
|
40
|
+
cfg = load_config(path)
|
|
41
|
+
inject_defaults(G, cfg, override=True)
|
tnfr/constants.py
CHANGED
|
@@ -11,8 +11,10 @@ from typing import Dict, Any
|
|
|
11
11
|
# Parámetros canónicos
|
|
12
12
|
# -------------------------
|
|
13
13
|
DEFAULTS: Dict[str, Any] = {
|
|
14
|
-
# Discretización
|
|
15
|
-
"DT": 1.0,
|
|
14
|
+
# Discretización
|
|
15
|
+
"DT": 1.0,
|
|
16
|
+
"INTEGRATOR_METHOD": "euler",
|
|
17
|
+
"DT_MIN": 0.1,
|
|
16
18
|
|
|
17
19
|
# Rango de EPI (estructura primaria)
|
|
18
20
|
"EPI_MIN": -1.0,
|
|
@@ -43,9 +45,10 @@ DEFAULTS: Dict[str, Any] = {
|
|
|
43
45
|
"INIT_VF_CLAMP_TO_LIMITS": True,
|
|
44
46
|
|
|
45
47
|
|
|
46
|
-
# Mezcla para ΔNFR (campo nodal)
|
|
47
|
-
# phase: dispersión de fase local; epi: gradiente de EPI; vf: desajuste de νf
|
|
48
|
-
|
|
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},
|
|
49
52
|
|
|
50
53
|
# Índice de sentido Si = α·νf_norm + β·(1 - disp_fase) + γ·(1 - |ΔNFR|/max)
|
|
51
54
|
"SI_WEIGHTS": {"alpha": 0.34, "beta": 0.33, "gamma": 0.33},
|
|
@@ -76,33 +79,54 @@ DEFAULTS: Dict[str, Any] = {
|
|
|
76
79
|
"REMESH_COOLDOWN_VENTANA": 20, # pasos mínimos entre RE’MESH
|
|
77
80
|
"REMESH_COOLDOWN_TS": 0.0, # cooldown adicional por tiempo simulado
|
|
78
81
|
# Gating adicional basado en observadores (conmutador + ventana)
|
|
79
|
-
"REMESH_REQUIRE_STABILITY":
|
|
80
|
-
"REMESH_STABILITY_WINDOW": 25, # tamaño de ventana para evaluar estabilidad
|
|
81
|
-
"REMESH_MIN_PHASE_SYNC": 0.85, # media mínima de sincronía de fase en ventana
|
|
82
|
-
"REMESH_MAX_GLYPH_DISR": 0.35, # media máxima de carga glífica disruptiva en ventana
|
|
83
|
-
"
|
|
84
|
-
|
|
85
|
-
#
|
|
86
|
-
"
|
|
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)
|
|
87
97
|
"REMESH_ALPHA": 0.5, # mezcla con pasado
|
|
88
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,
|
|
89
105
|
|
|
90
|
-
# Histéresis glífica
|
|
91
|
-
"GLYPH_HYSTERESIS_WINDOW": 7,
|
|
92
|
-
|
|
93
|
-
#
|
|
94
|
-
"
|
|
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,
|
|
95
115
|
|
|
96
|
-
# Ventana para estimar la carga glífica en history/plots
|
|
116
|
+
# Ventana para estimar la carga glífica en history/plots
|
|
97
117
|
"GLYPH_LOAD_WINDOW": 50,
|
|
98
|
-
|
|
99
|
-
# Tamaño de ventana para coherencia promedio W̄
|
|
100
|
-
"WBAR_WINDOW": 25,
|
|
101
|
-
|
|
102
|
-
#
|
|
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)
|
|
103
127
|
"GLYPH_FACTORS": {
|
|
104
|
-
"AL_boost": 0.05, # A’L — pequeña emisión
|
|
105
|
-
"EN_mix": 0.25, # E’N — mezcla con vecindad
|
|
128
|
+
"AL_boost": 0.05, # A’L — pequeña emisión
|
|
129
|
+
"EN_mix": 0.25, # E’N — mezcla con vecindad
|
|
106
130
|
"IL_dnfr_factor": 0.7, # I’L — reduce ΔNFR
|
|
107
131
|
"OZ_dnfr_factor": 1.3, # O’Z — aumenta ΔNFR
|
|
108
132
|
"UM_theta_push": 0.25, # U’M — empuje adicional de fase local
|
|
@@ -111,17 +135,19 @@ DEFAULTS: Dict[str, Any] = {
|
|
|
111
135
|
"VAL_scale": 1.15, # VA’L — expande EPI
|
|
112
136
|
"NUL_scale": 0.85, # NU’L — contrae EPI
|
|
113
137
|
"THOL_accel": 0.10, # T’HOL — acelera (seg. deriv.) si hay umbral
|
|
114
|
-
"ZHIR_theta_shift": 1.57079632679, # Z’HIR — desplazamiento ~π/2
|
|
115
|
-
"NAV_jitter": 0.05, # NA’V — pequeña inestabilidad creativa
|
|
116
|
-
"
|
|
117
|
-
|
|
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
|
+
},
|
|
118
143
|
|
|
119
144
|
# Umbrales para el selector glífico por defecto
|
|
120
145
|
"GLYPH_THRESHOLDS": {"hi": 0.66, "lo": 0.33, "dnfr": 1e-3},
|
|
121
146
|
|
|
122
147
|
# Comportamiento NA’V
|
|
123
|
-
"NAV_RANDOM": True, # si True, usa jitter aleatorio en [-j, j]; si False, jitter determinista por signo
|
|
124
|
-
"
|
|
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
|
|
125
151
|
|
|
126
152
|
# Modo ruido para O’Z
|
|
127
153
|
"OZ_NOISE_MODE": False, # si True, añade ruido aditivo en ΔNFR
|
|
@@ -169,6 +195,41 @@ DEFAULTS.setdefault("GRAMMAR_CANON", {
|
|
|
169
195
|
"thol_close_dnfr": 0.15, # si el campo calma, cerramos con SH’A/NU’L
|
|
170
196
|
"si_high": 0.66, # umbral para elegir NU’L vs SH’A al cerrar
|
|
171
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
|
+
})
|
|
172
233
|
|
|
173
234
|
|
|
174
235
|
# -------------------------
|
|
@@ -177,11 +238,7 @@ DEFAULTS.setdefault("GRAMMAR_CANON", {
|
|
|
177
238
|
|
|
178
239
|
def attach_defaults(G, override: bool = False) -> None:
|
|
179
240
|
"""Escribe DEFAULTS en G.graph (sin sobreescribir si override=False)."""
|
|
180
|
-
G
|
|
181
|
-
for k, v in DEFAULTS.items():
|
|
182
|
-
if override or k not in G.graph:
|
|
183
|
-
G.graph[k] = v
|
|
184
|
-
G.graph["_tnfr_defaults_attached"] = True
|
|
241
|
+
inject_defaults(G, DEFAULTS, override=override)
|
|
185
242
|
|
|
186
243
|
|
|
187
244
|
def inject_defaults(G, defaults: Dict[str, Any] = DEFAULTS, override: bool = False) -> None:
|
|
@@ -210,7 +267,11 @@ def merge_overrides(G, **overrides) -> None:
|
|
|
210
267
|
ALIAS_VF = ("νf", "nu_f", "nu-f", "nu", "freq", "frequency")
|
|
211
268
|
ALIAS_THETA = ("θ", "theta", "fase", "phi", "phase")
|
|
212
269
|
ALIAS_DNFR = ("ΔNFR", "delta_nfr", "dnfr")
|
|
213
|
-
ALIAS_EPI = ("EPI", "psi", "PSI", "value")
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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")
|