tnfr 4.5.0__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.
- tnfr/__init__.py +91 -89
- tnfr/alias.py +546 -0
- tnfr/cache.py +578 -0
- tnfr/callback_utils.py +388 -0
- tnfr/cli/__init__.py +75 -0
- tnfr/cli/arguments.py +177 -0
- tnfr/cli/execution.py +288 -0
- tnfr/cli/utils.py +36 -0
- tnfr/collections_utils.py +300 -0
- tnfr/config.py +19 -28
- tnfr/constants/__init__.py +174 -0
- tnfr/constants/core.py +159 -0
- tnfr/constants/init.py +31 -0
- tnfr/constants/metric.py +110 -0
- tnfr/constants_glyphs.py +98 -0
- tnfr/dynamics/__init__.py +658 -0
- tnfr/dynamics/dnfr.py +733 -0
- tnfr/dynamics/integrators.py +267 -0
- tnfr/dynamics/sampling.py +31 -0
- tnfr/execution.py +201 -0
- tnfr/flatten.py +283 -0
- tnfr/gamma.py +302 -88
- tnfr/glyph_history.py +290 -0
- tnfr/grammar.py +285 -96
- tnfr/graph_utils.py +84 -0
- tnfr/helpers/__init__.py +71 -0
- tnfr/helpers/numeric.py +87 -0
- tnfr/immutable.py +178 -0
- tnfr/import_utils.py +228 -0
- tnfr/initialization.py +197 -0
- tnfr/io.py +246 -0
- tnfr/json_utils.py +162 -0
- tnfr/locking.py +37 -0
- tnfr/logging_utils.py +116 -0
- tnfr/metrics/__init__.py +41 -0
- tnfr/metrics/coherence.py +829 -0
- tnfr/metrics/common.py +151 -0
- tnfr/metrics/core.py +101 -0
- tnfr/metrics/diagnosis.py +234 -0
- tnfr/metrics/export.py +137 -0
- tnfr/metrics/glyph_timing.py +189 -0
- tnfr/metrics/reporting.py +148 -0
- tnfr/metrics/sense_index.py +120 -0
- tnfr/metrics/trig.py +181 -0
- tnfr/metrics/trig_cache.py +109 -0
- tnfr/node.py +214 -159
- tnfr/observers.py +126 -128
- tnfr/ontosim.py +134 -134
- tnfr/operators/__init__.py +420 -0
- tnfr/operators/jitter.py +203 -0
- tnfr/operators/remesh.py +485 -0
- tnfr/presets.py +46 -14
- tnfr/rng.py +254 -0
- tnfr/selector.py +210 -0
- tnfr/sense.py +284 -131
- tnfr/structural.py +207 -79
- tnfr/tokens.py +60 -0
- tnfr/trace.py +329 -94
- tnfr/types.py +43 -17
- tnfr/validators.py +70 -24
- tnfr/value_utils.py +59 -0
- tnfr-4.5.2.dist-info/METADATA +379 -0
- tnfr-4.5.2.dist-info/RECORD +67 -0
- tnfr/cli.py +0 -322
- 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-4.5.0.dist-info/METADATA +0 -109
- tnfr-4.5.0.dist-info/RECORD +0 -28
- {tnfr-4.5.0.dist-info → tnfr-4.5.2.dist-info}/WHEEL +0 -0
- {tnfr-4.5.0.dist-info → tnfr-4.5.2.dist-info}/entry_points.txt +0 -0
- {tnfr-4.5.0.dist-info → tnfr-4.5.2.dist-info}/licenses/LICENSE.md +0 -0
- {tnfr-4.5.0.dist-info → tnfr-4.5.2.dist-info}/top_level.txt +0 -0
tnfr/gamma.py
CHANGED
|
@@ -1,44 +1,187 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Gamma registry."""
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from typing import Any, Callable, NamedTuple
|
|
5
|
+
import math
|
|
6
|
+
import logging
|
|
7
|
+
import hashlib
|
|
8
|
+
from collections.abc import Mapping
|
|
9
|
+
from functools import lru_cache
|
|
10
|
+
from types import MappingProxyType
|
|
5
11
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
from .constants import DEFAULTS, get_aliases
|
|
13
|
+
from .alias import get_attr
|
|
14
|
+
from .graph_utils import get_graph_mapping
|
|
15
|
+
from .cache import edge_version_cache, node_set_checksum
|
|
16
|
+
from .json_utils import json_dumps
|
|
17
|
+
from .logging_utils import get_logger
|
|
18
|
+
from .metrics.trig_cache import get_trig_cache
|
|
9
19
|
|
|
10
|
-
|
|
11
|
-
``kuramoto_bandpass``)
|
|
12
|
-
* ``beta`` – ganancia del acoplamiento
|
|
13
|
-
* ``R0`` – umbral de activación (solo lineal)
|
|
20
|
+
ALIAS_THETA = get_aliases("THETA")
|
|
14
21
|
|
|
15
|
-
Provee:
|
|
16
|
-
- kuramoto_R_psi(G): (R, ψ) orden de Kuramoto en la red
|
|
17
|
-
- GAMMA_REGISTRY: registro de acoplamientos canónicos
|
|
18
|
-
- eval_gamma(G, node, t): evalúa Γ para cada nodo según la config
|
|
19
|
-
"""
|
|
20
|
-
from __future__ import annotations
|
|
21
|
-
from typing import Dict, Any, Tuple
|
|
22
|
-
import math
|
|
23
|
-
import cmath
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
logger = get_logger(__name__)
|
|
24
|
+
|
|
25
|
+
DEFAULT_GAMMA: Mapping[str, Any] = MappingProxyType(dict(DEFAULTS["GAMMA"]))
|
|
26
|
+
|
|
27
|
+
__all__ = (
|
|
28
|
+
"kuramoto_R_psi",
|
|
29
|
+
"gamma_none",
|
|
30
|
+
"gamma_kuramoto_linear",
|
|
31
|
+
"gamma_kuramoto_bandpass",
|
|
32
|
+
"gamma_kuramoto_tanh",
|
|
33
|
+
"gamma_harmonic",
|
|
34
|
+
"GammaEntry",
|
|
35
|
+
"GAMMA_REGISTRY",
|
|
36
|
+
"eval_gamma",
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@lru_cache(maxsize=1)
|
|
41
|
+
def _default_gamma_spec() -> tuple[bytes, str]:
|
|
42
|
+
dumped = json_dumps(dict(DEFAULT_GAMMA), sort_keys=True, to_bytes=True)
|
|
43
|
+
hash_ = hashlib.blake2b(dumped, digest_size=16).hexdigest()
|
|
44
|
+
return dumped, hash_
|
|
45
|
+
|
|
27
46
|
|
|
47
|
+
def _ensure_kuramoto_cache(G, t) -> None:
|
|
48
|
+
"""Cache ``(R, ψ)`` for the current step ``t`` using
|
|
49
|
+
``edge_version_cache``."""
|
|
50
|
+
checksum = G.graph.get("_dnfr_nodes_checksum")
|
|
51
|
+
if checksum is None:
|
|
52
|
+
# reuse checksum from cached_nodes_and_A when available
|
|
53
|
+
checksum = node_set_checksum(G)
|
|
54
|
+
nodes_sig = (len(G), checksum)
|
|
55
|
+
max_steps = int(G.graph.get("KURAMOTO_CACHE_STEPS", 1))
|
|
28
56
|
|
|
29
|
-
def
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
57
|
+
def builder() -> dict[str, float]:
|
|
58
|
+
R, psi = kuramoto_R_psi(G)
|
|
59
|
+
return {"R": R, "psi": psi}
|
|
60
|
+
|
|
61
|
+
key = (t, nodes_sig)
|
|
62
|
+
entry = edge_version_cache(G, key, builder, max_entries=max_steps)
|
|
63
|
+
G.graph["_kuramoto_cache"] = entry
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def kuramoto_R_psi(G) -> tuple[float, float]:
|
|
67
|
+
"""Return ``(R, ψ)`` for Kuramoto order using θ from all nodes."""
|
|
68
|
+
max_steps = int(G.graph.get("KURAMOTO_CACHE_STEPS", 1))
|
|
69
|
+
trig = get_trig_cache(G, cache_size=max_steps)
|
|
70
|
+
n = len(trig.theta)
|
|
38
71
|
if n == 0:
|
|
39
72
|
return 0.0, 0.0
|
|
40
|
-
|
|
41
|
-
|
|
73
|
+
|
|
74
|
+
cos_sum = sum(trig.cos.values())
|
|
75
|
+
sin_sum = sum(trig.sin.values())
|
|
76
|
+
R = math.hypot(cos_sum, sin_sum) / n
|
|
77
|
+
psi = math.atan2(sin_sum, cos_sum)
|
|
78
|
+
return R, psi
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _kuramoto_common(G, node, _cfg):
|
|
82
|
+
"""Return ``(θ_i, R, ψ)`` for Kuramoto-based Γ functions.
|
|
83
|
+
|
|
84
|
+
Reads cached global order ``R`` and mean phase ``ψ`` and obtains node
|
|
85
|
+
phase ``θ_i``. ``_cfg`` is accepted only to keep a homogeneous signature
|
|
86
|
+
with Γ evaluators.
|
|
87
|
+
"""
|
|
88
|
+
cache = G.graph.get("_kuramoto_cache", {})
|
|
89
|
+
R = float(cache.get("R", 0.0))
|
|
90
|
+
psi = float(cache.get("psi", 0.0))
|
|
91
|
+
th_i = get_attr(G.nodes[node], ALIAS_THETA, 0.0)
|
|
92
|
+
return th_i, R, psi
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _read_gamma_raw(G) -> Mapping[str, Any] | None:
|
|
96
|
+
"""Return raw Γ specification from ``G.graph['GAMMA']``.
|
|
97
|
+
|
|
98
|
+
The returned value is the direct contents of ``G.graph['GAMMA']`` when
|
|
99
|
+
it is a mapping or the result of :func:`get_graph_mapping` if a path is
|
|
100
|
+
provided. Final validation and caching are handled elsewhere.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
raw = G.graph.get("GAMMA")
|
|
104
|
+
if raw is None or isinstance(raw, Mapping):
|
|
105
|
+
return raw
|
|
106
|
+
return get_graph_mapping(
|
|
107
|
+
G, "GAMMA", "G.graph['GAMMA'] no es un mapeo; se usa {'type': 'none'}"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _get_gamma_spec(G) -> Mapping[str, Any]:
|
|
112
|
+
"""Return validated Γ specification caching results.
|
|
113
|
+
|
|
114
|
+
The raw value from ``G.graph['GAMMA']`` is cached together with the
|
|
115
|
+
normalized specification and its hash. When the raw value is unchanged,
|
|
116
|
+
the cached spec is returned without re-reading or re-validating,
|
|
117
|
+
preventing repeated warnings or costly hashing.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
raw = G.graph.get("GAMMA")
|
|
121
|
+
cached_raw = G.graph.get("_gamma_raw")
|
|
122
|
+
cached_spec = G.graph.get("_gamma_spec")
|
|
123
|
+
cached_hash = G.graph.get("_gamma_spec_hash")
|
|
124
|
+
|
|
125
|
+
def _hash_mapping(mapping: Mapping[str, Any]) -> str:
|
|
126
|
+
dumped = json_dumps(mapping, sort_keys=True, to_bytes=True)
|
|
127
|
+
return hashlib.blake2b(dumped, digest_size=16).hexdigest()
|
|
128
|
+
|
|
129
|
+
mapping_hash: str | None = None
|
|
130
|
+
if isinstance(raw, Mapping):
|
|
131
|
+
mapping_hash = _hash_mapping(raw)
|
|
132
|
+
if (
|
|
133
|
+
raw is cached_raw
|
|
134
|
+
and cached_spec is not None
|
|
135
|
+
and cached_hash == mapping_hash
|
|
136
|
+
):
|
|
137
|
+
return cached_spec
|
|
138
|
+
elif raw is cached_raw and cached_spec is not None and cached_hash is not None:
|
|
139
|
+
return cached_spec
|
|
140
|
+
|
|
141
|
+
if raw is None:
|
|
142
|
+
spec = DEFAULT_GAMMA
|
|
143
|
+
_, cur_hash = _default_gamma_spec()
|
|
144
|
+
elif isinstance(raw, Mapping):
|
|
145
|
+
spec = raw
|
|
146
|
+
cur_hash = mapping_hash if mapping_hash is not None else _hash_mapping(spec)
|
|
147
|
+
else:
|
|
148
|
+
spec_raw = _read_gamma_raw(G)
|
|
149
|
+
if isinstance(spec_raw, Mapping) and spec_raw is not None:
|
|
150
|
+
spec = spec_raw
|
|
151
|
+
cur_hash = _hash_mapping(spec)
|
|
152
|
+
else:
|
|
153
|
+
spec = DEFAULT_GAMMA
|
|
154
|
+
_, cur_hash = _default_gamma_spec()
|
|
155
|
+
|
|
156
|
+
# Store raw input, validated spec and its hash for future calls
|
|
157
|
+
G.graph["_gamma_raw"] = raw
|
|
158
|
+
G.graph["_gamma_spec"] = spec
|
|
159
|
+
G.graph["_gamma_spec_hash"] = cur_hash
|
|
160
|
+
return spec
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# -----------------
|
|
164
|
+
# Helpers
|
|
165
|
+
# -----------------
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _gamma_params(
|
|
169
|
+
cfg: Mapping[str, Any], **defaults: float
|
|
170
|
+
) -> tuple[float, ...]:
|
|
171
|
+
"""Return normalized Γ parameters from ``cfg``.
|
|
172
|
+
|
|
173
|
+
Parameters are retrieved from ``cfg`` using the keys in ``defaults`` and
|
|
174
|
+
converted to ``float``. If a key is missing, its value from ``defaults`` is
|
|
175
|
+
used. Values convertible to ``float`` (e.g. strings) are accepted.
|
|
176
|
+
|
|
177
|
+
Example
|
|
178
|
+
-------
|
|
179
|
+
>>> beta, R0 = _gamma_params(cfg, beta=0.0, R0=0.0)
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
return tuple(
|
|
183
|
+
float(cfg.get(name, default)) for name, default in defaults.items()
|
|
184
|
+
)
|
|
42
185
|
|
|
43
186
|
|
|
44
187
|
# -----------------
|
|
@@ -46,82 +189,153 @@ def kuramoto_R_psi(G) -> Tuple[float, float]:
|
|
|
46
189
|
# -----------------
|
|
47
190
|
|
|
48
191
|
|
|
49
|
-
def gamma_none(G, node, t, cfg:
|
|
192
|
+
def gamma_none(G, node, t, cfg: dict[str, Any]) -> float:
|
|
50
193
|
return 0.0
|
|
51
194
|
|
|
52
195
|
|
|
53
|
-
def
|
|
54
|
-
|
|
196
|
+
def _gamma_kuramoto(
|
|
197
|
+
G,
|
|
198
|
+
node,
|
|
199
|
+
cfg: Mapping[str, Any],
|
|
200
|
+
builder: Callable[..., float],
|
|
201
|
+
**defaults: float,
|
|
202
|
+
) -> float:
|
|
203
|
+
"""Helper for Kuramoto-based Γ functions.
|
|
55
204
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
- ψ es la fase media (dirección de coordinación).
|
|
59
|
-
- β, R0 son parámetros (ganancia/umbral).
|
|
60
|
-
|
|
61
|
-
Uso: refuerza integración cuando la red ya exhibe coherencia de fase (R>R0).
|
|
205
|
+
``builder`` receives ``(θ_i, R, ψ, *params)`` where ``params`` are
|
|
206
|
+
extracted from ``cfg`` according to ``defaults``.
|
|
62
207
|
"""
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
R, psi =
|
|
66
|
-
|
|
208
|
+
|
|
209
|
+
params = _gamma_params(cfg, **defaults)
|
|
210
|
+
th_i, R, psi = _kuramoto_common(G, node, cfg)
|
|
211
|
+
return builder(th_i, R, psi, *params)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _builder_linear(th_i: float, R: float, psi: float, beta: float, R0: float) -> float:
|
|
67
215
|
return beta * (R - R0) * math.cos(th_i - psi)
|
|
68
216
|
|
|
69
217
|
|
|
70
|
-
def
|
|
71
|
-
"""Γ = β · R(1-R) · sign(cos(θ_i - ψ))"""
|
|
72
|
-
beta = float(cfg.get("beta", 0.0))
|
|
73
|
-
R, psi = kuramoto_R_psi(G)
|
|
74
|
-
th_i = _get_attr(G.nodes[node], ALIAS_THETA, 0.0)
|
|
218
|
+
def _builder_bandpass(th_i: float, R: float, psi: float, beta: float) -> float:
|
|
75
219
|
sgn = 1.0 if math.cos(th_i - psi) >= 0.0 else -1.0
|
|
76
220
|
return beta * R * (1.0 - R) * sgn
|
|
77
221
|
|
|
78
222
|
|
|
79
|
-
def
|
|
80
|
-
|
|
223
|
+
def _builder_tanh(th_i: float, R: float, psi: float, beta: float, k: float, R0: float) -> float:
|
|
224
|
+
return beta * math.tanh(k * (R - R0)) * math.cos(th_i - psi)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def gamma_kuramoto_linear(G, node, t, cfg: dict[str, Any]) -> float:
|
|
228
|
+
"""Linear Kuramoto coupling for Γi(R).
|
|
81
229
|
|
|
82
|
-
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
- R0
|
|
230
|
+
Formula: Γ = β · (R - R0) · cos(θ_i - ψ)
|
|
231
|
+
- R ∈ [0,1] is the global phase order.
|
|
232
|
+
- ψ is the mean phase (coordination direction).
|
|
233
|
+
- β, R0 are parameters (gain/threshold).
|
|
234
|
+
|
|
235
|
+
Use: reinforces integration when the network already shows phase
|
|
236
|
+
coherence (R>R0).
|
|
86
237
|
"""
|
|
87
|
-
beta = float(cfg.get("beta", 0.0))
|
|
88
|
-
k = float(cfg.get("k", 1.0))
|
|
89
|
-
R0 = float(cfg.get("R0", 0.0))
|
|
90
|
-
R, psi = kuramoto_R_psi(G)
|
|
91
|
-
th_i = _get_attr(G.nodes[node], ALIAS_THETA, 0.0)
|
|
92
|
-
return beta * math.tanh(k * (R - R0)) * math.cos(th_i - psi)
|
|
93
238
|
|
|
239
|
+
return _gamma_kuramoto(G, node, cfg, _builder_linear, beta=0.0, R0=0.0)
|
|
94
240
|
|
|
95
|
-
def gamma_harmonic(G, node, t, cfg: Dict[str, Any]) -> float:
|
|
96
|
-
"""Forzamiento armónico coherente con el campo global de fase.
|
|
97
241
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
242
|
+
def gamma_kuramoto_bandpass(G, node, t, cfg: dict[str, Any]) -> float:
|
|
243
|
+
"""Γ = β · R(1-R) · sign(cos(θ_i - ψ))"""
|
|
244
|
+
|
|
245
|
+
return _gamma_kuramoto(G, node, cfg, _builder_bandpass, beta=0.0)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def gamma_kuramoto_tanh(G, node, t, cfg: dict[str, Any]) -> float:
|
|
249
|
+
"""Saturating tanh coupling for Γi(R).
|
|
250
|
+
|
|
251
|
+
Formula: Γ = β · tanh(k·(R - R0)) · cos(θ_i - ψ)
|
|
252
|
+
- β: coupling gain
|
|
253
|
+
- k: tanh slope (how fast it saturates)
|
|
254
|
+
- R0: activation threshold
|
|
102
255
|
"""
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
"
|
|
115
|
-
|
|
116
|
-
|
|
256
|
+
|
|
257
|
+
return _gamma_kuramoto(G, node, cfg, _builder_tanh, beta=0.0, k=1.0, R0=0.0)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def gamma_harmonic(G, node, t, cfg: dict[str, Any]) -> float:
|
|
261
|
+
"""Harmonic forcing aligned with the global phase field.
|
|
262
|
+
|
|
263
|
+
Formula: Γ = β · sin(ω·t + φ) · cos(θ_i - ψ)
|
|
264
|
+
- β: coupling gain
|
|
265
|
+
- ω: angular frequency of the forcing
|
|
266
|
+
- φ: initial phase of the forcing
|
|
267
|
+
"""
|
|
268
|
+
beta, omega, phi = _gamma_params(cfg, beta=0.0, omega=1.0, phi=0.0)
|
|
269
|
+
th_i, _, psi = _kuramoto_common(G, node, cfg)
|
|
270
|
+
return beta * math.sin(omega * t + phi) * math.cos(th_i - psi)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class GammaEntry(NamedTuple):
|
|
274
|
+
fn: Callable[[Any, Any, Any, dict[str, Any]], float]
|
|
275
|
+
needs_kuramoto: bool
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
# ``GAMMA_REGISTRY`` asocia el nombre del acoplamiento con un
|
|
279
|
+
# ``GammaEntry`` donde ``fn`` es la función evaluadora y
|
|
280
|
+
# ``needs_kuramoto`` indica si requiere precomputar el orden global de fase.
|
|
281
|
+
GAMMA_REGISTRY: dict[str, GammaEntry] = {
|
|
282
|
+
"none": GammaEntry(gamma_none, False),
|
|
283
|
+
"kuramoto_linear": GammaEntry(gamma_kuramoto_linear, True),
|
|
284
|
+
"kuramoto_bandpass": GammaEntry(gamma_kuramoto_bandpass, True),
|
|
285
|
+
"kuramoto_tanh": GammaEntry(gamma_kuramoto_tanh, True),
|
|
286
|
+
"harmonic": GammaEntry(gamma_harmonic, True),
|
|
117
287
|
}
|
|
118
288
|
|
|
119
289
|
|
|
120
|
-
def eval_gamma(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
290
|
+
def eval_gamma(
|
|
291
|
+
G,
|
|
292
|
+
node,
|
|
293
|
+
t,
|
|
294
|
+
*,
|
|
295
|
+
strict: bool = False,
|
|
296
|
+
log_level: int | None = None,
|
|
297
|
+
) -> float:
|
|
298
|
+
"""Evaluate Γi for ``node`` according to ``G.graph['GAMMA']``
|
|
299
|
+
specification.
|
|
300
|
+
|
|
301
|
+
If ``strict`` is ``True`` exceptions raised during evaluation are
|
|
302
|
+
propagated instead of returning ``0.0``. Likewise, if the specified
|
|
303
|
+
Γ type is not registered a warning is emitted (o ``ValueError`` en
|
|
304
|
+
modo estricto) y se usa ``gamma_none``.
|
|
305
|
+
|
|
306
|
+
``log_level`` controls the logging level for captured errors when
|
|
307
|
+
``strict`` is ``False``. If omitted, ``logging.ERROR`` is used in
|
|
308
|
+
strict mode and ``logging.DEBUG`` otherwise.
|
|
309
|
+
"""
|
|
310
|
+
spec = _get_gamma_spec(G)
|
|
311
|
+
spec_type = spec.get("type", "none")
|
|
312
|
+
reg_entry = GAMMA_REGISTRY.get(spec_type)
|
|
313
|
+
if reg_entry is None:
|
|
314
|
+
msg = f"Tipo GAMMA desconocido: {spec_type}"
|
|
315
|
+
if strict:
|
|
316
|
+
raise ValueError(msg)
|
|
317
|
+
logger.warning(msg)
|
|
318
|
+
entry = GammaEntry(gamma_none, False)
|
|
319
|
+
else:
|
|
320
|
+
entry = reg_entry
|
|
321
|
+
if entry.needs_kuramoto:
|
|
322
|
+
_ensure_kuramoto_cache(G, t)
|
|
124
323
|
try:
|
|
125
|
-
return float(fn(G, node, t, spec))
|
|
126
|
-
except
|
|
324
|
+
return float(entry.fn(G, node, t, spec))
|
|
325
|
+
except (ValueError, TypeError, ArithmeticError) as exc:
|
|
326
|
+
level = (
|
|
327
|
+
log_level
|
|
328
|
+
if log_level is not None
|
|
329
|
+
else (logging.ERROR if strict else logging.DEBUG)
|
|
330
|
+
)
|
|
331
|
+
logger.log(
|
|
332
|
+
level,
|
|
333
|
+
"Fallo al evaluar Γi para nodo %s en t=%s: %s: %s",
|
|
334
|
+
node,
|
|
335
|
+
t,
|
|
336
|
+
exc.__class__.__name__,
|
|
337
|
+
exc,
|
|
338
|
+
)
|
|
339
|
+
if strict:
|
|
340
|
+
raise
|
|
127
341
|
return 0.0
|