tnfr 4.5.1__py3-none-any.whl → 6.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tnfr/__init__.py +270 -90
- tnfr/__init__.pyi +40 -0
- tnfr/_compat.py +11 -0
- tnfr/_version.py +7 -0
- tnfr/_version.pyi +7 -0
- tnfr/alias.py +631 -0
- tnfr/alias.pyi +140 -0
- tnfr/cache.py +732 -0
- tnfr/cache.pyi +232 -0
- tnfr/callback_utils.py +381 -0
- tnfr/callback_utils.pyi +105 -0
- tnfr/cli/__init__.py +89 -0
- tnfr/cli/__init__.pyi +47 -0
- tnfr/cli/arguments.py +199 -0
- tnfr/cli/arguments.pyi +33 -0
- tnfr/cli/execution.py +322 -0
- tnfr/cli/execution.pyi +80 -0
- tnfr/cli/utils.py +34 -0
- tnfr/cli/utils.pyi +8 -0
- tnfr/config/__init__.py +12 -0
- tnfr/config/__init__.pyi +8 -0
- tnfr/config/constants.py +104 -0
- tnfr/config/constants.pyi +12 -0
- tnfr/config/init.py +36 -0
- tnfr/config/init.pyi +8 -0
- tnfr/config/operator_names.py +106 -0
- tnfr/config/operator_names.pyi +28 -0
- tnfr/config/presets.py +104 -0
- tnfr/config/presets.pyi +7 -0
- tnfr/constants/__init__.py +228 -0
- tnfr/constants/__init__.pyi +104 -0
- tnfr/constants/core.py +158 -0
- tnfr/constants/core.pyi +17 -0
- tnfr/constants/init.py +31 -0
- tnfr/constants/init.pyi +12 -0
- tnfr/constants/metric.py +102 -0
- tnfr/constants/metric.pyi +19 -0
- tnfr/constants_glyphs.py +16 -0
- tnfr/constants_glyphs.pyi +12 -0
- tnfr/dynamics/__init__.py +136 -0
- tnfr/dynamics/__init__.pyi +83 -0
- tnfr/dynamics/adaptation.py +201 -0
- tnfr/dynamics/aliases.py +22 -0
- tnfr/dynamics/coordination.py +343 -0
- tnfr/dynamics/dnfr.py +2315 -0
- tnfr/dynamics/dnfr.pyi +33 -0
- tnfr/dynamics/integrators.py +561 -0
- tnfr/dynamics/integrators.pyi +35 -0
- tnfr/dynamics/runtime.py +521 -0
- tnfr/dynamics/sampling.py +34 -0
- tnfr/dynamics/sampling.pyi +7 -0
- tnfr/dynamics/selectors.py +680 -0
- tnfr/execution.py +216 -0
- tnfr/execution.pyi +65 -0
- tnfr/flatten.py +283 -0
- tnfr/flatten.pyi +28 -0
- tnfr/gamma.py +320 -89
- tnfr/gamma.pyi +40 -0
- tnfr/glyph_history.py +337 -0
- tnfr/glyph_history.pyi +53 -0
- tnfr/grammar.py +23 -153
- tnfr/grammar.pyi +13 -0
- tnfr/helpers/__init__.py +151 -0
- tnfr/helpers/__init__.pyi +66 -0
- tnfr/helpers/numeric.py +88 -0
- tnfr/helpers/numeric.pyi +12 -0
- tnfr/immutable.py +214 -0
- tnfr/immutable.pyi +37 -0
- tnfr/initialization.py +199 -0
- tnfr/initialization.pyi +73 -0
- tnfr/io.py +311 -0
- tnfr/io.pyi +11 -0
- tnfr/locking.py +37 -0
- tnfr/locking.pyi +7 -0
- tnfr/metrics/__init__.py +41 -0
- tnfr/metrics/__init__.pyi +20 -0
- tnfr/metrics/coherence.py +1469 -0
- tnfr/metrics/common.py +149 -0
- tnfr/metrics/common.pyi +15 -0
- tnfr/metrics/core.py +259 -0
- tnfr/metrics/core.pyi +13 -0
- tnfr/metrics/diagnosis.py +840 -0
- tnfr/metrics/diagnosis.pyi +89 -0
- tnfr/metrics/export.py +151 -0
- tnfr/metrics/glyph_timing.py +369 -0
- tnfr/metrics/reporting.py +152 -0
- tnfr/metrics/reporting.pyi +12 -0
- tnfr/metrics/sense_index.py +294 -0
- tnfr/metrics/sense_index.pyi +9 -0
- tnfr/metrics/trig.py +216 -0
- tnfr/metrics/trig.pyi +12 -0
- tnfr/metrics/trig_cache.py +105 -0
- tnfr/metrics/trig_cache.pyi +10 -0
- tnfr/node.py +255 -177
- tnfr/node.pyi +161 -0
- tnfr/observers.py +154 -150
- tnfr/observers.pyi +46 -0
- tnfr/ontosim.py +135 -134
- tnfr/ontosim.pyi +33 -0
- tnfr/operators/__init__.py +452 -0
- tnfr/operators/__init__.pyi +31 -0
- tnfr/operators/definitions.py +181 -0
- tnfr/operators/definitions.pyi +92 -0
- tnfr/operators/jitter.py +266 -0
- tnfr/operators/jitter.pyi +11 -0
- tnfr/operators/registry.py +80 -0
- tnfr/operators/registry.pyi +15 -0
- tnfr/operators/remesh.py +569 -0
- tnfr/presets.py +10 -23
- tnfr/presets.pyi +7 -0
- tnfr/py.typed +0 -0
- tnfr/rng.py +440 -0
- tnfr/rng.pyi +14 -0
- tnfr/selector.py +217 -0
- tnfr/selector.pyi +19 -0
- tnfr/sense.py +307 -142
- tnfr/sense.pyi +30 -0
- tnfr/structural.py +69 -164
- tnfr/structural.pyi +46 -0
- tnfr/telemetry/__init__.py +13 -0
- tnfr/telemetry/verbosity.py +37 -0
- tnfr/tokens.py +61 -0
- tnfr/tokens.pyi +41 -0
- tnfr/trace.py +520 -95
- tnfr/trace.pyi +68 -0
- tnfr/types.py +382 -17
- tnfr/types.pyi +145 -0
- tnfr/utils/__init__.py +158 -0
- tnfr/utils/__init__.pyi +133 -0
- tnfr/utils/cache.py +755 -0
- tnfr/utils/cache.pyi +156 -0
- tnfr/utils/data.py +267 -0
- tnfr/utils/data.pyi +73 -0
- tnfr/utils/graph.py +87 -0
- tnfr/utils/graph.pyi +10 -0
- tnfr/utils/init.py +746 -0
- tnfr/utils/init.pyi +85 -0
- tnfr/utils/io.py +157 -0
- tnfr/utils/io.pyi +10 -0
- tnfr/utils/validators.py +130 -0
- tnfr/utils/validators.pyi +19 -0
- tnfr/validation/__init__.py +25 -0
- tnfr/validation/__init__.pyi +17 -0
- tnfr/validation/compatibility.py +59 -0
- tnfr/validation/compatibility.pyi +8 -0
- tnfr/validation/grammar.py +149 -0
- tnfr/validation/grammar.pyi +11 -0
- tnfr/validation/rules.py +194 -0
- tnfr/validation/rules.pyi +18 -0
- tnfr/validation/syntax.py +151 -0
- tnfr/validation/syntax.pyi +7 -0
- tnfr-6.0.0.dist-info/METADATA +135 -0
- tnfr-6.0.0.dist-info/RECORD +157 -0
- tnfr/cli.py +0 -322
- tnfr/config.py +0 -41
- tnfr/constants.py +0 -277
- tnfr/dynamics.py +0 -814
- tnfr/helpers.py +0 -264
- tnfr/main.py +0 -47
- tnfr/metrics.py +0 -597
- tnfr/operators.py +0 -525
- tnfr/program.py +0 -176
- tnfr/scenarios.py +0 -34
- tnfr/validators.py +0 -38
- tnfr-4.5.1.dist-info/METADATA +0 -221
- tnfr-4.5.1.dist-info/RECORD +0 -28
- {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/WHEEL +0 -0
- {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/entry_points.txt +0 -0
- {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/licenses/LICENSE.md +0 -0
- {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/top_level.txt +0 -0
tnfr/helpers.py
DELETED
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
helpers.py — TNFR canónica
|
|
3
|
-
|
|
4
|
-
Utilidades transversales + cálculo de Índice de sentido (Si).
|
|
5
|
-
"""
|
|
6
|
-
from __future__ import annotations
|
|
7
|
-
from typing import Iterable, Dict, Any
|
|
8
|
-
import math
|
|
9
|
-
from collections import deque
|
|
10
|
-
from statistics import fmean, StatisticsError
|
|
11
|
-
|
|
12
|
-
try:
|
|
13
|
-
import networkx as nx # solo para tipos
|
|
14
|
-
except Exception: # pragma: no cover
|
|
15
|
-
nx = None # type: ignore
|
|
16
|
-
|
|
17
|
-
from .constants import DEFAULTS, ALIAS_VF, ALIAS_THETA, ALIAS_DNFR, ALIAS_EPI, ALIAS_SI, ALIAS_EPI_KIND
|
|
18
|
-
|
|
19
|
-
# -------------------------
|
|
20
|
-
# Utilidades numéricas
|
|
21
|
-
# -------------------------
|
|
22
|
-
|
|
23
|
-
def clamp(x: float, a: float, b: float) -> float:
|
|
24
|
-
"""Constriñe ``x`` al intervalo cerrado [a, b]."""
|
|
25
|
-
return a if x < a else b if x > b else x
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def clamp_abs(x: float, m: float) -> float:
|
|
29
|
-
"""Limita ``x`` al rango simétrico [-m, m] usando ``abs(m)``."""
|
|
30
|
-
m = abs(m)
|
|
31
|
-
return clamp(x, -m, m)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def clamp01(x: float) -> float:
|
|
35
|
-
"""Ataja ``x`` a la banda [0, 1]."""
|
|
36
|
-
return clamp(x, 0.0, 1.0)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def list_mean(xs: Iterable[float], default: float = 0.0) -> float:
|
|
40
|
-
"""Promedio aritmético o ``default`` si ``xs`` está vacío."""
|
|
41
|
-
try:
|
|
42
|
-
return fmean(xs)
|
|
43
|
-
except StatisticsError:
|
|
44
|
-
return default
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def _wrap_angle(a: float) -> float:
|
|
48
|
-
"""Envuelve ángulo a (-π, π]."""
|
|
49
|
-
pi = math.pi
|
|
50
|
-
a = (a + pi) % (2 * pi) - pi
|
|
51
|
-
return a
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def phase_distance(a: float, b: float) -> float:
|
|
55
|
-
"""Distancia de fase normalizada en [0,1]. 0 = misma fase, 1 = opuesta."""
|
|
56
|
-
return abs(_wrap_angle(a - b)) / math.pi
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# -------------------------
|
|
60
|
-
# Acceso a atributos con alias
|
|
61
|
-
# -------------------------
|
|
62
|
-
|
|
63
|
-
def _get_attr(d: Dict[str, Any], aliases: Iterable[str], default: float = 0.0) -> float:
|
|
64
|
-
for k in aliases:
|
|
65
|
-
if k in d:
|
|
66
|
-
try:
|
|
67
|
-
return float(d[k])
|
|
68
|
-
except Exception:
|
|
69
|
-
continue
|
|
70
|
-
return float(default)
|
|
71
|
-
|
|
72
|
-
def _set_attr(d, aliases, value: float) -> None:
|
|
73
|
-
for k in aliases:
|
|
74
|
-
if k in d:
|
|
75
|
-
d[k] = float(value)
|
|
76
|
-
return
|
|
77
|
-
d[next(iter(aliases))] = float(value)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def _get_attr_str(d: Dict[str, Any], aliases: Iterable[str], default: str = "") -> str:
|
|
81
|
-
for k in aliases:
|
|
82
|
-
if k in d:
|
|
83
|
-
try:
|
|
84
|
-
return str(d[k])
|
|
85
|
-
except Exception:
|
|
86
|
-
continue
|
|
87
|
-
return str(default)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def _set_attr_str(d, aliases, value: str) -> None:
|
|
91
|
-
for k in aliases:
|
|
92
|
-
if k in d:
|
|
93
|
-
d[k] = str(value)
|
|
94
|
-
return
|
|
95
|
-
d[next(iter(aliases))] = str(value)
|
|
96
|
-
|
|
97
|
-
# -------------------------
|
|
98
|
-
# Estadísticos vecinales
|
|
99
|
-
# -------------------------
|
|
100
|
-
|
|
101
|
-
def media_vecinal(G, n, aliases: Iterable[str], default: float = 0.0) -> float:
|
|
102
|
-
"""Media del atributo indicado por ``aliases`` en los vecinos de ``n``."""
|
|
103
|
-
vals = (_get_attr(G.nodes[v], aliases, default) for v in G.neighbors(n))
|
|
104
|
-
return list_mean(vals, default)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def fase_media(G, n) -> float:
|
|
108
|
-
"""Promedio circular de las fases de los vecinos."""
|
|
109
|
-
x = y = 0.0
|
|
110
|
-
count = 0
|
|
111
|
-
for v in G.neighbors(n):
|
|
112
|
-
th = _get_attr(G.nodes[v], ALIAS_THETA, 0.0)
|
|
113
|
-
x += math.cos(th)
|
|
114
|
-
y += math.sin(th)
|
|
115
|
-
count += 1
|
|
116
|
-
if count == 0:
|
|
117
|
-
return _get_attr(G.nodes[n], ALIAS_THETA, 0.0)
|
|
118
|
-
return math.atan2(y / count, x / count)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
# -------------------------
|
|
122
|
-
# Historial de glifos por nodo
|
|
123
|
-
# -------------------------
|
|
124
|
-
|
|
125
|
-
def push_glifo(nd: Dict[str, Any], glifo: str, window: int) -> None:
|
|
126
|
-
"""Añade ``glifo`` al historial del nodo con tamaño máximo ``window``."""
|
|
127
|
-
hist = nd.setdefault("hist_glifos", deque(maxlen=window))
|
|
128
|
-
hist.append(str(glifo))
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
def reciente_glifo(nd: Dict[str, Any], glifo: str, ventana: int) -> bool:
|
|
132
|
-
"""Indica si ``glifo`` apareció en las últimas ``ventana`` emisiones"""
|
|
133
|
-
hist = nd.get("hist_glifos")
|
|
134
|
-
gl = str(glifo)
|
|
135
|
-
from itertools import islice
|
|
136
|
-
if hist and any(g == gl for g in islice(reversed(hist), ventana)):
|
|
137
|
-
return True
|
|
138
|
-
# fallback al glifo dominante actual
|
|
139
|
-
return _get_attr_str(nd, ALIAS_EPI_KIND, "") == gl
|
|
140
|
-
|
|
141
|
-
# -------------------------
|
|
142
|
-
# Utilidades de historial global
|
|
143
|
-
# -------------------------
|
|
144
|
-
|
|
145
|
-
def ensure_history(G) -> Dict[str, Any]:
|
|
146
|
-
"""Garantiza G.graph['history'] y la devuelve."""
|
|
147
|
-
return G.graph.setdefault("history", {})
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
def last_glifo(nd: Dict[str, Any]) -> str | None:
|
|
151
|
-
"""Retorna el glifo más reciente del nodo o ``None``."""
|
|
152
|
-
kind = _get_attr_str(nd, ALIAS_EPI_KIND, "")
|
|
153
|
-
if kind:
|
|
154
|
-
return kind
|
|
155
|
-
hist = nd.get("hist_glifos")
|
|
156
|
-
if not hist:
|
|
157
|
-
return None
|
|
158
|
-
try:
|
|
159
|
-
return hist[-1]
|
|
160
|
-
except Exception:
|
|
161
|
-
return None
|
|
162
|
-
|
|
163
|
-
# -------------------------
|
|
164
|
-
# Callbacks Γ(R)
|
|
165
|
-
# -------------------------
|
|
166
|
-
|
|
167
|
-
def _ensure_callbacks(G):
|
|
168
|
-
"""Garantiza la estructura de callbacks en G.graph."""
|
|
169
|
-
cbs = G.graph.setdefault("callbacks", {
|
|
170
|
-
"before_step": [],
|
|
171
|
-
"after_step": [],
|
|
172
|
-
"on_remesh": [],
|
|
173
|
-
})
|
|
174
|
-
# normaliza claves por si vienen incompletas
|
|
175
|
-
for k in ("before_step", "after_step", "on_remesh"):
|
|
176
|
-
cbs.setdefault(k, [])
|
|
177
|
-
return cbs
|
|
178
|
-
|
|
179
|
-
def register_callback(
|
|
180
|
-
G,
|
|
181
|
-
event: str | None = None,
|
|
182
|
-
func=None,
|
|
183
|
-
*,
|
|
184
|
-
when: str | None = None,
|
|
185
|
-
name: str | None = None,
|
|
186
|
-
):
|
|
187
|
-
"""Registra ``func`` como callback del ``event`` indicado.
|
|
188
|
-
|
|
189
|
-
Permite tanto la forma posicional ``register_callback(G, "after_step", fn)``
|
|
190
|
-
como la forma con palabras clave ``register_callback(G, when="after_step", func=fn)``.
|
|
191
|
-
El parámetro ``name`` se acepta por compatibilidad pero actualmente no se
|
|
192
|
-
utiliza.
|
|
193
|
-
"""
|
|
194
|
-
event = event or when
|
|
195
|
-
if event not in ("before_step", "after_step", "on_remesh"):
|
|
196
|
-
raise ValueError(f"Evento desconocido: {event}")
|
|
197
|
-
if func is None:
|
|
198
|
-
raise TypeError("func es obligatorio")
|
|
199
|
-
cbs = _ensure_callbacks(G)
|
|
200
|
-
cbs[event].append(func)
|
|
201
|
-
return func
|
|
202
|
-
|
|
203
|
-
def invoke_callbacks(G, event: str, ctx: dict | None = None):
|
|
204
|
-
"""Invoca todos los callbacks registrados para `event` con el contexto `ctx`."""
|
|
205
|
-
cbs = _ensure_callbacks(G).get(event, [])
|
|
206
|
-
strict = bool(G.graph.get("CALLBACKS_STRICT", DEFAULTS["CALLBACKS_STRICT"]))
|
|
207
|
-
ctx = ctx or {}
|
|
208
|
-
for fn in list(cbs):
|
|
209
|
-
try:
|
|
210
|
-
fn(G, ctx)
|
|
211
|
-
except Exception as e:
|
|
212
|
-
if strict:
|
|
213
|
-
raise
|
|
214
|
-
G.graph.setdefault("_callback_errors", []).append({
|
|
215
|
-
"event": event, "step": ctx.get("step"), "error": repr(e), "fn": repr(fn)
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
# -------------------------
|
|
219
|
-
# Índice de sentido (Si)
|
|
220
|
-
# -------------------------
|
|
221
|
-
|
|
222
|
-
def compute_Si(G, *, inplace: bool = True) -> Dict[Any, float]:
|
|
223
|
-
"""Calcula Si por nodo y lo escribe en G.nodes[n]["Si"].
|
|
224
|
-
|
|
225
|
-
Fórmula:
|
|
226
|
-
Si = α·νf_norm + β·(1 - disp_fase_local) + γ·(1 - |ΔNFR|/max|ΔNFR|)
|
|
227
|
-
También guarda en ``G.graph`` los pesos normalizados y la
|
|
228
|
-
sensibilidad parcial (∂Si/∂componente).
|
|
229
|
-
"""
|
|
230
|
-
alpha = float(G.graph.get("SI_WEIGHTS", DEFAULTS["SI_WEIGHTS"]).get("alpha", 0.34))
|
|
231
|
-
beta = float(G.graph.get("SI_WEIGHTS", DEFAULTS["SI_WEIGHTS"]).get("beta", 0.33))
|
|
232
|
-
gamma = float(G.graph.get("SI_WEIGHTS", DEFAULTS["SI_WEIGHTS"]).get("gamma", 0.33))
|
|
233
|
-
s = alpha + beta + gamma
|
|
234
|
-
if s <= 0:
|
|
235
|
-
alpha = beta = gamma = 1/3
|
|
236
|
-
else:
|
|
237
|
-
alpha, beta, gamma = alpha/s, beta/s, gamma/s
|
|
238
|
-
G.graph["_Si_weights"] = {"alpha": alpha, "beta": beta, "gamma": gamma}
|
|
239
|
-
G.graph["_Si_sensitivity"] = {"dSi_dvf_norm": alpha, "dSi_ddisp_fase": -beta, "dSi_ddnfr_norm": -gamma}
|
|
240
|
-
|
|
241
|
-
# Normalización de νf y ΔNFR en red
|
|
242
|
-
vfmax = max((abs(_get_attr(G.nodes[n], ALIAS_VF, 0.0)) for n in G.nodes()), default=1.0)
|
|
243
|
-
dnfrmax = max((abs(_get_attr(G.nodes[n], ALIAS_DNFR, 0.0)) for n in G.nodes()), default=1.0)
|
|
244
|
-
|
|
245
|
-
out: Dict[Any, float] = {}
|
|
246
|
-
for n in G.nodes():
|
|
247
|
-
nd = G.nodes[n]
|
|
248
|
-
vf = _get_attr(nd, ALIAS_VF, 0.0)
|
|
249
|
-
vf_norm = 0.0 if vfmax == 0 else clamp01(abs(vf)/vfmax)
|
|
250
|
-
|
|
251
|
-
# dispersión de fase local
|
|
252
|
-
th_i = _get_attr(nd, ALIAS_THETA, 0.0)
|
|
253
|
-
th_bar = fase_media(G, n)
|
|
254
|
-
disp_fase = phase_distance(th_i, th_bar) # [0,1]
|
|
255
|
-
|
|
256
|
-
dnfr = _get_attr(nd, ALIAS_DNFR, 0.0)
|
|
257
|
-
dnfr_norm = 0.0 if dnfrmax == 0 else clamp01(abs(dnfr)/dnfrmax)
|
|
258
|
-
|
|
259
|
-
Si = alpha*vf_norm + beta*(1.0 - disp_fase) + gamma*(1.0 - dnfr_norm)
|
|
260
|
-
Si = clamp01(Si)
|
|
261
|
-
out[n] = Si
|
|
262
|
-
if inplace:
|
|
263
|
-
_set_attr(nd, ALIAS_SI, Si)
|
|
264
|
-
return out
|
tnfr/main.py
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
import argparse, sys
|
|
3
|
-
import networkx as nx
|
|
4
|
-
from . import preparar_red, run, __version__
|
|
5
|
-
from .constants import merge_overrides, attach_defaults
|
|
6
|
-
from .sense import register_sigma_callback
|
|
7
|
-
from .metrics import register_metrics_callbacks
|
|
8
|
-
from .trace import register_trace
|
|
9
|
-
|
|
10
|
-
def main(argv: list[str] | None = None) -> None:
|
|
11
|
-
p = argparse.ArgumentParser(
|
|
12
|
-
prog="tnfr",
|
|
13
|
-
description="TNFR canónica — demo CLI (red Erdős–Rényi + dinámica glífica)",
|
|
14
|
-
)
|
|
15
|
-
p.add_argument("--version", action="store_true", help="muestra versión y sale")
|
|
16
|
-
p.add_argument("--n", type=int, default=30, help="nodos (Erdős–Rényi)")
|
|
17
|
-
p.add_argument("--p", type=float, default=0.15, help="probabilidad de arista (Erdős–Rényi)")
|
|
18
|
-
p.add_argument("--steps", type=int, default=100, help="pasos a simular")
|
|
19
|
-
p.add_argument("--observer", action="store_true", help="adjunta observador estándar")
|
|
20
|
-
args = p.parse_args(argv)
|
|
21
|
-
|
|
22
|
-
if args.version:
|
|
23
|
-
print(__version__)
|
|
24
|
-
return
|
|
25
|
-
|
|
26
|
-
G = nx.erdos_renyi_graph(args.n, args.p)
|
|
27
|
-
preparar_red(G, ATTACH_STD_OBSERVER=bool(args.observer))
|
|
28
|
-
attach_defaults(G)
|
|
29
|
-
register_sigma_callback(G)
|
|
30
|
-
register_metrics_callbacks(G)
|
|
31
|
-
register_trace(G)
|
|
32
|
-
# Ejemplo: activar Γi(R) lineal con β=0.2 y R0=0.5
|
|
33
|
-
merge_overrides(G, GAMMA={"type": "kuramoto_linear", "beta": 0.2, "R0": 0.5})
|
|
34
|
-
run(G, args.steps)
|
|
35
|
-
|
|
36
|
-
h = G.graph.get("history", {})
|
|
37
|
-
C = h.get("C_steps", [])[-1] if h.get("C_steps") else None
|
|
38
|
-
stab = h.get("stable_frac", [])[-1] if h.get("stable_frac") else None
|
|
39
|
-
R = h.get("kuramoto_R", [])[-1] if h.get("kuramoto_R") else None
|
|
40
|
-
|
|
41
|
-
print("TNFR terminado:")
|
|
42
|
-
if C is not None: print(f" C(t) ~ {C:.3f}")
|
|
43
|
-
if stab is not None: print(f" estable ~ {stab:.3f}")
|
|
44
|
-
if R is not None: print(f" R (Kuramoto) ~ {R:.3f}")
|
|
45
|
-
|
|
46
|
-
if __name__ == "__main__":
|
|
47
|
-
main(sys.argv[1:])
|