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
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""Shared constants."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Callable
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
import copy
|
|
8
|
+
from types import MappingProxyType
|
|
9
|
+
|
|
10
|
+
from .core import CORE_DEFAULTS, REMESH_DEFAULTS
|
|
11
|
+
from .init import INIT_DEFAULTS
|
|
12
|
+
from .metric import (
|
|
13
|
+
METRIC_DEFAULTS,
|
|
14
|
+
SIGMA,
|
|
15
|
+
TRACE,
|
|
16
|
+
METRICS,
|
|
17
|
+
GRAMMAR_CANON,
|
|
18
|
+
COHERENCE,
|
|
19
|
+
DIAGNOSIS,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
from ..immutable import _is_immutable
|
|
23
|
+
|
|
24
|
+
try: # pragma: no cover - optional dependency
|
|
25
|
+
from ..cache import ensure_node_offset_map
|
|
26
|
+
except ImportError: # noqa: BLE001 - allow any import error
|
|
27
|
+
ensure_node_offset_map = None
|
|
28
|
+
|
|
29
|
+
# Secciones individuales exportadas
|
|
30
|
+
DEFAULT_SECTIONS: Mapping[str, Mapping[str, Any]] = MappingProxyType(
|
|
31
|
+
{
|
|
32
|
+
"core": CORE_DEFAULTS,
|
|
33
|
+
"init": INIT_DEFAULTS,
|
|
34
|
+
"remesh": REMESH_DEFAULTS,
|
|
35
|
+
"metric": METRIC_DEFAULTS,
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Diccionario combinado exportado
|
|
40
|
+
# Unimos los diccionarios en orden de menor a mayor prioridad para que los
|
|
41
|
+
# valores de ``METRIC_DEFAULTS`` sobrescriban al resto, como hacía
|
|
42
|
+
# ``ChainMap``.
|
|
43
|
+
DEFAULTS: Mapping[str, Any] = MappingProxyType(
|
|
44
|
+
CORE_DEFAULTS | INIT_DEFAULTS | REMESH_DEFAULTS | METRIC_DEFAULTS
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# -------------------------
|
|
48
|
+
# Utilidades
|
|
49
|
+
# -------------------------
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def inject_defaults(
|
|
53
|
+
G, defaults: Mapping[str, Any] = DEFAULTS, override: bool = False
|
|
54
|
+
) -> None:
|
|
55
|
+
"""Inject ``defaults`` into ``G.graph``.
|
|
56
|
+
|
|
57
|
+
``defaults`` is usually ``DEFAULTS``, combining all sub-dictionaries.
|
|
58
|
+
If ``override`` is ``True`` existing values are overwritten. Immutable
|
|
59
|
+
values (numbers, strings, tuples, etc.) are assigned directly. Tuples are
|
|
60
|
+
inspected recursively; if any element is mutable, a ``deepcopy`` is made
|
|
61
|
+
to avoid shared state.
|
|
62
|
+
"""
|
|
63
|
+
G.graph.setdefault("_tnfr_defaults_attached", False)
|
|
64
|
+
for k, v in defaults.items():
|
|
65
|
+
if override or k not in G.graph:
|
|
66
|
+
G.graph[k] = v if _is_immutable(v) else copy.deepcopy(v)
|
|
67
|
+
G.graph["_tnfr_defaults_attached"] = True
|
|
68
|
+
if ensure_node_offset_map is not None:
|
|
69
|
+
ensure_node_offset_map(G)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def merge_overrides(G, **overrides) -> None:
|
|
73
|
+
"""Apply specific changes to ``G.graph``.
|
|
74
|
+
|
|
75
|
+
Non-immutable values are deep-copied to avoid shared state with
|
|
76
|
+
:data:`DEFAULTS`.
|
|
77
|
+
"""
|
|
78
|
+
for key, value in overrides.items():
|
|
79
|
+
if key not in DEFAULTS:
|
|
80
|
+
raise KeyError(f"Parámetro desconocido: '{key}'")
|
|
81
|
+
G.graph[key] = value if _is_immutable(value) else copy.deepcopy(value)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_param(G, key: str):
|
|
85
|
+
"""Retrieve a parameter from ``G.graph`` or fall back to defaults."""
|
|
86
|
+
if key in G.graph:
|
|
87
|
+
return G.graph[key]
|
|
88
|
+
if key not in DEFAULTS:
|
|
89
|
+
raise KeyError(f"Parámetro desconocido: '{key}'")
|
|
90
|
+
return DEFAULTS[key]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def get_graph_param(G, key: str, cast: Callable[[Any], Any] = float):
|
|
94
|
+
"""Return ``key`` from ``G.graph`` applying ``cast``.
|
|
95
|
+
|
|
96
|
+
The ``cast`` argument must be a function (e.g. ``float``, ``int``,
|
|
97
|
+
``bool``). If the stored value is ``None`` it is returned without
|
|
98
|
+
casting.
|
|
99
|
+
"""
|
|
100
|
+
val = get_param(G, key)
|
|
101
|
+
return None if val is None else cast(val)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# Claves canónicas con nombres ASCII
|
|
105
|
+
VF_KEY = "νf"
|
|
106
|
+
THETA_KEY = "θ"
|
|
107
|
+
|
|
108
|
+
# Mapa de aliases para atributos nodales
|
|
109
|
+
ALIASES: dict[str, tuple[str, ...]] = {
|
|
110
|
+
"VF": (VF_KEY, "nu_f", "nu-f", "nu", "freq", "frequency"),
|
|
111
|
+
"THETA": (THETA_KEY, "theta", "fase", "phi", "phase"),
|
|
112
|
+
"DNFR": ("ΔNFR", "delta_nfr", "dnfr"),
|
|
113
|
+
"EPI": ("EPI", "psi", "PSI", "value"),
|
|
114
|
+
"EPI_KIND": ("EPI_kind", "epi_kind", "source_glyph"),
|
|
115
|
+
"SI": ("Si", "sense_index", "S_i", "sense", "meaning_index"),
|
|
116
|
+
"DEPI": ("dEPI_dt", "dpsi_dt", "dEPI", "velocity"),
|
|
117
|
+
"D2EPI": ("d2EPI_dt2", "d2psi_dt2", "d2EPI", "accel"),
|
|
118
|
+
"DVF": ("dνf_dt", "dvf_dt", "dnu_dt", "dvf"),
|
|
119
|
+
"D2VF": ("d2νf_dt2", "d2vf_dt2", "d2nu_dt2", "B"),
|
|
120
|
+
"DSI": ("δSi", "delta_Si", "dSi"),
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def get_aliases(key: str) -> tuple[str, ...]:
|
|
125
|
+
"""Return alias tuple for canonical ``key``."""
|
|
126
|
+
|
|
127
|
+
return ALIASES[key]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
VF_PRIMARY = get_aliases("VF")[0]
|
|
131
|
+
THETA_PRIMARY = get_aliases("THETA")[0]
|
|
132
|
+
DNFR_PRIMARY = get_aliases("DNFR")[0]
|
|
133
|
+
EPI_PRIMARY = get_aliases("EPI")[0]
|
|
134
|
+
EPI_KIND_PRIMARY = get_aliases("EPI_KIND")[0]
|
|
135
|
+
SI_PRIMARY = get_aliases("SI")[0]
|
|
136
|
+
dEPI_PRIMARY = get_aliases("DEPI")[0]
|
|
137
|
+
D2EPI_PRIMARY = get_aliases("D2EPI")[0]
|
|
138
|
+
dVF_PRIMARY = get_aliases("DVF")[0]
|
|
139
|
+
D2VF_PRIMARY = get_aliases("D2VF")[0]
|
|
140
|
+
dSI_PRIMARY = get_aliases("DSI")[0]
|
|
141
|
+
|
|
142
|
+
__all__ = (
|
|
143
|
+
"CORE_DEFAULTS",
|
|
144
|
+
"INIT_DEFAULTS",
|
|
145
|
+
"REMESH_DEFAULTS",
|
|
146
|
+
"METRIC_DEFAULTS",
|
|
147
|
+
"SIGMA",
|
|
148
|
+
"TRACE",
|
|
149
|
+
"METRICS",
|
|
150
|
+
"GRAMMAR_CANON",
|
|
151
|
+
"COHERENCE",
|
|
152
|
+
"DIAGNOSIS",
|
|
153
|
+
"DEFAULTS",
|
|
154
|
+
"DEFAULT_SECTIONS",
|
|
155
|
+
"ALIASES",
|
|
156
|
+
"inject_defaults",
|
|
157
|
+
"merge_overrides",
|
|
158
|
+
"get_param",
|
|
159
|
+
"get_graph_param",
|
|
160
|
+
"get_aliases",
|
|
161
|
+
"VF_KEY",
|
|
162
|
+
"THETA_KEY",
|
|
163
|
+
"VF_PRIMARY",
|
|
164
|
+
"THETA_PRIMARY",
|
|
165
|
+
"DNFR_PRIMARY",
|
|
166
|
+
"EPI_PRIMARY",
|
|
167
|
+
"EPI_KIND_PRIMARY",
|
|
168
|
+
"SI_PRIMARY",
|
|
169
|
+
"dEPI_PRIMARY",
|
|
170
|
+
"D2EPI_PRIMARY",
|
|
171
|
+
"dVF_PRIMARY",
|
|
172
|
+
"D2VF_PRIMARY",
|
|
173
|
+
"dSI_PRIMARY",
|
|
174
|
+
)
|
tnfr/constants/core.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""Core constants."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, asdict, field
|
|
6
|
+
from typing import Any, Mapping
|
|
7
|
+
from types import MappingProxyType
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
SELECTOR_THRESHOLD_DEFAULTS: Mapping[str, float] = MappingProxyType(
|
|
11
|
+
{
|
|
12
|
+
"si_hi": 0.66,
|
|
13
|
+
"si_lo": 0.33,
|
|
14
|
+
"dnfr_hi": 0.50,
|
|
15
|
+
"dnfr_lo": 0.10,
|
|
16
|
+
"accel_hi": 0.50,
|
|
17
|
+
"accel_lo": 0.10,
|
|
18
|
+
}
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True, slots=True)
|
|
23
|
+
class CoreDefaults:
|
|
24
|
+
"""Default parameters for the core engine.
|
|
25
|
+
|
|
26
|
+
The fields are exported via :data:`CORE_DEFAULTS` and may therefore appear
|
|
27
|
+
unused to static analysis tools such as Vulture.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
DT: float = 1.0
|
|
31
|
+
INTEGRATOR_METHOD: str = "euler"
|
|
32
|
+
DT_MIN: float = 0.1
|
|
33
|
+
EPI_MIN: float = -1.0
|
|
34
|
+
EPI_MAX: float = 1.0
|
|
35
|
+
VF_MIN: float = 0.0
|
|
36
|
+
VF_MAX: float = 1.0
|
|
37
|
+
THETA_WRAP: bool = True
|
|
38
|
+
DNFR_WEIGHTS: dict[str, float] = field(
|
|
39
|
+
default_factory=lambda: {
|
|
40
|
+
"phase": 0.34,
|
|
41
|
+
"epi": 0.33,
|
|
42
|
+
"vf": 0.33,
|
|
43
|
+
"topo": 0.0,
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
SI_WEIGHTS: dict[str, float] = field(
|
|
47
|
+
default_factory=lambda: {"alpha": 0.34, "beta": 0.33, "gamma": 0.33}
|
|
48
|
+
)
|
|
49
|
+
PHASE_K_GLOBAL: float = 0.05
|
|
50
|
+
PHASE_K_LOCAL: float = 0.15
|
|
51
|
+
PHASE_ADAPT: dict[str, Any] = field(
|
|
52
|
+
default_factory=lambda: {
|
|
53
|
+
"enabled": True,
|
|
54
|
+
"R_hi": 0.90,
|
|
55
|
+
"R_lo": 0.60,
|
|
56
|
+
"disr_hi": 0.50,
|
|
57
|
+
"disr_lo": 0.25,
|
|
58
|
+
"kG_min": 0.01,
|
|
59
|
+
"kG_max": 0.20,
|
|
60
|
+
"kL_min": 0.05,
|
|
61
|
+
"kL_max": 0.25,
|
|
62
|
+
"up": 0.10,
|
|
63
|
+
"down": 0.07,
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
UM_COMPAT_THRESHOLD: float = 0.75
|
|
67
|
+
UM_CANDIDATE_MODE: str = "sample"
|
|
68
|
+
UM_CANDIDATE_COUNT: int = 0
|
|
69
|
+
GLYPH_HYSTERESIS_WINDOW: int = 7
|
|
70
|
+
AL_MAX_LAG: int = 5
|
|
71
|
+
EN_MAX_LAG: int = 3
|
|
72
|
+
GLYPH_SELECTOR_MARGIN: float = 0.05
|
|
73
|
+
VF_ADAPT_TAU: int = 5
|
|
74
|
+
VF_ADAPT_MU: float = 0.1
|
|
75
|
+
GLYPH_FACTORS: dict[str, float] = field(
|
|
76
|
+
default_factory=lambda: {
|
|
77
|
+
"AL_boost": 0.05,
|
|
78
|
+
"EN_mix": 0.25,
|
|
79
|
+
"IL_dnfr_factor": 0.7,
|
|
80
|
+
"OZ_dnfr_factor": 1.3,
|
|
81
|
+
"UM_theta_push": 0.25,
|
|
82
|
+
"RA_epi_diff": 0.15,
|
|
83
|
+
"SHA_vf_factor": 0.85,
|
|
84
|
+
"VAL_scale": 1.15,
|
|
85
|
+
"NUL_scale": 0.85,
|
|
86
|
+
"THOL_accel": 0.10,
|
|
87
|
+
"ZHIR_theta_shift": 1.57079632679,
|
|
88
|
+
"NAV_jitter": 0.05,
|
|
89
|
+
"NAV_eta": 0.5,
|
|
90
|
+
"REMESH_alpha": 0.5,
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
GLYPH_THRESHOLDS: dict[str, float] = field(
|
|
94
|
+
default_factory=lambda: {"hi": 0.66, "lo": 0.33, "dnfr": 1e-3}
|
|
95
|
+
)
|
|
96
|
+
NAV_RANDOM: bool = True
|
|
97
|
+
NAV_STRICT: bool = False
|
|
98
|
+
RANDOM_SEED: int = 0
|
|
99
|
+
JITTER_CACHE_SIZE: int = 256
|
|
100
|
+
OZ_NOISE_MODE: bool = False
|
|
101
|
+
OZ_SIGMA: float = 0.1
|
|
102
|
+
GRAMMAR: dict[str, Any] = field(
|
|
103
|
+
default_factory=lambda: {
|
|
104
|
+
"window": 3,
|
|
105
|
+
"avoid_repeats": ["ZHIR", "OZ", "THOL"],
|
|
106
|
+
"force_dnfr": 0.60,
|
|
107
|
+
"force_accel": 0.60,
|
|
108
|
+
"fallbacks": {"ZHIR": "NAV", "OZ": "ZHIR", "THOL": "NAV"},
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
SELECTOR_WEIGHTS: dict[str, float] = field(
|
|
112
|
+
default_factory=lambda: {"w_si": 0.5, "w_dnfr": 0.3, "w_accel": 0.2}
|
|
113
|
+
)
|
|
114
|
+
SELECTOR_THRESHOLDS: dict[str, float] = field(
|
|
115
|
+
default_factory=lambda: dict(SELECTOR_THRESHOLD_DEFAULTS)
|
|
116
|
+
)
|
|
117
|
+
GAMMA: dict[str, Any] = field(
|
|
118
|
+
default_factory=lambda: {"type": "none", "beta": 0.0, "R0": 0.0}
|
|
119
|
+
)
|
|
120
|
+
CALLBACKS_STRICT: bool = False
|
|
121
|
+
VALIDATORS_STRICT: bool = False
|
|
122
|
+
PROGRAM_TRACE_MAXLEN: int = 50
|
|
123
|
+
HISTORY_MAXLEN: int = 0
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@dataclass(frozen=True, slots=True)
|
|
127
|
+
class RemeshDefaults:
|
|
128
|
+
"""Default parameters for the remeshing subsystem.
|
|
129
|
+
|
|
130
|
+
As with :class:`CoreDefaults`, the fields are exported via
|
|
131
|
+
:data:`REMESH_DEFAULTS` and may look unused to static analysers.
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
EPS_DNFR_STABLE: float = 1e-3
|
|
135
|
+
EPS_DEPI_STABLE: float = 1e-3
|
|
136
|
+
FRACTION_STABLE_REMESH: float = 0.80
|
|
137
|
+
REMESH_COOLDOWN_VENTANA: int = 20
|
|
138
|
+
REMESH_COOLDOWN_TS: float = 0.0
|
|
139
|
+
REMESH_REQUIRE_STABILITY: bool = True
|
|
140
|
+
REMESH_STABILITY_WINDOW: int = 25
|
|
141
|
+
REMESH_MIN_PHASE_SYNC: float = 0.85
|
|
142
|
+
REMESH_MAX_GLYPH_DISR: float = 0.35
|
|
143
|
+
REMESH_MIN_SIGMA_MAG: float = 0.50
|
|
144
|
+
REMESH_MIN_KURAMOTO_R: float = 0.80
|
|
145
|
+
REMESH_MIN_SI_HI_FRAC: float = 0.50
|
|
146
|
+
REMESH_LOG_EVENTS: bool = True
|
|
147
|
+
REMESH_MODE: str = "knn"
|
|
148
|
+
REMESH_COMMUNITY_K: int = 2
|
|
149
|
+
REMESH_TAU_GLOBAL: int = 8
|
|
150
|
+
REMESH_TAU_LOCAL: int = 4
|
|
151
|
+
REMESH_ALPHA: float = 0.5
|
|
152
|
+
REMESH_ALPHA_HARD: bool = False
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
_core_defaults = asdict(CoreDefaults())
|
|
156
|
+
_remesh_defaults = asdict(RemeshDefaults())
|
|
157
|
+
|
|
158
|
+
CORE_DEFAULTS = MappingProxyType(_core_defaults)
|
|
159
|
+
REMESH_DEFAULTS = MappingProxyType(_remesh_defaults)
|
tnfr/constants/init.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Initialization constants."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, asdict
|
|
6
|
+
import math
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True, slots=True)
|
|
10
|
+
class InitDefaults:
|
|
11
|
+
"""Default parameters for node initialisation.
|
|
12
|
+
|
|
13
|
+
The fields are collected into :data:`INIT_DEFAULTS` and may therefore
|
|
14
|
+
appear unused to tools like Vulture.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
INIT_RANDOM_PHASE: bool = True
|
|
18
|
+
INIT_THETA_MIN: float = -math.pi
|
|
19
|
+
INIT_THETA_MAX: float = math.pi
|
|
20
|
+
INIT_VF_MODE: str = "uniform"
|
|
21
|
+
INIT_VF_MIN: float | None = None
|
|
22
|
+
INIT_VF_MAX: float | None = None
|
|
23
|
+
INIT_VF_MEAN: float = 0.5
|
|
24
|
+
INIT_VF_STD: float = 0.15
|
|
25
|
+
INIT_VF_CLAMP_TO_LIMITS: bool = True
|
|
26
|
+
INIT_SI_MIN: float = 0.4
|
|
27
|
+
INIT_SI_MAX: float = 0.7
|
|
28
|
+
INIT_EPI_VALUE: float = 0.0
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
INIT_DEFAULTS = asdict(InitDefaults())
|
tnfr/constants/metric.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Metric constants."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, asdict, field
|
|
6
|
+
from typing import Any
|
|
7
|
+
from types import MappingProxyType
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True, slots=True)
|
|
11
|
+
class MetricDefaults:
|
|
12
|
+
"""Default parameters for metric computation.
|
|
13
|
+
|
|
14
|
+
The fields are gathered into :data:`METRIC_DEFAULTS` and exposed through
|
|
15
|
+
read-only views below, so they may appear unused to static analysis tools.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
PHASE_HISTORY_MAXLEN: int = 50
|
|
19
|
+
STOP_EARLY: dict[str, Any] = field(
|
|
20
|
+
default_factory=lambda: {
|
|
21
|
+
"enabled": False,
|
|
22
|
+
"window": 25,
|
|
23
|
+
"fraction": 0.90,
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
SIGMA: dict[str, Any] = field(
|
|
27
|
+
default_factory=lambda: {
|
|
28
|
+
"enabled": True,
|
|
29
|
+
"weight": "Si", # "Si" | "EPI" | "1"
|
|
30
|
+
"smooth": 0.0, # EMA sobre el vector global (0=off)
|
|
31
|
+
"history_key": "sigma_global",
|
|
32
|
+
"per_node": False,
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
TRACE: dict[str, Any] = field(
|
|
36
|
+
default_factory=lambda: {
|
|
37
|
+
"enabled": True,
|
|
38
|
+
"capture": [
|
|
39
|
+
"gamma",
|
|
40
|
+
"grammar",
|
|
41
|
+
"selector",
|
|
42
|
+
"dnfr_weights",
|
|
43
|
+
"si_weights",
|
|
44
|
+
"callbacks",
|
|
45
|
+
"thol_open_nodes",
|
|
46
|
+
"sigma",
|
|
47
|
+
"kuramoto",
|
|
48
|
+
"glyph_counts",
|
|
49
|
+
],
|
|
50
|
+
"history_key": "trace_meta",
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
METRICS: dict[str, Any] = field(
|
|
54
|
+
default_factory=lambda: {
|
|
55
|
+
"enabled": True,
|
|
56
|
+
"save_by_node": True,
|
|
57
|
+
"normalize_series": False,
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
GRAMMAR_CANON: dict[str, Any] = field(
|
|
61
|
+
default_factory=lambda: {
|
|
62
|
+
"enabled": True,
|
|
63
|
+
"zhir_requires_oz_window": 3,
|
|
64
|
+
"zhir_dnfr_min": 0.05,
|
|
65
|
+
"thol_min_len": 2,
|
|
66
|
+
"thol_max_len": 6,
|
|
67
|
+
"thol_close_dnfr": 0.15,
|
|
68
|
+
"si_high": 0.66,
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
COHERENCE: dict[str, Any] = field(
|
|
72
|
+
default_factory=lambda: {
|
|
73
|
+
"enabled": True,
|
|
74
|
+
"scope": "neighbors",
|
|
75
|
+
"weights": {"phase": 0.34, "epi": 0.33, "vf": 0.20, "si": 0.13},
|
|
76
|
+
"self_on_diag": True,
|
|
77
|
+
"store_mode": "sparse",
|
|
78
|
+
"threshold": 0.0,
|
|
79
|
+
"history_key": "W_sparse",
|
|
80
|
+
"Wi_history_key": "W_i",
|
|
81
|
+
"stats_history_key": "W_stats",
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
DIAGNOSIS: dict[str, Any] = field(
|
|
85
|
+
default_factory=lambda: {
|
|
86
|
+
"enabled": True,
|
|
87
|
+
"window": 16,
|
|
88
|
+
"history_key": "nodal_diag",
|
|
89
|
+
"stable": {"Rloc_hi": 0.80, "dnfr_lo": 0.20, "persist": 3},
|
|
90
|
+
"dissonance": {"Rloc_lo": 0.40, "dnfr_hi": 0.50, "persist": 3},
|
|
91
|
+
"transition": {"persist": 2},
|
|
92
|
+
"compute_symmetry": True,
|
|
93
|
+
"include_typology": False,
|
|
94
|
+
"advice": {
|
|
95
|
+
"stable": ["Coherence", "Coupling", "Resonance"],
|
|
96
|
+
"transition": ["Transition", "Resonance", "Self-organisation"],
|
|
97
|
+
"dissonant": ["Silence", "Contraction", "Mutation"],
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
METRIC_DEFAULTS = asdict(MetricDefaults())
|
|
104
|
+
|
|
105
|
+
SIGMA = MappingProxyType(METRIC_DEFAULTS["SIGMA"])
|
|
106
|
+
TRACE = MappingProxyType(METRIC_DEFAULTS["TRACE"])
|
|
107
|
+
METRICS = MappingProxyType(METRIC_DEFAULTS["METRICS"])
|
|
108
|
+
GRAMMAR_CANON = MappingProxyType(METRIC_DEFAULTS["GRAMMAR_CANON"])
|
|
109
|
+
COHERENCE = MappingProxyType(METRIC_DEFAULTS["COHERENCE"])
|
|
110
|
+
DIAGNOSIS = MappingProxyType(METRIC_DEFAULTS["DIAGNOSIS"])
|
tnfr/constants_glyphs.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Default glyphs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
from types import MappingProxyType
|
|
7
|
+
from typing import Mapping
|
|
8
|
+
|
|
9
|
+
from .types import Glyph
|
|
10
|
+
|
|
11
|
+
# -------------------------
|
|
12
|
+
# Orden canónico y clasificaciones funcionales
|
|
13
|
+
# -------------------------
|
|
14
|
+
|
|
15
|
+
GLYPHS_CANONICAL: tuple[str, ...] = (
|
|
16
|
+
Glyph.AL.value, # 0
|
|
17
|
+
Glyph.EN.value, # 1
|
|
18
|
+
Glyph.IL.value, # 2
|
|
19
|
+
Glyph.OZ.value, # 3
|
|
20
|
+
Glyph.UM.value, # 4
|
|
21
|
+
Glyph.RA.value, # 5
|
|
22
|
+
Glyph.SHA.value, # 6
|
|
23
|
+
Glyph.VAL.value, # 7
|
|
24
|
+
Glyph.NUL.value, # 8
|
|
25
|
+
Glyph.THOL.value, # 9
|
|
26
|
+
Glyph.ZHIR.value, # 10
|
|
27
|
+
Glyph.NAV.value, # 11
|
|
28
|
+
Glyph.REMESH.value, # 12
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
GLYPHS_CANONICAL_SET: frozenset[str] = frozenset(GLYPHS_CANONICAL)
|
|
32
|
+
|
|
33
|
+
ESTABILIZADORES = (
|
|
34
|
+
Glyph.IL.value,
|
|
35
|
+
Glyph.RA.value,
|
|
36
|
+
Glyph.UM.value,
|
|
37
|
+
Glyph.SHA.value,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
DISRUPTIVOS = (
|
|
41
|
+
Glyph.OZ.value,
|
|
42
|
+
Glyph.ZHIR.value,
|
|
43
|
+
Glyph.NAV.value,
|
|
44
|
+
Glyph.THOL.value,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Mapa general de agrupaciones glíficas para referencia cruzada.
|
|
48
|
+
GLYPH_GROUPS: Mapping[str, tuple[str, ...]] = MappingProxyType(
|
|
49
|
+
{
|
|
50
|
+
"estabilizadores": ESTABILIZADORES,
|
|
51
|
+
"disruptivos": DISRUPTIVOS,
|
|
52
|
+
# Grupos auxiliares para métricas morfosintácticas
|
|
53
|
+
"ID": (Glyph.OZ.value,),
|
|
54
|
+
"CM": (Glyph.ZHIR.value, Glyph.NAV.value),
|
|
55
|
+
"NE": (Glyph.IL.value, Glyph.THOL.value),
|
|
56
|
+
"PP_num": (Glyph.SHA.value,),
|
|
57
|
+
"PP_den": (Glyph.REMESH.value,),
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# -------------------------
|
|
62
|
+
# Mapa de ángulos glíficos
|
|
63
|
+
# -------------------------
|
|
64
|
+
|
|
65
|
+
# Ángulos canónicos para todos los glyphs reconocidos. Se calculan a partir
|
|
66
|
+
# del orden canónico y reglas de orientación para las categorías
|
|
67
|
+
# "estabilizadores" y "disruptivos".
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _build_angle_map() -> dict[str, float]:
|
|
71
|
+
"""Construir el mapa de ángulos en el plano σ."""
|
|
72
|
+
step = 2 * math.pi / len(GLYPHS_CANONICAL)
|
|
73
|
+
canonical = {g: i * step for i, g in enumerate(GLYPHS_CANONICAL)}
|
|
74
|
+
angles = dict(canonical)
|
|
75
|
+
|
|
76
|
+
# Reglas específicas de orientación
|
|
77
|
+
for idx, g in enumerate(ESTABILIZADORES):
|
|
78
|
+
angles[g] = idx * math.pi / 4
|
|
79
|
+
for idx, g in enumerate(DISRUPTIVOS):
|
|
80
|
+
angles[g] = math.pi + idx * math.pi / 4
|
|
81
|
+
|
|
82
|
+
# Excepciones manuales
|
|
83
|
+
angles[Glyph.VAL.value] = canonical[Glyph.RA.value]
|
|
84
|
+
angles[Glyph.NUL.value] = canonical[Glyph.ZHIR.value]
|
|
85
|
+
angles[Glyph.AL.value] = 0.0
|
|
86
|
+
return angles
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
ANGLE_MAP: Mapping[str, float] = MappingProxyType(_build_angle_map())
|
|
90
|
+
|
|
91
|
+
__all__ = (
|
|
92
|
+
"GLYPHS_CANONICAL",
|
|
93
|
+
"GLYPHS_CANONICAL_SET",
|
|
94
|
+
"ESTABILIZADORES",
|
|
95
|
+
"DISRUPTIVOS",
|
|
96
|
+
"GLYPH_GROUPS",
|
|
97
|
+
"ANGLE_MAP",
|
|
98
|
+
)
|