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.
Files changed (170) hide show
  1. tnfr/__init__.py +270 -90
  2. tnfr/__init__.pyi +40 -0
  3. tnfr/_compat.py +11 -0
  4. tnfr/_version.py +7 -0
  5. tnfr/_version.pyi +7 -0
  6. tnfr/alias.py +631 -0
  7. tnfr/alias.pyi +140 -0
  8. tnfr/cache.py +732 -0
  9. tnfr/cache.pyi +232 -0
  10. tnfr/callback_utils.py +381 -0
  11. tnfr/callback_utils.pyi +105 -0
  12. tnfr/cli/__init__.py +89 -0
  13. tnfr/cli/__init__.pyi +47 -0
  14. tnfr/cli/arguments.py +199 -0
  15. tnfr/cli/arguments.pyi +33 -0
  16. tnfr/cli/execution.py +322 -0
  17. tnfr/cli/execution.pyi +80 -0
  18. tnfr/cli/utils.py +34 -0
  19. tnfr/cli/utils.pyi +8 -0
  20. tnfr/config/__init__.py +12 -0
  21. tnfr/config/__init__.pyi +8 -0
  22. tnfr/config/constants.py +104 -0
  23. tnfr/config/constants.pyi +12 -0
  24. tnfr/config/init.py +36 -0
  25. tnfr/config/init.pyi +8 -0
  26. tnfr/config/operator_names.py +106 -0
  27. tnfr/config/operator_names.pyi +28 -0
  28. tnfr/config/presets.py +104 -0
  29. tnfr/config/presets.pyi +7 -0
  30. tnfr/constants/__init__.py +228 -0
  31. tnfr/constants/__init__.pyi +104 -0
  32. tnfr/constants/core.py +158 -0
  33. tnfr/constants/core.pyi +17 -0
  34. tnfr/constants/init.py +31 -0
  35. tnfr/constants/init.pyi +12 -0
  36. tnfr/constants/metric.py +102 -0
  37. tnfr/constants/metric.pyi +19 -0
  38. tnfr/constants_glyphs.py +16 -0
  39. tnfr/constants_glyphs.pyi +12 -0
  40. tnfr/dynamics/__init__.py +136 -0
  41. tnfr/dynamics/__init__.pyi +83 -0
  42. tnfr/dynamics/adaptation.py +201 -0
  43. tnfr/dynamics/aliases.py +22 -0
  44. tnfr/dynamics/coordination.py +343 -0
  45. tnfr/dynamics/dnfr.py +2315 -0
  46. tnfr/dynamics/dnfr.pyi +33 -0
  47. tnfr/dynamics/integrators.py +561 -0
  48. tnfr/dynamics/integrators.pyi +35 -0
  49. tnfr/dynamics/runtime.py +521 -0
  50. tnfr/dynamics/sampling.py +34 -0
  51. tnfr/dynamics/sampling.pyi +7 -0
  52. tnfr/dynamics/selectors.py +680 -0
  53. tnfr/execution.py +216 -0
  54. tnfr/execution.pyi +65 -0
  55. tnfr/flatten.py +283 -0
  56. tnfr/flatten.pyi +28 -0
  57. tnfr/gamma.py +320 -89
  58. tnfr/gamma.pyi +40 -0
  59. tnfr/glyph_history.py +337 -0
  60. tnfr/glyph_history.pyi +53 -0
  61. tnfr/grammar.py +23 -153
  62. tnfr/grammar.pyi +13 -0
  63. tnfr/helpers/__init__.py +151 -0
  64. tnfr/helpers/__init__.pyi +66 -0
  65. tnfr/helpers/numeric.py +88 -0
  66. tnfr/helpers/numeric.pyi +12 -0
  67. tnfr/immutable.py +214 -0
  68. tnfr/immutable.pyi +37 -0
  69. tnfr/initialization.py +199 -0
  70. tnfr/initialization.pyi +73 -0
  71. tnfr/io.py +311 -0
  72. tnfr/io.pyi +11 -0
  73. tnfr/locking.py +37 -0
  74. tnfr/locking.pyi +7 -0
  75. tnfr/metrics/__init__.py +41 -0
  76. tnfr/metrics/__init__.pyi +20 -0
  77. tnfr/metrics/coherence.py +1469 -0
  78. tnfr/metrics/common.py +149 -0
  79. tnfr/metrics/common.pyi +15 -0
  80. tnfr/metrics/core.py +259 -0
  81. tnfr/metrics/core.pyi +13 -0
  82. tnfr/metrics/diagnosis.py +840 -0
  83. tnfr/metrics/diagnosis.pyi +89 -0
  84. tnfr/metrics/export.py +151 -0
  85. tnfr/metrics/glyph_timing.py +369 -0
  86. tnfr/metrics/reporting.py +152 -0
  87. tnfr/metrics/reporting.pyi +12 -0
  88. tnfr/metrics/sense_index.py +294 -0
  89. tnfr/metrics/sense_index.pyi +9 -0
  90. tnfr/metrics/trig.py +216 -0
  91. tnfr/metrics/trig.pyi +12 -0
  92. tnfr/metrics/trig_cache.py +105 -0
  93. tnfr/metrics/trig_cache.pyi +10 -0
  94. tnfr/node.py +255 -177
  95. tnfr/node.pyi +161 -0
  96. tnfr/observers.py +154 -150
  97. tnfr/observers.pyi +46 -0
  98. tnfr/ontosim.py +135 -134
  99. tnfr/ontosim.pyi +33 -0
  100. tnfr/operators/__init__.py +452 -0
  101. tnfr/operators/__init__.pyi +31 -0
  102. tnfr/operators/definitions.py +181 -0
  103. tnfr/operators/definitions.pyi +92 -0
  104. tnfr/operators/jitter.py +266 -0
  105. tnfr/operators/jitter.pyi +11 -0
  106. tnfr/operators/registry.py +80 -0
  107. tnfr/operators/registry.pyi +15 -0
  108. tnfr/operators/remesh.py +569 -0
  109. tnfr/presets.py +10 -23
  110. tnfr/presets.pyi +7 -0
  111. tnfr/py.typed +0 -0
  112. tnfr/rng.py +440 -0
  113. tnfr/rng.pyi +14 -0
  114. tnfr/selector.py +217 -0
  115. tnfr/selector.pyi +19 -0
  116. tnfr/sense.py +307 -142
  117. tnfr/sense.pyi +30 -0
  118. tnfr/structural.py +69 -164
  119. tnfr/structural.pyi +46 -0
  120. tnfr/telemetry/__init__.py +13 -0
  121. tnfr/telemetry/verbosity.py +37 -0
  122. tnfr/tokens.py +61 -0
  123. tnfr/tokens.pyi +41 -0
  124. tnfr/trace.py +520 -95
  125. tnfr/trace.pyi +68 -0
  126. tnfr/types.py +382 -17
  127. tnfr/types.pyi +145 -0
  128. tnfr/utils/__init__.py +158 -0
  129. tnfr/utils/__init__.pyi +133 -0
  130. tnfr/utils/cache.py +755 -0
  131. tnfr/utils/cache.pyi +156 -0
  132. tnfr/utils/data.py +267 -0
  133. tnfr/utils/data.pyi +73 -0
  134. tnfr/utils/graph.py +87 -0
  135. tnfr/utils/graph.pyi +10 -0
  136. tnfr/utils/init.py +746 -0
  137. tnfr/utils/init.pyi +85 -0
  138. tnfr/utils/io.py +157 -0
  139. tnfr/utils/io.pyi +10 -0
  140. tnfr/utils/validators.py +130 -0
  141. tnfr/utils/validators.pyi +19 -0
  142. tnfr/validation/__init__.py +25 -0
  143. tnfr/validation/__init__.pyi +17 -0
  144. tnfr/validation/compatibility.py +59 -0
  145. tnfr/validation/compatibility.pyi +8 -0
  146. tnfr/validation/grammar.py +149 -0
  147. tnfr/validation/grammar.pyi +11 -0
  148. tnfr/validation/rules.py +194 -0
  149. tnfr/validation/rules.pyi +18 -0
  150. tnfr/validation/syntax.py +151 -0
  151. tnfr/validation/syntax.pyi +7 -0
  152. tnfr-6.0.0.dist-info/METADATA +135 -0
  153. tnfr-6.0.0.dist-info/RECORD +157 -0
  154. tnfr/cli.py +0 -322
  155. tnfr/config.py +0 -41
  156. tnfr/constants.py +0 -277
  157. tnfr/dynamics.py +0 -814
  158. tnfr/helpers.py +0 -264
  159. tnfr/main.py +0 -47
  160. tnfr/metrics.py +0 -597
  161. tnfr/operators.py +0 -525
  162. tnfr/program.py +0 -176
  163. tnfr/scenarios.py +0 -34
  164. tnfr/validators.py +0 -38
  165. tnfr-4.5.1.dist-info/METADATA +0 -221
  166. tnfr-4.5.1.dist-info/RECORD +0 -28
  167. {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/WHEEL +0 -0
  168. {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/entry_points.txt +0 -0
  169. {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/licenses/LICENSE.md +0 -0
  170. {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,228 @@
1
+ """Shared constants."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import copy
6
+ from collections.abc import Mapping
7
+ from types import MappingProxyType
8
+ from typing import Callable, TypeVar, cast
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
+ from ..types import GraphLike, TNFRConfigValue
24
+
25
+ T = TypeVar("T")
26
+
27
+ STATE_STABLE = "stable"
28
+ STATE_TRANSITION = "transition"
29
+ STATE_DISSONANT = "dissonant"
30
+
31
+ CANONICAL_STATE_TOKENS = frozenset(
32
+ {STATE_STABLE, STATE_TRANSITION, STATE_DISSONANT}
33
+ )
34
+
35
+ def normalise_state_token(token: str) -> str:
36
+ """Return the canonical English token for ``token``.
37
+
38
+ The helper now enforces the English identifiers exclusively. Values that
39
+ do not match the canonical set raise :class:`ValueError` so callers can
40
+ surface explicit migration errors when legacy payloads are encountered.
41
+ """
42
+
43
+ if not isinstance(token, str):
44
+ raise TypeError("state token must be a string")
45
+
46
+ stripped = token.strip()
47
+ lowered = stripped.lower()
48
+
49
+ if stripped in CANONICAL_STATE_TOKENS:
50
+ return stripped
51
+
52
+ if lowered in CANONICAL_STATE_TOKENS:
53
+ return lowered
54
+
55
+ raise ValueError(
56
+ "state token must be one of 'stable', 'transition', or 'dissonant'"
57
+ )
58
+
59
+ try: # pragma: no cover - optional dependency
60
+ from ..utils import ensure_node_offset_map as _ensure_node_offset_map
61
+ except ImportError: # noqa: BLE001 - allow any import error
62
+ _ensure_node_offset_map = None
63
+
64
+ ensure_node_offset_map: Callable[[GraphLike], None] | None = _ensure_node_offset_map
65
+
66
+ # Exported sections
67
+ DEFAULT_SECTIONS: Mapping[str, Mapping[str, TNFRConfigValue]] = MappingProxyType(
68
+ {
69
+ "core": CORE_DEFAULTS,
70
+ "init": INIT_DEFAULTS,
71
+ "remesh": REMESH_DEFAULTS,
72
+ "metric": METRIC_DEFAULTS,
73
+ }
74
+ )
75
+
76
+ # Combined exported dictionary
77
+ # Merge the dictionaries from lowest to highest priority so that
78
+ # ``METRIC_DEFAULTS`` overrides the rest, mirroring the previous ``ChainMap``
79
+ # behaviour.
80
+ DEFAULTS: Mapping[str, TNFRConfigValue] = MappingProxyType(
81
+ CORE_DEFAULTS | INIT_DEFAULTS | REMESH_DEFAULTS | METRIC_DEFAULTS
82
+ )
83
+
84
+ # -------------------------
85
+ # Utilities
86
+ # -------------------------
87
+
88
+
89
+ def inject_defaults(
90
+ G: GraphLike,
91
+ defaults: Mapping[str, TNFRConfigValue] = DEFAULTS,
92
+ override: bool = False,
93
+ ) -> None:
94
+ """Inject ``defaults`` into ``G.graph``.
95
+
96
+ ``defaults`` is usually ``DEFAULTS``, combining all sub-dictionaries.
97
+ If ``override`` is ``True`` existing values are overwritten. Immutable
98
+ values (numbers, strings, tuples, etc.) are assigned directly. Tuples are
99
+ inspected recursively; if any element is mutable, a ``deepcopy`` is made
100
+ to avoid shared state.
101
+ """
102
+ G.graph.setdefault("_tnfr_defaults_attached", False)
103
+ for k, v in defaults.items():
104
+ if override or k not in G.graph:
105
+ G.graph[k] = (
106
+ v
107
+ if _is_immutable(v)
108
+ else cast(TNFRConfigValue, copy.deepcopy(v))
109
+ )
110
+ G.graph["_tnfr_defaults_attached"] = True
111
+ if ensure_node_offset_map is not None:
112
+ ensure_node_offset_map(G)
113
+
114
+
115
+ def merge_overrides(G: GraphLike, **overrides: TNFRConfigValue) -> None:
116
+ """Apply specific changes to ``G.graph``.
117
+
118
+ Non-immutable values are deep-copied to avoid shared state with
119
+ :data:`DEFAULTS`.
120
+ """
121
+ for key, value in overrides.items():
122
+ if key not in DEFAULTS:
123
+ raise KeyError(f"Unknown parameter: '{key}'")
124
+ G.graph[key] = (
125
+ value
126
+ if _is_immutable(value)
127
+ else cast(TNFRConfigValue, copy.deepcopy(value))
128
+ )
129
+
130
+
131
+ def get_param(G: GraphLike, key: str) -> TNFRConfigValue:
132
+ """Retrieve a parameter from ``G.graph`` or fall back to defaults."""
133
+ if key in G.graph:
134
+ return G.graph[key]
135
+ if key not in DEFAULTS:
136
+ raise KeyError(f"Unknown parameter: '{key}'")
137
+ return DEFAULTS[key]
138
+
139
+
140
+ def get_graph_param(
141
+ G: GraphLike, key: str, cast: Callable[[object], T] = float
142
+ ) -> T | None:
143
+ """Return ``key`` from ``G.graph`` applying ``cast``.
144
+
145
+ The ``cast`` argument must be a function (e.g. ``float``, ``int``,
146
+ ``bool``). If the stored value is ``None`` it is returned without
147
+ casting.
148
+ """
149
+ val = get_param(G, key)
150
+ return None if val is None else cast(val)
151
+
152
+
153
+ # Canonical keys with ASCII spellings
154
+ VF_KEY = "νf"
155
+ THETA_KEY = "theta"
156
+
157
+ # Alias map for node attributes
158
+ ALIASES: dict[str, tuple[str, ...]] = {
159
+ "VF": (VF_KEY, "nu_f", "nu-f", "nu", "freq", "frequency"),
160
+ "THETA": (THETA_KEY, "phase"),
161
+ "DNFR": ("ΔNFR", "delta_nfr", "dnfr"),
162
+ "EPI": ("EPI", "psi", "PSI", "value"),
163
+ "EPI_KIND": ("EPI_kind", "epi_kind", "source_glyph"),
164
+ "SI": ("Si", "sense_index", "S_i", "sense", "meaning_index"),
165
+ "DEPI": ("dEPI_dt", "dpsi_dt", "dEPI", "velocity"),
166
+ "D2EPI": ("d2EPI_dt2", "d2psi_dt2", "d2EPI", "accel"),
167
+ "DVF": ("dνf_dt", "dvf_dt", "dnu_dt", "dvf"),
168
+ "D2VF": ("d2νf_dt2", "d2vf_dt2", "d2nu_dt2", "B"),
169
+ "DSI": ("δSi", "delta_Si", "dSi"),
170
+ }
171
+
172
+
173
+ def get_aliases(key: str) -> tuple[str, ...]:
174
+ """Return alias tuple for canonical ``key``."""
175
+
176
+ return ALIASES[key]
177
+
178
+
179
+ VF_PRIMARY = get_aliases("VF")[0]
180
+ THETA_PRIMARY = get_aliases("THETA")[0]
181
+ DNFR_PRIMARY = get_aliases("DNFR")[0]
182
+ EPI_PRIMARY = get_aliases("EPI")[0]
183
+ EPI_KIND_PRIMARY = get_aliases("EPI_KIND")[0]
184
+ SI_PRIMARY = get_aliases("SI")[0]
185
+ dEPI_PRIMARY = get_aliases("DEPI")[0]
186
+ D2EPI_PRIMARY = get_aliases("D2EPI")[0]
187
+ dVF_PRIMARY = get_aliases("DVF")[0]
188
+ D2VF_PRIMARY = get_aliases("D2VF")[0]
189
+ dSI_PRIMARY = get_aliases("DSI")[0]
190
+
191
+ __all__ = (
192
+ "CORE_DEFAULTS",
193
+ "INIT_DEFAULTS",
194
+ "REMESH_DEFAULTS",
195
+ "METRIC_DEFAULTS",
196
+ "SIGMA",
197
+ "TRACE",
198
+ "METRICS",
199
+ "GRAMMAR_CANON",
200
+ "COHERENCE",
201
+ "DIAGNOSIS",
202
+ "DEFAULTS",
203
+ "DEFAULT_SECTIONS",
204
+ "ALIASES",
205
+ "inject_defaults",
206
+ "merge_overrides",
207
+ "get_param",
208
+ "get_graph_param",
209
+ "get_aliases",
210
+ "VF_KEY",
211
+ "THETA_KEY",
212
+ "VF_PRIMARY",
213
+ "THETA_PRIMARY",
214
+ "DNFR_PRIMARY",
215
+ "EPI_PRIMARY",
216
+ "EPI_KIND_PRIMARY",
217
+ "SI_PRIMARY",
218
+ "dEPI_PRIMARY",
219
+ "D2EPI_PRIMARY",
220
+ "dVF_PRIMARY",
221
+ "D2VF_PRIMARY",
222
+ "dSI_PRIMARY",
223
+ "STATE_STABLE",
224
+ "STATE_TRANSITION",
225
+ "STATE_DISSONANT",
226
+ "CANONICAL_STATE_TOKENS",
227
+ "normalise_state_token",
228
+ )
@@ -0,0 +1,104 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from typing import Callable, TypeVar
5
+
6
+ from .core import CORE_DEFAULTS as CORE_DEFAULTS, REMESH_DEFAULTS as REMESH_DEFAULTS
7
+ from .init import INIT_DEFAULTS as INIT_DEFAULTS
8
+ from .metric import (
9
+ COHERENCE as COHERENCE,
10
+ DIAGNOSIS as DIAGNOSIS,
11
+ GRAMMAR_CANON as GRAMMAR_CANON,
12
+ METRICS as METRICS,
13
+ METRIC_DEFAULTS as METRIC_DEFAULTS,
14
+ SIGMA as SIGMA,
15
+ TRACE as TRACE,
16
+ )
17
+ from ..types import GraphLike, TNFRConfigValue
18
+
19
+ T = TypeVar("T")
20
+
21
+ __all__ = (
22
+ "CORE_DEFAULTS",
23
+ "INIT_DEFAULTS",
24
+ "REMESH_DEFAULTS",
25
+ "METRIC_DEFAULTS",
26
+ "SIGMA",
27
+ "TRACE",
28
+ "METRICS",
29
+ "GRAMMAR_CANON",
30
+ "COHERENCE",
31
+ "DIAGNOSIS",
32
+ "DEFAULTS",
33
+ "DEFAULT_SECTIONS",
34
+ "ALIASES",
35
+ "inject_defaults",
36
+ "merge_overrides",
37
+ "get_param",
38
+ "get_graph_param",
39
+ "get_aliases",
40
+ "VF_KEY",
41
+ "THETA_KEY",
42
+ "VF_PRIMARY",
43
+ "THETA_PRIMARY",
44
+ "DNFR_PRIMARY",
45
+ "EPI_PRIMARY",
46
+ "EPI_KIND_PRIMARY",
47
+ "SI_PRIMARY",
48
+ "dEPI_PRIMARY",
49
+ "D2EPI_PRIMARY",
50
+ "dVF_PRIMARY",
51
+ "D2VF_PRIMARY",
52
+ "dSI_PRIMARY",
53
+ "STATE_STABLE",
54
+ "STATE_TRANSITION",
55
+ "STATE_DISSONANT",
56
+ "CANONICAL_STATE_TOKENS",
57
+ "normalise_state_token",
58
+ )
59
+
60
+ ensure_node_offset_map: Callable[[GraphLike], None] | None
61
+ DEFAULT_SECTIONS: Mapping[str, Mapping[str, TNFRConfigValue]]
62
+ DEFAULTS: Mapping[str, TNFRConfigValue]
63
+ ALIASES: dict[str, tuple[str, ...]]
64
+ VF_KEY: str
65
+ THETA_KEY: str
66
+ VF_PRIMARY: str
67
+ THETA_PRIMARY: str
68
+ DNFR_PRIMARY: str
69
+ EPI_PRIMARY: str
70
+ EPI_KIND_PRIMARY: str
71
+ SI_PRIMARY: str
72
+ dEPI_PRIMARY: str
73
+ D2EPI_PRIMARY: str
74
+ dVF_PRIMARY: str
75
+ D2VF_PRIMARY: str
76
+ dSI_PRIMARY: str
77
+ STATE_STABLE: str
78
+ STATE_TRANSITION: str
79
+ STATE_DISSONANT: str
80
+ CANONICAL_STATE_TOKENS: frozenset[str]
81
+
82
+
83
+ def inject_defaults(
84
+ G: GraphLike,
85
+ defaults: Mapping[str, TNFRConfigValue] = ...,
86
+ override: bool = ...,
87
+ ) -> None: ...
88
+
89
+
90
+ def merge_overrides(G: GraphLike, **overrides: TNFRConfigValue) -> None: ...
91
+
92
+
93
+ def get_param(G: GraphLike, key: str) -> TNFRConfigValue: ...
94
+
95
+
96
+ def get_graph_param(
97
+ G: GraphLike, key: str, cast: Callable[[object], T] = ...
98
+ ) -> T | None: ...
99
+
100
+
101
+ def get_aliases(key: str) -> tuple[str, ...]: ...
102
+
103
+
104
+ def normalise_state_token(token: str) -> str: ...
tnfr/constants/core.py ADDED
@@ -0,0 +1,158 @@
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_WINDOW: 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
+ _core_defaults = asdict(CoreDefaults())
155
+ _remesh_defaults = asdict(RemeshDefaults())
156
+
157
+ CORE_DEFAULTS = MappingProxyType(_core_defaults)
158
+ REMESH_DEFAULTS = MappingProxyType(_remesh_defaults)
@@ -0,0 +1,17 @@
1
+ from typing import Any
2
+
3
+ __all__: Any
4
+
5
+ def __getattr__(name: str) -> Any: ...
6
+
7
+ CORE_DEFAULTS: Any
8
+ CoreDefaults: Any
9
+ Mapping: Any
10
+ MappingProxyType: Any
11
+ REMESH_DEFAULTS: Any
12
+ RemeshDefaults: Any
13
+ SELECTOR_THRESHOLD_DEFAULTS: Any
14
+ annotations: Any
15
+ asdict: Any
16
+ dataclass: Any
17
+ field: Any
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())
@@ -0,0 +1,12 @@
1
+ from typing import Any
2
+
3
+ __all__: Any
4
+
5
+ def __getattr__(name: str) -> Any: ...
6
+
7
+ INIT_DEFAULTS: Any
8
+ InitDefaults: Any
9
+ annotations: Any
10
+ asdict: Any
11
+ dataclass: Any
12
+ math: Any
@@ -0,0 +1,102 @@
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
+ "verbosity": "debug",
39
+ "history_key": "trace_meta",
40
+ }
41
+ )
42
+ METRICS: dict[str, Any] = field(
43
+ default_factory=lambda: {
44
+ "enabled": True,
45
+ "save_by_node": True,
46
+ "normalize_series": False,
47
+ "n_jobs": 1,
48
+ "verbosity": "debug",
49
+ }
50
+ )
51
+ GRAMMAR_CANON: dict[str, Any] = field(
52
+ default_factory=lambda: {
53
+ "enabled": True,
54
+ "zhir_requires_oz_window": 3,
55
+ "zhir_dnfr_min": 0.05,
56
+ "thol_min_len": 2,
57
+ "thol_max_len": 6,
58
+ "thol_close_dnfr": 0.15,
59
+ "si_high": 0.66,
60
+ }
61
+ )
62
+ COHERENCE: dict[str, Any] = field(
63
+ default_factory=lambda: {
64
+ "enabled": True,
65
+ "scope": "neighbors",
66
+ "weights": {"phase": 0.34, "epi": 0.33, "vf": 0.20, "si": 0.13},
67
+ "self_on_diag": True,
68
+ "store_mode": "sparse",
69
+ "threshold": 0.0,
70
+ "n_jobs": 1,
71
+ "history_key": "W_sparse",
72
+ "Wi_history_key": "W_i",
73
+ "stats_history_key": "W_stats",
74
+ }
75
+ )
76
+ DIAGNOSIS: dict[str, Any] = field(
77
+ default_factory=lambda: {
78
+ "enabled": True,
79
+ "window": 16,
80
+ "history_key": "nodal_diag",
81
+ "stable": {"Rloc_hi": 0.80, "dnfr_lo": 0.20, "persist": 3},
82
+ "dissonance": {"Rloc_lo": 0.40, "dnfr_hi": 0.50, "persist": 3},
83
+ "transition": {"persist": 2},
84
+ "compute_symmetry": True,
85
+ "include_typology": False,
86
+ "advice": {
87
+ "stable": ["Coherence", "Coupling", "Resonance"],
88
+ "transition": ["Transition", "Resonance", "Self-organisation"],
89
+ "dissonant": ["Silence", "Contraction", "Mutation"],
90
+ },
91
+ }
92
+ )
93
+
94
+
95
+ METRIC_DEFAULTS = asdict(MetricDefaults())
96
+
97
+ SIGMA = MappingProxyType(METRIC_DEFAULTS["SIGMA"])
98
+ TRACE = MappingProxyType(METRIC_DEFAULTS["TRACE"])
99
+ METRICS = MappingProxyType(METRIC_DEFAULTS["METRICS"])
100
+ GRAMMAR_CANON = MappingProxyType(METRIC_DEFAULTS["GRAMMAR_CANON"])
101
+ COHERENCE = MappingProxyType(METRIC_DEFAULTS["COHERENCE"])
102
+ DIAGNOSIS = MappingProxyType(METRIC_DEFAULTS["DIAGNOSIS"])
@@ -0,0 +1,19 @@
1
+ from typing import Any
2
+
3
+ __all__: Any
4
+
5
+ def __getattr__(name: str) -> Any: ...
6
+
7
+ COHERENCE: Any
8
+ DIAGNOSIS: Any
9
+ GRAMMAR_CANON: Any
10
+ METRICS: Any
11
+ METRIC_DEFAULTS: Any
12
+ MappingProxyType: Any
13
+ MetricDefaults: Any
14
+ SIGMA: Any
15
+ TRACE: Any
16
+ annotations: Any
17
+ asdict: Any
18
+ dataclass: Any
19
+ field: Any
@@ -0,0 +1,16 @@
1
+ """Backward compatibility shim for glyph constants."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import warnings
6
+
7
+ from .config.constants import * # noqa: F401,F403 - re-export legacy API
8
+ from .config.constants import __all__ as _CONFIG_ALL
9
+
10
+ warnings.warn(
11
+ "'tnfr.constants_glyphs' is deprecated; use 'tnfr.config.constants' instead",
12
+ DeprecationWarning,
13
+ stacklevel=2,
14
+ )
15
+
16
+ __all__ = _CONFIG_ALL