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
tnfr/telemetry/nu_f.py ADDED
@@ -0,0 +1,422 @@
1
+ """Structural frequency (νf) telemetry estimators.
2
+
3
+ This module aggregates discrete reorganisation counts observed over
4
+ time windows and exposes Poisson maximum likelihood estimators (MLE) for
5
+ the structural frequency νf. Results are provided both in canonical
6
+ ``Hz_str`` and converted ``Hz`` using :mod:`tnfr.units`, allowing callers
7
+ to surface telemetry without duplicating conversion logic.
8
+
9
+ Snapshots emitted by :class:`NuFTelemetryAccumulator` are appended to the
10
+ ``G.graph["telemetry"]["nu_f_history"]`` channel so downstream observers
11
+ and structured logging hooks can consume them without interfering with
12
+ runtime summaries stored under ``G.graph["telemetry"]["nu_f"]``.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import math
18
+ import weakref
19
+ from collections import deque
20
+ from collections.abc import MutableMapping
21
+ from dataclasses import dataclass
22
+ from statistics import NormalDist
23
+ from typing import Any, Deque, Mapping
24
+
25
+ from ..types import GraphLike
26
+ from ..units import get_hz_bridge, hz_str_to_hz
27
+
28
+ __all__ = (
29
+ "NuFWindow",
30
+ "NuFSnapshot",
31
+ "NuFTelemetryAccumulator",
32
+ "ensure_nu_f_telemetry",
33
+ "record_nu_f_window",
34
+ )
35
+
36
+
37
+ @dataclass(frozen=True)
38
+ class NuFWindow:
39
+ """Discrete reorganisation observations captured over a time window."""
40
+
41
+ reorganisations: int
42
+ """Number of reorganisations counted within the window."""
43
+
44
+ duration: float
45
+ """Duration of the window expressed in structural time units."""
46
+
47
+ start: float | None = None
48
+ """Optional inclusive window start timestamp."""
49
+
50
+ end: float | None = None
51
+ """Optional exclusive window end timestamp."""
52
+
53
+ def __post_init__(self) -> None:
54
+ reorganisations = int(self.reorganisations)
55
+ duration = float(self.duration)
56
+ object.__setattr__(self, "reorganisations", reorganisations)
57
+ object.__setattr__(self, "duration", duration)
58
+ if reorganisations < 0:
59
+ raise ValueError("reorganisations must be non-negative")
60
+ if not math.isfinite(duration) or duration <= 0.0:
61
+ raise ValueError("duration must be a positive finite number")
62
+ if self.start is not None and self.end is not None:
63
+ start = float(self.start)
64
+ end = float(self.end)
65
+ object.__setattr__(self, "start", start)
66
+ object.__setattr__(self, "end", end)
67
+ if end < start:
68
+ raise ValueError("end must be greater than or equal to start")
69
+ window = end - start
70
+ if window <= 0.0:
71
+ raise ValueError("start and end must describe a non-empty window")
72
+ # Allow minor numerical discrepancies when duration is supplied
73
+ # independently from ``start``/``end``.
74
+ if not math.isclose(window, duration, rel_tol=1e-9, abs_tol=1e-9):
75
+ raise ValueError(
76
+ "duration does not match the difference between start and end",
77
+ )
78
+
79
+ @classmethod
80
+ def from_bounds(cls, reorganisations: int, start: float, end: float) -> "NuFWindow":
81
+ """Construct a window inferring the duration from ``start``/``end``."""
82
+
83
+ start_f = float(start)
84
+ end_f = float(end)
85
+ if end_f <= start_f:
86
+ raise ValueError("end must be greater than start")
87
+ return cls(
88
+ reorganisations=int(reorganisations),
89
+ duration=end_f - start_f,
90
+ start=start_f,
91
+ end=end_f,
92
+ )
93
+
94
+ def as_payload(self) -> Mapping[str, float | int | None]:
95
+ """Return a JSON-serialisable representation of the window."""
96
+
97
+ return {
98
+ "reorganisations": int(self.reorganisations),
99
+ "duration": float(self.duration),
100
+ "start": float(self.start) if self.start is not None else None,
101
+ "end": float(self.end) if self.end is not None else None,
102
+ }
103
+
104
+
105
+ @dataclass(frozen=True)
106
+ class NuFSnapshot:
107
+ """Aggregate νf estimates computed from recorded windows."""
108
+
109
+ windows: tuple[NuFWindow, ...]
110
+ total_reorganisations: int
111
+ total_duration: float
112
+ rate_hz_str: float | None
113
+ rate_hz: float | None
114
+ variance_hz_str: float | None
115
+ variance_hz: float | None
116
+ confidence_level: float | None
117
+ ci_lower_hz_str: float | None
118
+ ci_upper_hz_str: float | None
119
+ ci_lower_hz: float | None
120
+ ci_upper_hz: float | None
121
+
122
+ def as_payload(self) -> dict[str, Any]:
123
+ """Return a structured representation suitable for telemetry sinks."""
124
+
125
+ return {
126
+ "windows": [window.as_payload() for window in self.windows],
127
+ "total_reorganisations": self.total_reorganisations,
128
+ "total_duration": self.total_duration,
129
+ "rate_hz_str": self.rate_hz_str,
130
+ "rate_hz": self.rate_hz,
131
+ "variance_hz_str": self.variance_hz_str,
132
+ "variance_hz": self.variance_hz,
133
+ "confidence_level": self.confidence_level,
134
+ "ci_lower_hz_str": self.ci_lower_hz_str,
135
+ "ci_upper_hz_str": self.ci_upper_hz_str,
136
+ "ci_lower_hz": self.ci_lower_hz,
137
+ "ci_upper_hz": self.ci_upper_hz,
138
+ }
139
+
140
+
141
+ class NuFTelemetryAccumulator:
142
+ """Accumulate reorganisation telemetry and produce νf estimates."""
143
+
144
+ def __init__(
145
+ self,
146
+ *,
147
+ confidence_level: float = 0.95,
148
+ history_limit: int | None = 128,
149
+ window_limit: int | None = None,
150
+ graph: GraphLike | MutableMapping[str, Any] | None = None,
151
+ ) -> None:
152
+ if not 0.0 < confidence_level < 1.0:
153
+ raise ValueError("confidence_level must be in the open interval (0, 1)")
154
+ if history_limit is not None and history_limit <= 0:
155
+ raise ValueError("history_limit must be positive when provided")
156
+ if window_limit is not None and window_limit <= 0:
157
+ raise ValueError("window_limit must be positive when provided")
158
+
159
+ self._confidence_level = float(confidence_level)
160
+ self._history_limit = history_limit
161
+ self._window_limit = window_limit
162
+ self._windows: Deque[NuFWindow] = deque()
163
+ self._total_reorganisations = 0
164
+ self._total_duration = 0.0
165
+ self._graph_ref: (
166
+ weakref.ReferenceType[GraphLike | MutableMapping[str, Any]] | None
167
+ ) = None
168
+ self.attach_graph(graph)
169
+
170
+ @property
171
+ def confidence_level(self) -> float:
172
+ """Return the configured confidence level for interval estimation."""
173
+
174
+ return self._confidence_level
175
+
176
+ @property
177
+ def history_limit(self) -> int | None:
178
+ """Return the maximum number of snapshots retained on the graph."""
179
+
180
+ return self._history_limit
181
+
182
+ @property
183
+ def window_limit(self) -> int | None:
184
+ """Return the maximum number of windows stored in memory."""
185
+
186
+ return self._window_limit
187
+
188
+ def attach_graph(self, graph: GraphLike | MutableMapping[str, Any] | None) -> None:
189
+ """Attach ``graph`` for unit conversions and telemetry persistence."""
190
+
191
+ if graph is None:
192
+ return
193
+ try:
194
+ self._graph_ref = weakref.ref(graph) # type: ignore[arg-type]
195
+ except TypeError: # pragma: no cover - mapping instances are not weakrefable
196
+ self._graph_ref = None
197
+
198
+ def _resolve_graph(
199
+ self,
200
+ ) -> GraphLike | MutableMapping[str, Any] | None:
201
+ return self._graph_ref() if self._graph_ref is not None else None
202
+
203
+ def _coerce_window(self, window: NuFWindow) -> None:
204
+ if self._window_limit is not None and len(self._windows) >= self._window_limit:
205
+ removed = self._windows.popleft()
206
+ self._total_reorganisations -= removed.reorganisations
207
+ self._total_duration -= removed.duration
208
+ self._windows.append(window)
209
+ self._total_reorganisations += window.reorganisations
210
+ self._total_duration += window.duration
211
+
212
+ def record_window(
213
+ self,
214
+ window: NuFWindow,
215
+ *,
216
+ graph: GraphLike | MutableMapping[str, Any] | None = None,
217
+ ) -> NuFSnapshot:
218
+ """Record ``window`` and return the updated telemetry snapshot."""
219
+
220
+ self._coerce_window(window)
221
+ graph_obj = graph or self._resolve_graph()
222
+ snapshot = self.snapshot(graph=graph_obj)
223
+ self._persist_snapshot(snapshot, graph_obj)
224
+ return snapshot
225
+
226
+ def record_counts(
227
+ self,
228
+ reorganisations: int,
229
+ duration: float,
230
+ *,
231
+ start: float | None = None,
232
+ end: float | None = None,
233
+ graph: GraphLike | MutableMapping[str, Any] | None = None,
234
+ ) -> NuFSnapshot:
235
+ """Record a window described by ``reorganisations`` and ``duration``."""
236
+
237
+ window = NuFWindow(
238
+ reorganisations=int(reorganisations),
239
+ duration=float(duration),
240
+ start=float(start) if start is not None else None,
241
+ end=float(end) if end is not None else None,
242
+ )
243
+ return self.record_window(window, graph=graph)
244
+
245
+ def reset(self) -> None:
246
+ """Clear accumulated windows and totals."""
247
+
248
+ self._windows.clear()
249
+ self._total_reorganisations = 0
250
+ self._total_duration = 0.0
251
+
252
+ def _normal_dist(self) -> NormalDist:
253
+ return NormalDist()
254
+
255
+ def _graph_mapping(
256
+ self, graph: GraphLike | MutableMapping[str, Any] | None
257
+ ) -> MutableMapping[str, Any] | None:
258
+ if graph is None:
259
+ return None
260
+ if isinstance(graph, MutableMapping):
261
+ return graph
262
+ graph_data = getattr(graph, "graph", None)
263
+ return graph_data if isinstance(graph_data, MutableMapping) else None
264
+
265
+ def snapshot(
266
+ self,
267
+ *,
268
+ graph: GraphLike | MutableMapping[str, Any] | None = None,
269
+ ) -> NuFSnapshot:
270
+ """Return a νf telemetry snapshot without mutating internal state."""
271
+
272
+ total_duration = self._total_duration
273
+ total_reorganisations = self._total_reorganisations
274
+ windows = tuple(self._windows)
275
+
276
+ if total_duration <= 0.0:
277
+ rate_hz_str = None
278
+ variance_hz_str = None
279
+ ci_lower_str = None
280
+ ci_upper_str = None
281
+ confidence_level: float | None = None
282
+ else:
283
+ rate_hz_str = total_reorganisations / total_duration
284
+ variance_hz_str = rate_hz_str / total_duration
285
+ std_error = math.sqrt(variance_hz_str)
286
+ z = self._normal_dist().inv_cdf(0.5 + (self._confidence_level / 2.0))
287
+ ci_lower_str = max(rate_hz_str - z * std_error, 0.0)
288
+ ci_upper_str = rate_hz_str + z * std_error
289
+ confidence_level = self._confidence_level
290
+
291
+ graph_obj = graph or self._resolve_graph()
292
+ rate_hz = variance_hz = ci_lower_hz = ci_upper_hz = None
293
+ if rate_hz_str is not None and graph_obj is not None:
294
+ if not isinstance(graph_obj, MutableMapping):
295
+ bridge = get_hz_bridge(graph_obj)
296
+ rate_hz = hz_str_to_hz(rate_hz_str, graph_obj)
297
+ if variance_hz_str is not None:
298
+ variance_hz = variance_hz_str * (bridge**2)
299
+ if ci_lower_str is not None and ci_upper_str is not None:
300
+ ci_lower_hz = hz_str_to_hz(ci_lower_str, graph_obj)
301
+ ci_upper_hz = hz_str_to_hz(ci_upper_str, graph_obj)
302
+
303
+ return NuFSnapshot(
304
+ windows=windows,
305
+ total_reorganisations=total_reorganisations,
306
+ total_duration=total_duration,
307
+ rate_hz_str=rate_hz_str,
308
+ rate_hz=rate_hz,
309
+ variance_hz_str=variance_hz_str,
310
+ variance_hz=variance_hz,
311
+ confidence_level=confidence_level,
312
+ ci_lower_hz_str=ci_lower_str,
313
+ ci_upper_hz_str=ci_upper_str,
314
+ ci_lower_hz=ci_lower_hz,
315
+ ci_upper_hz=ci_upper_hz,
316
+ )
317
+
318
+ def _persist_snapshot(
319
+ self,
320
+ snapshot: NuFSnapshot,
321
+ graph: GraphLike | MutableMapping[str, Any] | None,
322
+ ) -> None:
323
+ mapping = self._graph_mapping(graph)
324
+ if mapping is None:
325
+ return
326
+
327
+ telemetry = mapping.setdefault("telemetry", {})
328
+ if not isinstance(telemetry, MutableMapping):
329
+ telemetry = {}
330
+ mapping["telemetry"] = telemetry
331
+ payload = snapshot.as_payload()
332
+ history_key = "nu_f_history"
333
+ history = telemetry.get(history_key)
334
+ if not isinstance(history, list):
335
+ legacy_history = telemetry.get("nu_f")
336
+ if isinstance(legacy_history, list):
337
+ history = legacy_history
338
+ else:
339
+ history = []
340
+ telemetry[history_key] = history
341
+ history.append(payload)
342
+ if self._history_limit is not None and len(history) > self._history_limit:
343
+ del history[: len(history) - self._history_limit]
344
+
345
+
346
+ _ACCUMULATOR_KEY = "_tnfr_nu_f_accumulator"
347
+
348
+
349
+ def ensure_nu_f_telemetry(
350
+ graph: GraphLike,
351
+ *,
352
+ confidence_level: float | None = None,
353
+ history_limit: int | None = 128,
354
+ window_limit: int | None = None,
355
+ ) -> NuFTelemetryAccumulator:
356
+ """Ensure ``graph`` exposes a :class:`NuFTelemetryAccumulator`.
357
+
358
+ When ``confidence_level`` is ``None`` the existing accumulator is preserved
359
+ and new accumulators default to ``0.95``.
360
+ """
361
+
362
+ mapping = getattr(graph, "graph", None)
363
+ if not isinstance(mapping, MutableMapping):
364
+ raise TypeError("graph.graph must be a mutable mapping for telemetry storage")
365
+
366
+ accumulator = mapping.get(_ACCUMULATOR_KEY)
367
+ replace = False
368
+ if isinstance(accumulator, NuFTelemetryAccumulator):
369
+ if (
370
+ (
371
+ confidence_level is not None
372
+ and abs(accumulator.confidence_level - confidence_level) > 1e-12
373
+ )
374
+ or (
375
+ history_limit is not None and accumulator.history_limit != history_limit
376
+ )
377
+ or (window_limit is not None and accumulator.window_limit != window_limit)
378
+ ):
379
+ replace = True
380
+ if not isinstance(accumulator, NuFTelemetryAccumulator) or replace:
381
+ requested_confidence = 0.95 if confidence_level is None else confidence_level
382
+ accumulator = NuFTelemetryAccumulator(
383
+ confidence_level=requested_confidence,
384
+ history_limit=history_limit,
385
+ window_limit=window_limit,
386
+ graph=graph,
387
+ )
388
+ mapping[_ACCUMULATOR_KEY] = accumulator
389
+ else:
390
+ accumulator.attach_graph(graph)
391
+ return accumulator
392
+
393
+
394
+ def record_nu_f_window(
395
+ graph: GraphLike,
396
+ reorganisations: int,
397
+ duration: float,
398
+ *,
399
+ start: float | None = None,
400
+ end: float | None = None,
401
+ confidence_level: float | None = None,
402
+ history_limit: int | None = None,
403
+ window_limit: int | None = None,
404
+ ) -> NuFSnapshot:
405
+ """Record a νf observation for ``graph`` and persist the snapshot."""
406
+
407
+ kwargs: dict[str, Any] = {}
408
+ if confidence_level is not None:
409
+ kwargs["confidence_level"] = confidence_level
410
+ if history_limit is not None:
411
+ kwargs["history_limit"] = history_limit
412
+ if window_limit is not None:
413
+ kwargs["window_limit"] = window_limit
414
+
415
+ accumulator = ensure_nu_f_telemetry(graph, **kwargs)
416
+ return accumulator.record_counts(
417
+ reorganisations,
418
+ duration,
419
+ start=start,
420
+ end=end,
421
+ graph=graph,
422
+ )
@@ -0,0 +1,108 @@
1
+ from __future__ import annotations
2
+
3
+ from collections import deque
4
+ from collections.abc import MutableMapping
5
+ from dataclasses import dataclass
6
+ from typing import Any, Mapping, Sequence
7
+
8
+ from ..types import GraphLike
9
+
10
+ __all__ = (
11
+ "NuFWindow",
12
+ "NuFSnapshot",
13
+ "NuFTelemetryAccumulator",
14
+ "ensure_nu_f_telemetry",
15
+ "record_nu_f_window",
16
+ )
17
+
18
+ @dataclass
19
+ class NuFWindow:
20
+ reorganisations: int
21
+ duration: float
22
+ start: float | None = ...
23
+ end: float | None = ...
24
+
25
+ def __post_init__(self) -> None: ...
26
+ @classmethod
27
+ def from_bounds(
28
+ cls, reorganisations: int, start: float, end: float
29
+ ) -> NuFWindow: ...
30
+ def as_payload(self) -> Mapping[str, float | int | None]: ...
31
+
32
+ @dataclass
33
+ class NuFSnapshot:
34
+ windows: Sequence[NuFWindow]
35
+ total_reorganisations: int
36
+ total_duration: float
37
+ rate_hz_str: float | None
38
+ rate_hz: float | None
39
+ variance_hz_str: float | None
40
+ variance_hz: float | None
41
+ confidence_level: float | None
42
+ ci_lower_hz_str: float | None
43
+ ci_upper_hz_str: float | None
44
+ ci_lower_hz: float | None
45
+ ci_upper_hz: float | None
46
+
47
+ def as_payload(self) -> dict[str, Any]: ...
48
+
49
+ class NuFTelemetryAccumulator:
50
+ _windows: deque[NuFWindow]
51
+
52
+ def __init__(
53
+ self,
54
+ *,
55
+ confidence_level: float = ...,
56
+ history_limit: int | None = ...,
57
+ window_limit: int | None = ...,
58
+ graph: GraphLike | MutableMapping[str, Any] | None = ...,
59
+ ) -> None: ...
60
+ @property
61
+ def confidence_level(self) -> float: ...
62
+ @property
63
+ def history_limit(self) -> int | None: ...
64
+ @property
65
+ def window_limit(self) -> int | None: ...
66
+ def attach_graph(
67
+ self, graph: GraphLike | MutableMapping[str, Any] | None
68
+ ) -> None: ...
69
+ def record_window(
70
+ self,
71
+ window: NuFWindow,
72
+ *,
73
+ graph: GraphLike | MutableMapping[str, Any] | None = ...,
74
+ ) -> NuFSnapshot: ...
75
+ def record_counts(
76
+ self,
77
+ reorganisations: int,
78
+ duration: float,
79
+ *,
80
+ start: float | None = ...,
81
+ end: float | None = ...,
82
+ graph: GraphLike | MutableMapping[str, Any] | None = ...,
83
+ ) -> NuFSnapshot: ...
84
+ def reset(self) -> None: ...
85
+ def snapshot(
86
+ self,
87
+ *,
88
+ graph: GraphLike | MutableMapping[str, Any] | None = ...,
89
+ ) -> NuFSnapshot: ...
90
+
91
+ def ensure_nu_f_telemetry(
92
+ graph: GraphLike,
93
+ *,
94
+ confidence_level: float | None = ...,
95
+ history_limit: int | None = ...,
96
+ window_limit: int | None = ...,
97
+ ) -> NuFTelemetryAccumulator: ...
98
+ def record_nu_f_window(
99
+ graph: GraphLike,
100
+ reorganisations: int,
101
+ duration: float,
102
+ *,
103
+ start: float | None = ...,
104
+ end: float | None = ...,
105
+ confidence_level: float | None = ...,
106
+ history_limit: int | None = ...,
107
+ window_limit: int | None = ...,
108
+ ) -> NuFSnapshot: ...
@@ -0,0 +1,36 @@
1
+ """Canonical telemetry verbosity presets for TNFR structures.
2
+
3
+ Each level expresses how much structural context is exported in traces and
4
+ metrics:
5
+
6
+ * ``basic`` preserves lightweight coherence checks for quick health probes.
7
+ * ``detailed`` adds phase alignment and coupling diagnostics to map resonance.
8
+ * ``debug`` captures the full glyph narrative for deep structural forensics.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from enum import Enum
14
+
15
+
16
+ class TelemetryVerbosity(str, Enum):
17
+ """Enumerated verbosity tiers shared by trace and metrics pipelines."""
18
+
19
+ BASIC = "basic"
20
+ DETAILED = "detailed"
21
+ DEBUG = "debug"
22
+
23
+
24
+ TELEMETRY_VERBOSITY_LEVELS: tuple[str, ...] = tuple(
25
+ level.value for level in TelemetryVerbosity
26
+ )
27
+ """Ordered tuple of canonical telemetry verbosity identifiers."""
28
+
29
+ TELEMETRY_VERBOSITY_DEFAULT: str = TelemetryVerbosity.DEBUG.value
30
+ """Default telemetry verbosity preserving complete structural capture."""
31
+
32
+ __all__ = [
33
+ "TelemetryVerbosity",
34
+ "TELEMETRY_VERBOSITY_LEVELS",
35
+ "TELEMETRY_VERBOSITY_DEFAULT",
36
+ ]
@@ -0,0 +1,15 @@
1
+ from enum import Enum
2
+
3
+ __all__ = [
4
+ "TelemetryVerbosity",
5
+ "TELEMETRY_VERBOSITY_LEVELS",
6
+ "TELEMETRY_VERBOSITY_DEFAULT",
7
+ ]
8
+
9
+ class TelemetryVerbosity(str, Enum):
10
+ BASIC = "basic"
11
+ DETAILED = "detailed"
12
+ DEBUG = "debug"
13
+
14
+ TELEMETRY_VERBOSITY_LEVELS: tuple[str, ...]
15
+ TELEMETRY_VERBOSITY_DEFAULT: str
tnfr/tokens.py ADDED
@@ -0,0 +1,58 @@
1
+ """Token primitives for the TNFR DSL."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .compat.dataclass import dataclass
6
+ from enum import Enum, auto
7
+ from typing import Any, Iterable, Optional, Sequence, Union
8
+
9
+ from .types import Glyph, Node
10
+
11
+
12
+ @dataclass(slots=True)
13
+ class WAIT:
14
+ """Wait a number of steps without applying glyphs."""
15
+
16
+ steps: int = 1
17
+
18
+
19
+ @dataclass(slots=True)
20
+ class TARGET:
21
+ """Select the subset of nodes for subsequent glyphs."""
22
+
23
+ nodes: Optional[Iterable[Node]] = None # ``None`` targets all nodes
24
+
25
+
26
+ @dataclass(slots=True)
27
+ class THOL:
28
+ """THOL block that opens self-organisation."""
29
+
30
+ body: Sequence[Any]
31
+ repeat: int = 1
32
+ force_close: Optional[Glyph] = None
33
+
34
+
35
+ Token = Union[Glyph, WAIT, TARGET, THOL, str]
36
+
37
+ # Sentinel used internally to mark the boundaries of a THOL block during flattening
38
+ THOL_SENTINEL = object()
39
+
40
+
41
+ class OpTag(Enum):
42
+ """Operation tags emitted by the flattening step."""
43
+
44
+ TARGET = auto()
45
+ WAIT = auto()
46
+ GLYPH = auto()
47
+ THOL = auto()
48
+
49
+
50
+ __all__ = [
51
+ "Node",
52
+ "WAIT",
53
+ "TARGET",
54
+ "THOL",
55
+ "Token",
56
+ "THOL_SENTINEL",
57
+ "OpTag",
58
+ ]
tnfr/tokens.pyi ADDED
@@ -0,0 +1,36 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from enum import Enum
5
+ from typing import Iterable, Optional, Sequence, Union
6
+
7
+ from ._compat import TypeAlias
8
+ from .types import Glyph, NodeId
9
+
10
+ __all__: tuple[str, ...]
11
+
12
+ Node: TypeAlias = NodeId
13
+
14
+ @dataclass
15
+ class WAIT:
16
+ steps: int = 1
17
+
18
+ @dataclass
19
+ class TARGET:
20
+ nodes: Optional[Iterable[Node]] = None
21
+
22
+ @dataclass
23
+ class THOL:
24
+ body: Sequence["Token"]
25
+ repeat: int = 1
26
+ force_close: Optional[Glyph] = None
27
+
28
+ Token: TypeAlias = Union[Glyph, WAIT, TARGET, THOL, str]
29
+
30
+ THOL_SENTINEL: object
31
+
32
+ class OpTag(Enum):
33
+ TARGET = ...
34
+ WAIT = ...
35
+ GLYPH = ...
36
+ THOL = ...
tnfr/tools/__init__.py ADDED
@@ -0,0 +1,20 @@
1
+ """TNFR tools module - Advanced utilities for sequence generation and analysis."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .domain_templates import (
6
+ DOMAIN_TEMPLATES,
7
+ get_template,
8
+ list_domains,
9
+ list_objectives,
10
+ )
11
+ from .sequence_generator import ContextualSequenceGenerator, GenerationResult
12
+
13
+ __all__ = [
14
+ "ContextualSequenceGenerator",
15
+ "GenerationResult",
16
+ "DOMAIN_TEMPLATES",
17
+ "get_template",
18
+ "list_domains",
19
+ "list_objectives",
20
+ ]