tnfr 3.0.3__py3-none-any.whl → 8.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.

Files changed (360) hide show
  1. tnfr/__init__.py +375 -56
  2. tnfr/__init__.pyi +33 -0
  3. tnfr/_compat.py +10 -0
  4. tnfr/_generated_version.py +34 -0
  5. tnfr/_version.py +49 -0
  6. tnfr/_version.pyi +7 -0
  7. tnfr/alias.py +723 -0
  8. tnfr/alias.pyi +108 -0
  9. tnfr/backends/__init__.py +354 -0
  10. tnfr/backends/jax_backend.py +173 -0
  11. tnfr/backends/numpy_backend.py +238 -0
  12. tnfr/backends/optimized_numpy.py +420 -0
  13. tnfr/backends/torch_backend.py +408 -0
  14. tnfr/cache.py +171 -0
  15. tnfr/cache.pyi +13 -0
  16. tnfr/cli/__init__.py +110 -0
  17. tnfr/cli/__init__.pyi +26 -0
  18. tnfr/cli/arguments.py +489 -0
  19. tnfr/cli/arguments.pyi +29 -0
  20. tnfr/cli/execution.py +914 -0
  21. tnfr/cli/execution.pyi +70 -0
  22. tnfr/cli/interactive_validator.py +614 -0
  23. tnfr/cli/utils.py +51 -0
  24. tnfr/cli/utils.pyi +7 -0
  25. tnfr/cli/validate.py +236 -0
  26. tnfr/compat/__init__.py +85 -0
  27. tnfr/compat/dataclass.py +136 -0
  28. tnfr/compat/jsonschema_stub.py +61 -0
  29. tnfr/compat/matplotlib_stub.py +73 -0
  30. tnfr/compat/numpy_stub.py +155 -0
  31. tnfr/config/__init__.py +224 -0
  32. tnfr/config/__init__.pyi +10 -0
  33. tnfr/config/constants.py +104 -0
  34. tnfr/config/constants.pyi +12 -0
  35. tnfr/config/defaults.py +54 -0
  36. tnfr/config/defaults_core.py +212 -0
  37. tnfr/config/defaults_init.py +33 -0
  38. tnfr/config/defaults_metric.py +104 -0
  39. tnfr/config/feature_flags.py +81 -0
  40. tnfr/config/feature_flags.pyi +16 -0
  41. tnfr/config/glyph_constants.py +31 -0
  42. tnfr/config/init.py +77 -0
  43. tnfr/config/init.pyi +8 -0
  44. tnfr/config/operator_names.py +254 -0
  45. tnfr/config/operator_names.pyi +36 -0
  46. tnfr/config/physics_derivation.py +354 -0
  47. tnfr/config/presets.py +83 -0
  48. tnfr/config/presets.pyi +7 -0
  49. tnfr/config/security.py +927 -0
  50. tnfr/config/thresholds.py +114 -0
  51. tnfr/config/tnfr_config.py +498 -0
  52. tnfr/constants/__init__.py +92 -0
  53. tnfr/constants/__init__.pyi +92 -0
  54. tnfr/constants/aliases.py +33 -0
  55. tnfr/constants/aliases.pyi +27 -0
  56. tnfr/constants/init.py +33 -0
  57. tnfr/constants/init.pyi +12 -0
  58. tnfr/constants/metric.py +104 -0
  59. tnfr/constants/metric.pyi +19 -0
  60. tnfr/core/__init__.py +33 -0
  61. tnfr/core/container.py +226 -0
  62. tnfr/core/default_implementations.py +329 -0
  63. tnfr/core/interfaces.py +279 -0
  64. tnfr/dynamics/__init__.py +238 -0
  65. tnfr/dynamics/__init__.pyi +83 -0
  66. tnfr/dynamics/adaptation.py +267 -0
  67. tnfr/dynamics/adaptation.pyi +7 -0
  68. tnfr/dynamics/adaptive_sequences.py +189 -0
  69. tnfr/dynamics/adaptive_sequences.pyi +14 -0
  70. tnfr/dynamics/aliases.py +23 -0
  71. tnfr/dynamics/aliases.pyi +19 -0
  72. tnfr/dynamics/bifurcation.py +232 -0
  73. tnfr/dynamics/canonical.py +229 -0
  74. tnfr/dynamics/canonical.pyi +48 -0
  75. tnfr/dynamics/coordination.py +385 -0
  76. tnfr/dynamics/coordination.pyi +25 -0
  77. tnfr/dynamics/dnfr.py +3034 -0
  78. tnfr/dynamics/dnfr.pyi +26 -0
  79. tnfr/dynamics/dynamic_limits.py +225 -0
  80. tnfr/dynamics/feedback.py +252 -0
  81. tnfr/dynamics/feedback.pyi +24 -0
  82. tnfr/dynamics/fused_dnfr.py +454 -0
  83. tnfr/dynamics/homeostasis.py +157 -0
  84. tnfr/dynamics/homeostasis.pyi +14 -0
  85. tnfr/dynamics/integrators.py +661 -0
  86. tnfr/dynamics/integrators.pyi +36 -0
  87. tnfr/dynamics/learning.py +310 -0
  88. tnfr/dynamics/learning.pyi +33 -0
  89. tnfr/dynamics/metabolism.py +254 -0
  90. tnfr/dynamics/nbody.py +796 -0
  91. tnfr/dynamics/nbody_tnfr.py +783 -0
  92. tnfr/dynamics/propagation.py +326 -0
  93. tnfr/dynamics/runtime.py +908 -0
  94. tnfr/dynamics/runtime.pyi +77 -0
  95. tnfr/dynamics/sampling.py +36 -0
  96. tnfr/dynamics/sampling.pyi +7 -0
  97. tnfr/dynamics/selectors.py +711 -0
  98. tnfr/dynamics/selectors.pyi +85 -0
  99. tnfr/dynamics/structural_clip.py +207 -0
  100. tnfr/errors/__init__.py +37 -0
  101. tnfr/errors/contextual.py +492 -0
  102. tnfr/execution.py +223 -0
  103. tnfr/execution.pyi +45 -0
  104. tnfr/extensions/__init__.py +205 -0
  105. tnfr/extensions/__init__.pyi +18 -0
  106. tnfr/extensions/base.py +173 -0
  107. tnfr/extensions/base.pyi +35 -0
  108. tnfr/extensions/business/__init__.py +71 -0
  109. tnfr/extensions/business/__init__.pyi +11 -0
  110. tnfr/extensions/business/cookbook.py +88 -0
  111. tnfr/extensions/business/cookbook.pyi +8 -0
  112. tnfr/extensions/business/health_analyzers.py +202 -0
  113. tnfr/extensions/business/health_analyzers.pyi +9 -0
  114. tnfr/extensions/business/patterns.py +183 -0
  115. tnfr/extensions/business/patterns.pyi +8 -0
  116. tnfr/extensions/medical/__init__.py +73 -0
  117. tnfr/extensions/medical/__init__.pyi +11 -0
  118. tnfr/extensions/medical/cookbook.py +88 -0
  119. tnfr/extensions/medical/cookbook.pyi +8 -0
  120. tnfr/extensions/medical/health_analyzers.py +181 -0
  121. tnfr/extensions/medical/health_analyzers.pyi +9 -0
  122. tnfr/extensions/medical/patterns.py +163 -0
  123. tnfr/extensions/medical/patterns.pyi +8 -0
  124. tnfr/flatten.py +262 -0
  125. tnfr/flatten.pyi +21 -0
  126. tnfr/gamma.py +354 -0
  127. tnfr/gamma.pyi +36 -0
  128. tnfr/glyph_history.py +377 -0
  129. tnfr/glyph_history.pyi +35 -0
  130. tnfr/glyph_runtime.py +19 -0
  131. tnfr/glyph_runtime.pyi +8 -0
  132. tnfr/immutable.py +218 -0
  133. tnfr/immutable.pyi +36 -0
  134. tnfr/initialization.py +203 -0
  135. tnfr/initialization.pyi +65 -0
  136. tnfr/io.py +10 -0
  137. tnfr/io.pyi +13 -0
  138. tnfr/locking.py +37 -0
  139. tnfr/locking.pyi +7 -0
  140. tnfr/mathematics/__init__.py +79 -0
  141. tnfr/mathematics/backend.py +453 -0
  142. tnfr/mathematics/backend.pyi +99 -0
  143. tnfr/mathematics/dynamics.py +408 -0
  144. tnfr/mathematics/dynamics.pyi +90 -0
  145. tnfr/mathematics/epi.py +391 -0
  146. tnfr/mathematics/epi.pyi +65 -0
  147. tnfr/mathematics/generators.py +242 -0
  148. tnfr/mathematics/generators.pyi +29 -0
  149. tnfr/mathematics/metrics.py +119 -0
  150. tnfr/mathematics/metrics.pyi +16 -0
  151. tnfr/mathematics/operators.py +239 -0
  152. tnfr/mathematics/operators.pyi +59 -0
  153. tnfr/mathematics/operators_factory.py +124 -0
  154. tnfr/mathematics/operators_factory.pyi +11 -0
  155. tnfr/mathematics/projection.py +87 -0
  156. tnfr/mathematics/projection.pyi +33 -0
  157. tnfr/mathematics/runtime.py +182 -0
  158. tnfr/mathematics/runtime.pyi +64 -0
  159. tnfr/mathematics/spaces.py +256 -0
  160. tnfr/mathematics/spaces.pyi +83 -0
  161. tnfr/mathematics/transforms.py +305 -0
  162. tnfr/mathematics/transforms.pyi +62 -0
  163. tnfr/metrics/__init__.py +79 -0
  164. tnfr/metrics/__init__.pyi +20 -0
  165. tnfr/metrics/buffer_cache.py +163 -0
  166. tnfr/metrics/buffer_cache.pyi +24 -0
  167. tnfr/metrics/cache_utils.py +214 -0
  168. tnfr/metrics/coherence.py +2009 -0
  169. tnfr/metrics/coherence.pyi +129 -0
  170. tnfr/metrics/common.py +158 -0
  171. tnfr/metrics/common.pyi +35 -0
  172. tnfr/metrics/core.py +316 -0
  173. tnfr/metrics/core.pyi +13 -0
  174. tnfr/metrics/diagnosis.py +833 -0
  175. tnfr/metrics/diagnosis.pyi +86 -0
  176. tnfr/metrics/emergence.py +245 -0
  177. tnfr/metrics/export.py +179 -0
  178. tnfr/metrics/export.pyi +7 -0
  179. tnfr/metrics/glyph_timing.py +379 -0
  180. tnfr/metrics/glyph_timing.pyi +81 -0
  181. tnfr/metrics/learning_metrics.py +280 -0
  182. tnfr/metrics/learning_metrics.pyi +21 -0
  183. tnfr/metrics/phase_coherence.py +351 -0
  184. tnfr/metrics/phase_compatibility.py +349 -0
  185. tnfr/metrics/reporting.py +183 -0
  186. tnfr/metrics/reporting.pyi +25 -0
  187. tnfr/metrics/sense_index.py +1203 -0
  188. tnfr/metrics/sense_index.pyi +9 -0
  189. tnfr/metrics/trig.py +373 -0
  190. tnfr/metrics/trig.pyi +13 -0
  191. tnfr/metrics/trig_cache.py +233 -0
  192. tnfr/metrics/trig_cache.pyi +10 -0
  193. tnfr/multiscale/__init__.py +32 -0
  194. tnfr/multiscale/hierarchical.py +517 -0
  195. tnfr/node.py +763 -0
  196. tnfr/node.pyi +139 -0
  197. tnfr/observers.py +255 -130
  198. tnfr/observers.pyi +31 -0
  199. tnfr/ontosim.py +144 -137
  200. tnfr/ontosim.pyi +28 -0
  201. tnfr/operators/__init__.py +1672 -0
  202. tnfr/operators/__init__.pyi +31 -0
  203. tnfr/operators/algebra.py +277 -0
  204. tnfr/operators/canonical_patterns.py +420 -0
  205. tnfr/operators/cascade.py +267 -0
  206. tnfr/operators/cycle_detection.py +358 -0
  207. tnfr/operators/definitions.py +4108 -0
  208. tnfr/operators/definitions.pyi +78 -0
  209. tnfr/operators/grammar.py +1164 -0
  210. tnfr/operators/grammar.pyi +140 -0
  211. tnfr/operators/hamiltonian.py +710 -0
  212. tnfr/operators/health_analyzer.py +809 -0
  213. tnfr/operators/jitter.py +272 -0
  214. tnfr/operators/jitter.pyi +11 -0
  215. tnfr/operators/lifecycle.py +314 -0
  216. tnfr/operators/metabolism.py +618 -0
  217. tnfr/operators/metrics.py +2138 -0
  218. tnfr/operators/network_analysis/__init__.py +27 -0
  219. tnfr/operators/network_analysis/source_detection.py +186 -0
  220. tnfr/operators/nodal_equation.py +395 -0
  221. tnfr/operators/pattern_detection.py +660 -0
  222. tnfr/operators/patterns.py +669 -0
  223. tnfr/operators/postconditions/__init__.py +38 -0
  224. tnfr/operators/postconditions/mutation.py +236 -0
  225. tnfr/operators/preconditions/__init__.py +1226 -0
  226. tnfr/operators/preconditions/coherence.py +305 -0
  227. tnfr/operators/preconditions/dissonance.py +236 -0
  228. tnfr/operators/preconditions/emission.py +128 -0
  229. tnfr/operators/preconditions/mutation.py +580 -0
  230. tnfr/operators/preconditions/reception.py +125 -0
  231. tnfr/operators/preconditions/resonance.py +364 -0
  232. tnfr/operators/registry.py +74 -0
  233. tnfr/operators/registry.pyi +9 -0
  234. tnfr/operators/remesh.py +1809 -0
  235. tnfr/operators/remesh.pyi +26 -0
  236. tnfr/operators/structural_units.py +268 -0
  237. tnfr/operators/unified_grammar.py +105 -0
  238. tnfr/parallel/__init__.py +54 -0
  239. tnfr/parallel/auto_scaler.py +234 -0
  240. tnfr/parallel/distributed.py +384 -0
  241. tnfr/parallel/engine.py +238 -0
  242. tnfr/parallel/gpu_engine.py +420 -0
  243. tnfr/parallel/monitoring.py +248 -0
  244. tnfr/parallel/partitioner.py +459 -0
  245. tnfr/py.typed +0 -0
  246. tnfr/recipes/__init__.py +22 -0
  247. tnfr/recipes/cookbook.py +743 -0
  248. tnfr/rng.py +178 -0
  249. tnfr/rng.pyi +26 -0
  250. tnfr/schemas/__init__.py +8 -0
  251. tnfr/schemas/grammar.json +94 -0
  252. tnfr/sdk/__init__.py +107 -0
  253. tnfr/sdk/__init__.pyi +19 -0
  254. tnfr/sdk/adaptive_system.py +173 -0
  255. tnfr/sdk/adaptive_system.pyi +21 -0
  256. tnfr/sdk/builders.py +370 -0
  257. tnfr/sdk/builders.pyi +51 -0
  258. tnfr/sdk/fluent.py +1121 -0
  259. tnfr/sdk/fluent.pyi +74 -0
  260. tnfr/sdk/templates.py +342 -0
  261. tnfr/sdk/templates.pyi +41 -0
  262. tnfr/sdk/utils.py +341 -0
  263. tnfr/secure_config.py +46 -0
  264. tnfr/security/__init__.py +70 -0
  265. tnfr/security/database.py +514 -0
  266. tnfr/security/subprocess.py +503 -0
  267. tnfr/security/validation.py +290 -0
  268. tnfr/selector.py +247 -0
  269. tnfr/selector.pyi +19 -0
  270. tnfr/sense.py +378 -0
  271. tnfr/sense.pyi +23 -0
  272. tnfr/services/__init__.py +17 -0
  273. tnfr/services/orchestrator.py +325 -0
  274. tnfr/sparse/__init__.py +39 -0
  275. tnfr/sparse/representations.py +492 -0
  276. tnfr/structural.py +705 -0
  277. tnfr/structural.pyi +83 -0
  278. tnfr/telemetry/__init__.py +35 -0
  279. tnfr/telemetry/cache_metrics.py +226 -0
  280. tnfr/telemetry/cache_metrics.pyi +64 -0
  281. tnfr/telemetry/nu_f.py +422 -0
  282. tnfr/telemetry/nu_f.pyi +108 -0
  283. tnfr/telemetry/verbosity.py +36 -0
  284. tnfr/telemetry/verbosity.pyi +15 -0
  285. tnfr/tokens.py +58 -0
  286. tnfr/tokens.pyi +36 -0
  287. tnfr/tools/__init__.py +20 -0
  288. tnfr/tools/domain_templates.py +478 -0
  289. tnfr/tools/sequence_generator.py +846 -0
  290. tnfr/topology/__init__.py +13 -0
  291. tnfr/topology/asymmetry.py +151 -0
  292. tnfr/trace.py +543 -0
  293. tnfr/trace.pyi +42 -0
  294. tnfr/tutorials/__init__.py +38 -0
  295. tnfr/tutorials/autonomous_evolution.py +285 -0
  296. tnfr/tutorials/interactive.py +1576 -0
  297. tnfr/tutorials/structural_metabolism.py +238 -0
  298. tnfr/types.py +775 -0
  299. tnfr/types.pyi +357 -0
  300. tnfr/units.py +68 -0
  301. tnfr/units.pyi +13 -0
  302. tnfr/utils/__init__.py +282 -0
  303. tnfr/utils/__init__.pyi +215 -0
  304. tnfr/utils/cache.py +4223 -0
  305. tnfr/utils/cache.pyi +470 -0
  306. tnfr/utils/callbacks.py +375 -0
  307. tnfr/utils/callbacks.pyi +49 -0
  308. tnfr/utils/chunks.py +108 -0
  309. tnfr/utils/chunks.pyi +22 -0
  310. tnfr/utils/data.py +428 -0
  311. tnfr/utils/data.pyi +74 -0
  312. tnfr/utils/graph.py +85 -0
  313. tnfr/utils/graph.pyi +10 -0
  314. tnfr/utils/init.py +821 -0
  315. tnfr/utils/init.pyi +80 -0
  316. tnfr/utils/io.py +559 -0
  317. tnfr/utils/io.pyi +66 -0
  318. tnfr/utils/numeric.py +114 -0
  319. tnfr/utils/numeric.pyi +21 -0
  320. tnfr/validation/__init__.py +257 -0
  321. tnfr/validation/__init__.pyi +85 -0
  322. tnfr/validation/compatibility.py +460 -0
  323. tnfr/validation/compatibility.pyi +6 -0
  324. tnfr/validation/config.py +73 -0
  325. tnfr/validation/graph.py +139 -0
  326. tnfr/validation/graph.pyi +18 -0
  327. tnfr/validation/input_validation.py +755 -0
  328. tnfr/validation/invariants.py +712 -0
  329. tnfr/validation/rules.py +253 -0
  330. tnfr/validation/rules.pyi +44 -0
  331. tnfr/validation/runtime.py +279 -0
  332. tnfr/validation/runtime.pyi +28 -0
  333. tnfr/validation/sequence_validator.py +162 -0
  334. tnfr/validation/soft_filters.py +170 -0
  335. tnfr/validation/soft_filters.pyi +32 -0
  336. tnfr/validation/spectral.py +164 -0
  337. tnfr/validation/spectral.pyi +42 -0
  338. tnfr/validation/validator.py +1266 -0
  339. tnfr/validation/window.py +39 -0
  340. tnfr/validation/window.pyi +1 -0
  341. tnfr/visualization/__init__.py +98 -0
  342. tnfr/visualization/cascade_viz.py +256 -0
  343. tnfr/visualization/hierarchy.py +284 -0
  344. tnfr/visualization/sequence_plotter.py +784 -0
  345. tnfr/viz/__init__.py +60 -0
  346. tnfr/viz/matplotlib.py +278 -0
  347. tnfr/viz/matplotlib.pyi +35 -0
  348. tnfr-8.5.0.dist-info/METADATA +573 -0
  349. tnfr-8.5.0.dist-info/RECORD +353 -0
  350. tnfr-8.5.0.dist-info/entry_points.txt +3 -0
  351. tnfr-3.0.3.dist-info/licenses/LICENSE.txt → tnfr-8.5.0.dist-info/licenses/LICENSE.md +1 -1
  352. tnfr/constants.py +0 -183
  353. tnfr/dynamics.py +0 -543
  354. tnfr/helpers.py +0 -198
  355. tnfr/main.py +0 -37
  356. tnfr/operators.py +0 -296
  357. tnfr-3.0.3.dist-info/METADATA +0 -35
  358. tnfr-3.0.3.dist-info/RECORD +0 -13
  359. {tnfr-3.0.3.dist-info → tnfr-8.5.0.dist-info}/WHEEL +0 -0
  360. {tnfr-3.0.3.dist-info → tnfr-8.5.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,272 @@
1
+ """Jitter operators for reproducible phase perturbations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import threading
6
+ from typing import TYPE_CHECKING, Any, cast
7
+
8
+ from ..rng import base_seed, cache_enabled
9
+ from ..rng import clear_rng_cache as _clear_rng_cache
10
+ from ..rng import (
11
+ make_rng,
12
+ seed_hash,
13
+ )
14
+ from ..types import NodeId, TNFRGraph
15
+ from ..utils import (
16
+ CacheManager,
17
+ InstrumentedLRUCache,
18
+ ScopedCounterCache,
19
+ build_cache_manager,
20
+ ensure_node_offset_map,
21
+ get_nodenx,
22
+ )
23
+
24
+ if TYPE_CHECKING: # pragma: no cover - type checking only
25
+ from ..node import NodeProtocol
26
+
27
+ # Guarded by the cache lock to ensure thread-safe access. ``seq`` stores
28
+ # per-scope jitter sequence counters in an instrumented LRU cache bounded to avoid
29
+ # unbounded memory usage.
30
+ _JITTER_MAX_ENTRIES = 1024
31
+
32
+
33
+ class JitterCache:
34
+ """Container for jitter-related caches."""
35
+
36
+ def __init__(
37
+ self,
38
+ max_entries: int = _JITTER_MAX_ENTRIES,
39
+ *,
40
+ manager: CacheManager | None = None,
41
+ ) -> None:
42
+ self._manager = manager or build_cache_manager()
43
+ if not self._manager.has_override("scoped_counter:jitter"):
44
+ self._manager.configure(
45
+ overrides={"scoped_counter:jitter": int(max_entries)}
46
+ )
47
+ self._sequence = ScopedCounterCache(
48
+ "jitter",
49
+ max_entries=None,
50
+ manager=self._manager,
51
+ default_max_entries=int(max_entries),
52
+ )
53
+ self._settings_key = "jitter_settings"
54
+ self._manager.register(
55
+ self._settings_key,
56
+ lambda: {"max_entries": self._sequence.max_entries},
57
+ reset=self._reset_settings,
58
+ )
59
+
60
+ def _reset_settings(self, settings: dict[str, Any] | None) -> dict[str, Any]:
61
+ return {"max_entries": self._sequence.max_entries}
62
+
63
+ def _refresh_settings(self) -> None:
64
+ self._manager.update(
65
+ self._settings_key,
66
+ lambda _: {"max_entries": self._sequence.max_entries},
67
+ )
68
+
69
+ @property
70
+ def manager(self) -> CacheManager:
71
+ """Expose the cache manager backing this cache."""
72
+
73
+ return self._manager
74
+
75
+ @property
76
+ def seq(self) -> InstrumentedLRUCache[tuple[int, int], int]:
77
+ """Expose the instrumented sequence cache for tests and diagnostics."""
78
+
79
+ return self._sequence.cache
80
+
81
+ @property
82
+ def lock(self) -> threading.Lock | threading.RLock:
83
+ """Return the lock protecting the sequence cache."""
84
+
85
+ return self._sequence.lock
86
+
87
+ @property
88
+ def max_entries(self) -> int:
89
+ """Return the maximum number of cached jitter sequences."""
90
+
91
+ return self._sequence.max_entries
92
+
93
+ @max_entries.setter
94
+ def max_entries(self, value: int) -> None:
95
+ """Set the maximum number of cached jitter sequences."""
96
+
97
+ self._sequence.configure(max_entries=int(value))
98
+ self._refresh_settings()
99
+
100
+ @property
101
+ def settings(self) -> dict[str, Any]:
102
+ """Return jitter cache settings stored on the manager."""
103
+
104
+ return cast(dict[str, Any], self._manager.get(self._settings_key))
105
+
106
+ def setup(self, force: bool = False, max_entries: int | None = None) -> None:
107
+ """Ensure jitter cache matches the configured size."""
108
+
109
+ self._sequence.configure(force=force, max_entries=max_entries)
110
+ self._refresh_settings()
111
+
112
+ def clear(self) -> None:
113
+ """Clear cached RNGs and jitter state."""
114
+
115
+ _clear_rng_cache()
116
+ self._sequence.clear()
117
+ self._manager.clear(self._settings_key)
118
+
119
+ def bump(self, key: tuple[int, int]) -> int:
120
+ """Return current jitter sequence counter for ``key`` and increment it."""
121
+
122
+ return self._sequence.bump(key)
123
+
124
+
125
+ class JitterCacheManager:
126
+ """Manager exposing the jitter cache without global reassignment."""
127
+
128
+ def __init__(
129
+ self,
130
+ cache: JitterCache | None = None,
131
+ *,
132
+ manager: CacheManager | None = None,
133
+ ) -> None:
134
+ if cache is not None:
135
+ self.cache = cache
136
+ self._manager = cache.manager
137
+ else:
138
+ self._manager = manager or build_cache_manager()
139
+ self.cache = JitterCache(manager=self._manager)
140
+
141
+ # Convenience passthrough properties
142
+ @property
143
+ def seq(self) -> InstrumentedLRUCache[tuple[int, int], int]:
144
+ """Expose the underlying instrumented jitter sequence cache."""
145
+
146
+ return self.cache.seq
147
+
148
+ @property
149
+ def settings(self) -> dict[str, Any]:
150
+ """Return persisted jitter cache configuration."""
151
+
152
+ return self.cache.settings
153
+
154
+ @property
155
+ def lock(self) -> threading.Lock | threading.RLock:
156
+ """Return the lock associated with the jitter cache."""
157
+
158
+ return self.cache.lock
159
+
160
+ @property
161
+ def max_entries(self) -> int:
162
+ """Return the maximum number of cached jitter entries."""
163
+
164
+ return self.cache.max_entries
165
+
166
+ @max_entries.setter
167
+ def max_entries(self, value: int) -> None:
168
+ """Set the maximum number of cached jitter entries."""
169
+
170
+ self.cache.max_entries = value
171
+
172
+ def setup(self, force: bool = False, max_entries: int | None = None) -> None:
173
+ """Ensure jitter cache matches the configured size.
174
+
175
+ ``max_entries`` may be provided to explicitly resize the cache.
176
+ When omitted the existing ``cache.max_entries`` is preserved.
177
+ """
178
+
179
+ if max_entries is not None:
180
+ self.cache.setup(force=True, max_entries=max_entries)
181
+ else:
182
+ self.cache.setup(force=force)
183
+
184
+ def clear(self) -> None:
185
+ """Clear cached RNGs and jitter state."""
186
+
187
+ self.cache.clear()
188
+
189
+ def bump(self, key: tuple[int, int]) -> int:
190
+ """Return and increment the jitter sequence counter for ``key``."""
191
+
192
+ return self.cache.bump(key)
193
+
194
+
195
+ # Lazy manager instance
196
+ _JITTER_MANAGER: JitterCacheManager | None = None
197
+
198
+
199
+ def get_jitter_manager() -> JitterCacheManager:
200
+ """Return the singleton jitter manager, initializing on first use."""
201
+ global _JITTER_MANAGER
202
+ if _JITTER_MANAGER is None:
203
+ _JITTER_MANAGER = JitterCacheManager()
204
+ _JITTER_MANAGER.setup(force=True)
205
+ return _JITTER_MANAGER
206
+
207
+
208
+ def reset_jitter_manager() -> None:
209
+ """Reset the global jitter manager (useful for tests)."""
210
+ global _JITTER_MANAGER
211
+ if _JITTER_MANAGER is not None:
212
+ _JITTER_MANAGER.clear()
213
+ _JITTER_MANAGER = None
214
+
215
+
216
+ def _node_offset(G: TNFRGraph, n: NodeId) -> int:
217
+ """Deterministic node index used for jitter seeds."""
218
+ mapping = ensure_node_offset_map(G)
219
+ return int(mapping.get(n, 0))
220
+
221
+
222
+ def _resolve_jitter_seed(node: NodeProtocol) -> tuple[int, int]:
223
+ node_nx_type = get_nodenx()
224
+ if node_nx_type is None:
225
+ raise ImportError("NodeNX is unavailable")
226
+ if isinstance(node, node_nx_type):
227
+ graph = cast(TNFRGraph, getattr(node, "G"))
228
+ node_id = cast(NodeId, getattr(node, "n"))
229
+ return _node_offset(graph, node_id), id(graph)
230
+ uid = getattr(node, "_noise_uid", None)
231
+ if uid is None:
232
+ uid = id(node)
233
+ setattr(node, "_noise_uid", uid)
234
+ graph = cast(TNFRGraph | None, getattr(node, "G", None))
235
+ scope = graph if graph is not None else node
236
+ return int(uid), id(scope)
237
+
238
+
239
+ def random_jitter(
240
+ node: NodeProtocol,
241
+ amplitude: float,
242
+ ) -> float:
243
+ """Return deterministic noise in ``[-amplitude, amplitude]`` for ``node``.
244
+
245
+ The per-node jitter sequences are tracked using the global manager
246
+ returned by :func:`get_jitter_manager`.
247
+ """
248
+ if amplitude < 0:
249
+ raise ValueError("amplitude must be positive")
250
+ if amplitude == 0:
251
+ return 0.0
252
+
253
+ seed_root = base_seed(node.G)
254
+ seed_key, scope_id = _resolve_jitter_seed(node)
255
+
256
+ cache_key = (seed_root, scope_id, seed_key)
257
+ seq = 0
258
+ if cache_enabled(node.G):
259
+ manager = get_jitter_manager()
260
+ seq = manager.bump(cache_key)
261
+ seed = seed_hash(seed_root, scope_id)
262
+ rng = make_rng(seed, seed_key + seq, node.G)
263
+ return rng.uniform(-amplitude, amplitude)
264
+
265
+
266
+ __all__ = [
267
+ "JitterCache",
268
+ "JitterCacheManager",
269
+ "get_jitter_manager",
270
+ "reset_jitter_manager",
271
+ "random_jitter",
272
+ ]
@@ -0,0 +1,11 @@
1
+ from typing import Any
2
+
3
+ __all__: Any
4
+
5
+ def __getattr__(name: str) -> Any: ...
6
+
7
+ JitterCache: Any
8
+ JitterCacheManager: Any
9
+ get_jitter_manager: Any
10
+ random_jitter: Any
11
+ reset_jitter_manager: Any
@@ -0,0 +1,314 @@
1
+ """Node lifecycle management for TNFR canonical theory.
2
+
3
+ According to TNFR theory (El pulso que nos atraviesa, p.44), nodes follow
4
+ a canonical lifecycle:
5
+
6
+ 1. Activation - Node emerges through sufficient reorganization
7
+ 2. Stabilization - Finds coherent phase and form
8
+ 3. Propagation - Reorganizes its network environment
9
+ 4. Mutation - Transforms through dissonance
10
+ 5. Collapse - Loses phase/frequency and dissolves
11
+
12
+ This module provides lifecycle state tracking and transition validation.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from enum import Enum
18
+ from typing import TYPE_CHECKING, Any
19
+
20
+ if TYPE_CHECKING:
21
+ from ..types import NodeId, TNFRGraph
22
+
23
+ from ..alias import get_attr
24
+ from ..constants.aliases import ALIAS_EPI, ALIAS_VF, ALIAS_DNFR, ALIAS_THETA
25
+
26
+ __all__ = [
27
+ "LifecycleState",
28
+ "CollapseReason",
29
+ "get_lifecycle_state",
30
+ "check_collapse_conditions",
31
+ "should_collapse",
32
+ ]
33
+
34
+ # Default thresholds for lifecycle state determination
35
+ DEFAULT_MIN_PHASE_COUPLING = 0.1 # Minimum phase coupling before decoupling collapse
36
+
37
+
38
+ class LifecycleState(Enum):
39
+ """Canonical TNFR node lifecycle states.
40
+
41
+ These states correspond to the fundamental phases of node existence
42
+ in the Resonant Fractal Nature paradigm.
43
+ """
44
+
45
+ DORMANT = "dormant"
46
+ """Node exists but has minimal structural frequency (νf < activation_threshold)."""
47
+
48
+ ACTIVATION = "activation"
49
+ """Node is emerging with increasing νf and ΔNFR."""
50
+
51
+ STABILIZATION = "stabilization"
52
+ """Node is finding coherent form (high C(t), decreasing |ΔNFR|)."""
53
+
54
+ PROPAGATION = "propagation"
55
+ """Node is reorganizing its environment (high phase coupling)."""
56
+
57
+ MUTATION = "mutation"
58
+ """Node is undergoing phase transformation (high |ΔNFR|, phase shifts)."""
59
+
60
+ COLLAPSING = "collapsing"
61
+ """Node is losing coherence and approaching dissolution."""
62
+
63
+ COLLAPSED = "collapsed"
64
+ """Node has dissolved (νf → 0 or extreme dissonance)."""
65
+
66
+
67
+ class CollapseReason(Enum):
68
+ """Canonical reasons for node collapse in TNFR.
69
+
70
+ These correspond to the fundamental ways structural coherence can fail.
71
+ """
72
+
73
+ FREQUENCY_FAILURE = "frequency_failure"
74
+ """Structural frequency dropped below collapse threshold (νf → 0)."""
75
+
76
+ EXTREME_DISSONANCE = "extreme_dissonance"
77
+ """ΔNFR magnitude exceeded bifurcation threshold."""
78
+
79
+ NETWORK_DECOUPLING = "network_decoupling"
80
+ """Phase coherence with network dropped below coupling threshold."""
81
+
82
+ EPI_DISSOLUTION = "epi_dissolution"
83
+ """Primary Information Structure lost coherence (EPI → 0)."""
84
+
85
+
86
+ def _get_node_attr(
87
+ G: TNFRGraph, node: NodeId, aliases: tuple[str, ...], default: float = 0.0
88
+ ) -> float:
89
+ """Get node attribute using alias fallback."""
90
+ return float(get_attr(G.nodes[node], aliases, default))
91
+
92
+
93
+ def get_lifecycle_state(
94
+ G: TNFRGraph,
95
+ node: NodeId,
96
+ *,
97
+ config: dict[str, Any] | None = None,
98
+ ) -> LifecycleState:
99
+ """Determine current lifecycle state of a node.
100
+
101
+ Analyzes node's structural parameters (νf, ΔNFR, EPI, θ) to determine
102
+ its position in the canonical TNFR lifecycle.
103
+
104
+ Parameters
105
+ ----------
106
+ G : TNFRGraph
107
+ Graph containing the node
108
+ node : NodeId
109
+ Node to analyze
110
+ config : dict, optional
111
+ Configuration overrides for thresholds:
112
+ - activation_threshold: Min νf for activation (default: 0.1)
113
+ - collapse_threshold: Min νf to avoid collapse (default: 0.01)
114
+ - bifurcation_threshold: Max |ΔNFR| before bifurcation (default: 10.0)
115
+ - stabilization_dnfr: Max |ΔNFR| for stabilization (default: 1.0)
116
+ - stabilization_coherence: Min coherence for stabilization (default: 0.8)
117
+ - propagation_coupling: Min phase coupling for propagation (default: 0.7)
118
+ - mutation_dnfr: Min |ΔNFR| for mutation state (default: 5.0)
119
+
120
+ Returns
121
+ -------
122
+ LifecycleState
123
+ Current lifecycle state
124
+
125
+ Notes
126
+ -----
127
+ Collapse conditions are checked first. Among active states, the state
128
+ with the strongest indicators is returned (e.g., high |ΔNFR| → mutation
129
+ takes precedence over stabilization).
130
+
131
+ Examples
132
+ --------
133
+ >>> from tnfr.structural import create_nfr
134
+ >>> G, node = create_nfr("test", epi=0.5, vf=1.0)
135
+ >>> G.nodes[node]["ΔNFR"] = 0.5
136
+ >>> state = get_lifecycle_state(G, node)
137
+ >>> state.value
138
+ 'activation'
139
+ """
140
+ if config is None:
141
+ config = {}
142
+
143
+ # Get thresholds from config or graph or defaults
144
+ def _get_threshold(key: str, default: float) -> float:
145
+ return float(config.get(key, G.graph.get(key.upper(), default)))
146
+
147
+ activation_threshold = _get_threshold("activation_threshold", 0.1)
148
+ collapse_threshold = _get_threshold("collapse_threshold", 0.01)
149
+ bifurcation_threshold = _get_threshold("bifurcation_threshold", 10.0)
150
+ stabilization_dnfr = _get_threshold("stabilization_dnfr", 1.0)
151
+ stabilization_coherence = _get_threshold("stabilization_coherence", 0.8)
152
+ propagation_coupling = _get_threshold("propagation_coupling", 0.7)
153
+ mutation_dnfr = _get_threshold("mutation_dnfr", 5.0)
154
+
155
+ # Get node structural parameters
156
+ vf = _get_node_attr(G, node, ALIAS_VF)
157
+ dnfr = _get_node_attr(G, node, ALIAS_DNFR)
158
+ epi = _get_node_attr(G, node, ALIAS_EPI)
159
+ theta = _get_node_attr(G, node, ALIAS_THETA)
160
+
161
+ # Check for collapse conditions first
162
+ if vf < collapse_threshold:
163
+ return LifecycleState.COLLAPSING
164
+
165
+ if abs(dnfr) > bifurcation_threshold:
166
+ return LifecycleState.COLLAPSING
167
+
168
+ # Compute phase coupling (simplified - could use full network coupling)
169
+ neighbors = list(G.neighbors(node))
170
+ if neighbors:
171
+ import math
172
+
173
+ neighbor_phases = [_get_node_attr(G, n, ALIAS_THETA) for n in neighbors]
174
+ mean_neighbor_phase = sum(neighbor_phases) / len(neighbor_phases)
175
+ phase_diff = abs(theta - mean_neighbor_phase)
176
+ # Normalize to [0, 1] where 1 is perfect alignment
177
+ phase_coupling = 1.0 - min(phase_diff, math.pi) / math.pi
178
+ else:
179
+ phase_coupling = 0.0
180
+
181
+ # Check for decoupling collapse
182
+ if neighbors and phase_coupling < DEFAULT_MIN_PHASE_COUPLING:
183
+ return LifecycleState.COLLAPSING
184
+
185
+ # Check active states (priority: mutation > propagation > stabilization > activation)
186
+
187
+ # Mutation: High dissonance with sufficient frequency
188
+ if abs(dnfr) > mutation_dnfr and vf > activation_threshold:
189
+ return LifecycleState.MUTATION
190
+
191
+ # Propagation: Strong network coupling
192
+ if phase_coupling > propagation_coupling and vf > activation_threshold:
193
+ return LifecycleState.PROPAGATION
194
+
195
+ # Stabilization: High coherence, low dissonance
196
+ # Note: C(t) computation would require full graph state, using EPI as proxy
197
+ if abs(dnfr) < stabilization_dnfr and epi > stabilization_coherence:
198
+ return LifecycleState.STABILIZATION
199
+
200
+ # Activation: Above activation threshold but not yet stabilized
201
+ if vf >= activation_threshold:
202
+ return LifecycleState.ACTIVATION
203
+
204
+ # Dormant: Below activation threshold but above collapse
205
+ return LifecycleState.DORMANT
206
+
207
+
208
+ def check_collapse_conditions(
209
+ G: TNFRGraph,
210
+ node: NodeId,
211
+ *,
212
+ config: dict[str, Any] | None = None,
213
+ ) -> tuple[bool, CollapseReason | None]:
214
+ """Check if node meets any collapse conditions.
215
+
216
+ Parameters
217
+ ----------
218
+ G : TNFRGraph
219
+ Graph containing the node
220
+ node : NodeId
221
+ Node to check
222
+ config : dict, optional
223
+ Configuration overrides for collapse thresholds
224
+
225
+ Returns
226
+ -------
227
+ should_collapse : bool
228
+ True if node should collapse
229
+ reason : CollapseReason | None
230
+ Reason for collapse, or None if not collapsing
231
+
232
+ Notes
233
+ -----
234
+ Multiple collapse conditions may be met simultaneously. This function
235
+ returns the first detected condition in priority order:
236
+ 1. Frequency failure (most fundamental)
237
+ 2. Extreme dissonance (structural instability)
238
+ 3. Network decoupling (loss of resonance)
239
+ 4. EPI dissolution (form loss)
240
+ """
241
+ if config is None:
242
+ config = {}
243
+
244
+ def _get_threshold(key: str, default: float) -> float:
245
+ return float(config.get(key, G.graph.get(key.upper(), default)))
246
+
247
+ collapse_threshold = _get_threshold("collapse_threshold", 0.01)
248
+ bifurcation_threshold = _get_threshold("bifurcation_threshold", 10.0)
249
+ min_coupling = _get_threshold("min_phase_coupling", 0.1)
250
+ min_epi = _get_threshold("min_epi", 0.01)
251
+
252
+ # Get node parameters
253
+ vf = _get_node_attr(G, node, ALIAS_VF)
254
+ dnfr = _get_node_attr(G, node, ALIAS_DNFR)
255
+ epi = _get_node_attr(G, node, ALIAS_EPI)
256
+ theta = _get_node_attr(G, node, ALIAS_THETA)
257
+
258
+ # Check frequency failure (most fundamental)
259
+ if vf < collapse_threshold:
260
+ return (True, CollapseReason.FREQUENCY_FAILURE)
261
+
262
+ # Check extreme dissonance
263
+ if abs(dnfr) > bifurcation_threshold:
264
+ return (True, CollapseReason.EXTREME_DISSONANCE)
265
+
266
+ # Check network decoupling
267
+ neighbors = list(G.neighbors(node))
268
+ if neighbors:
269
+ import math
270
+
271
+ neighbor_phases = [_get_node_attr(G, n, ALIAS_THETA) for n in neighbors]
272
+ mean_neighbor_phase = sum(neighbor_phases) / len(neighbor_phases)
273
+ phase_diff = abs(theta - mean_neighbor_phase)
274
+ phase_coupling = 1.0 - min(phase_diff, math.pi) / math.pi
275
+
276
+ if phase_coupling < min_coupling:
277
+ return (True, CollapseReason.NETWORK_DECOUPLING)
278
+
279
+ # Check EPI dissolution
280
+ if epi < min_epi:
281
+ return (True, CollapseReason.EPI_DISSOLUTION)
282
+
283
+ return (False, None)
284
+
285
+
286
+ def should_collapse(
287
+ G: TNFRGraph,
288
+ node: NodeId,
289
+ *,
290
+ config: dict[str, Any] | None = None,
291
+ ) -> bool:
292
+ """Check if node should collapse (simplified interface).
293
+
294
+ Parameters
295
+ ----------
296
+ G : TNFRGraph
297
+ Graph containing the node
298
+ node : NodeId
299
+ Node to check
300
+ config : dict, optional
301
+ Configuration overrides
302
+
303
+ Returns
304
+ -------
305
+ bool
306
+ True if node meets collapse conditions
307
+
308
+ See Also
309
+ --------
310
+ check_collapse_conditions : Full collapse check with reason
311
+ get_lifecycle_state : Complete lifecycle state determination
312
+ """
313
+ should_collapse_flag, _ = check_collapse_conditions(G, node, config=config)
314
+ return should_collapse_flag