tnfr 4.5.2__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 (365) hide show
  1. tnfr/__init__.py +334 -50
  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 +214 -37
  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 +149 -556
  15. tnfr/cache.pyi +13 -0
  16. tnfr/cli/__init__.py +51 -16
  17. tnfr/cli/__init__.pyi +26 -0
  18. tnfr/cli/arguments.py +344 -32
  19. tnfr/cli/arguments.pyi +29 -0
  20. tnfr/cli/execution.py +676 -50
  21. tnfr/cli/execution.pyi +70 -0
  22. tnfr/cli/interactive_validator.py +614 -0
  23. tnfr/cli/utils.py +18 -3
  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/{constants_glyphs.py → config/constants.py} +26 -20
  34. tnfr/config/constants.pyi +12 -0
  35. tnfr/config/defaults.py +54 -0
  36. tnfr/{constants/core.py → config/defaults_core.py} +59 -6
  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 +51 -133
  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 +3 -1
  57. tnfr/constants/init.pyi +12 -0
  58. tnfr/constants/metric.py +9 -15
  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 +213 -633
  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 +2699 -398
  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 +496 -102
  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 +10 -5
  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 +77 -55
  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 +29 -50
  125. tnfr/flatten.pyi +21 -0
  126. tnfr/gamma.py +66 -53
  127. tnfr/gamma.pyi +36 -0
  128. tnfr/glyph_history.py +144 -57
  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 +70 -30
  133. tnfr/immutable.pyi +36 -0
  134. tnfr/initialization.py +22 -16
  135. tnfr/initialization.pyi +65 -0
  136. tnfr/io.py +5 -241
  137. tnfr/io.pyi +13 -0
  138. tnfr/locking.pyi +7 -0
  139. tnfr/mathematics/__init__.py +79 -0
  140. tnfr/mathematics/backend.py +453 -0
  141. tnfr/mathematics/backend.pyi +99 -0
  142. tnfr/mathematics/dynamics.py +408 -0
  143. tnfr/mathematics/dynamics.pyi +90 -0
  144. tnfr/mathematics/epi.py +391 -0
  145. tnfr/mathematics/epi.pyi +65 -0
  146. tnfr/mathematics/generators.py +242 -0
  147. tnfr/mathematics/generators.pyi +29 -0
  148. tnfr/mathematics/metrics.py +119 -0
  149. tnfr/mathematics/metrics.pyi +16 -0
  150. tnfr/mathematics/operators.py +239 -0
  151. tnfr/mathematics/operators.pyi +59 -0
  152. tnfr/mathematics/operators_factory.py +124 -0
  153. tnfr/mathematics/operators_factory.pyi +11 -0
  154. tnfr/mathematics/projection.py +87 -0
  155. tnfr/mathematics/projection.pyi +33 -0
  156. tnfr/mathematics/runtime.py +182 -0
  157. tnfr/mathematics/runtime.pyi +64 -0
  158. tnfr/mathematics/spaces.py +256 -0
  159. tnfr/mathematics/spaces.pyi +83 -0
  160. tnfr/mathematics/transforms.py +305 -0
  161. tnfr/mathematics/transforms.pyi +62 -0
  162. tnfr/metrics/__init__.py +47 -9
  163. tnfr/metrics/__init__.pyi +20 -0
  164. tnfr/metrics/buffer_cache.py +163 -0
  165. tnfr/metrics/buffer_cache.pyi +24 -0
  166. tnfr/metrics/cache_utils.py +214 -0
  167. tnfr/metrics/coherence.py +1510 -330
  168. tnfr/metrics/coherence.pyi +129 -0
  169. tnfr/metrics/common.py +23 -16
  170. tnfr/metrics/common.pyi +35 -0
  171. tnfr/metrics/core.py +251 -36
  172. tnfr/metrics/core.pyi +13 -0
  173. tnfr/metrics/diagnosis.py +709 -110
  174. tnfr/metrics/diagnosis.pyi +86 -0
  175. tnfr/metrics/emergence.py +245 -0
  176. tnfr/metrics/export.py +60 -18
  177. tnfr/metrics/export.pyi +7 -0
  178. tnfr/metrics/glyph_timing.py +233 -43
  179. tnfr/metrics/glyph_timing.pyi +81 -0
  180. tnfr/metrics/learning_metrics.py +280 -0
  181. tnfr/metrics/learning_metrics.pyi +21 -0
  182. tnfr/metrics/phase_coherence.py +351 -0
  183. tnfr/metrics/phase_compatibility.py +349 -0
  184. tnfr/metrics/reporting.py +63 -28
  185. tnfr/metrics/reporting.pyi +25 -0
  186. tnfr/metrics/sense_index.py +1126 -43
  187. tnfr/metrics/sense_index.pyi +9 -0
  188. tnfr/metrics/trig.py +215 -23
  189. tnfr/metrics/trig.pyi +13 -0
  190. tnfr/metrics/trig_cache.py +148 -24
  191. tnfr/metrics/trig_cache.pyi +10 -0
  192. tnfr/multiscale/__init__.py +32 -0
  193. tnfr/multiscale/hierarchical.py +517 -0
  194. tnfr/node.py +646 -140
  195. tnfr/node.pyi +139 -0
  196. tnfr/observers.py +160 -45
  197. tnfr/observers.pyi +31 -0
  198. tnfr/ontosim.py +23 -19
  199. tnfr/ontosim.pyi +28 -0
  200. tnfr/operators/__init__.py +1358 -106
  201. tnfr/operators/__init__.pyi +31 -0
  202. tnfr/operators/algebra.py +277 -0
  203. tnfr/operators/canonical_patterns.py +420 -0
  204. tnfr/operators/cascade.py +267 -0
  205. tnfr/operators/cycle_detection.py +358 -0
  206. tnfr/operators/definitions.py +4108 -0
  207. tnfr/operators/definitions.pyi +78 -0
  208. tnfr/operators/grammar.py +1164 -0
  209. tnfr/operators/grammar.pyi +140 -0
  210. tnfr/operators/hamiltonian.py +710 -0
  211. tnfr/operators/health_analyzer.py +809 -0
  212. tnfr/operators/jitter.py +107 -38
  213. tnfr/operators/jitter.pyi +11 -0
  214. tnfr/operators/lifecycle.py +314 -0
  215. tnfr/operators/metabolism.py +618 -0
  216. tnfr/operators/metrics.py +2138 -0
  217. tnfr/operators/network_analysis/__init__.py +27 -0
  218. tnfr/operators/network_analysis/source_detection.py +186 -0
  219. tnfr/operators/nodal_equation.py +395 -0
  220. tnfr/operators/pattern_detection.py +660 -0
  221. tnfr/operators/patterns.py +669 -0
  222. tnfr/operators/postconditions/__init__.py +38 -0
  223. tnfr/operators/postconditions/mutation.py +236 -0
  224. tnfr/operators/preconditions/__init__.py +1226 -0
  225. tnfr/operators/preconditions/coherence.py +305 -0
  226. tnfr/operators/preconditions/dissonance.py +236 -0
  227. tnfr/operators/preconditions/emission.py +128 -0
  228. tnfr/operators/preconditions/mutation.py +580 -0
  229. tnfr/operators/preconditions/reception.py +125 -0
  230. tnfr/operators/preconditions/resonance.py +364 -0
  231. tnfr/operators/registry.py +74 -0
  232. tnfr/operators/registry.pyi +9 -0
  233. tnfr/operators/remesh.py +1415 -91
  234. tnfr/operators/remesh.pyi +26 -0
  235. tnfr/operators/structural_units.py +268 -0
  236. tnfr/operators/unified_grammar.py +105 -0
  237. tnfr/parallel/__init__.py +54 -0
  238. tnfr/parallel/auto_scaler.py +234 -0
  239. tnfr/parallel/distributed.py +384 -0
  240. tnfr/parallel/engine.py +238 -0
  241. tnfr/parallel/gpu_engine.py +420 -0
  242. tnfr/parallel/monitoring.py +248 -0
  243. tnfr/parallel/partitioner.py +459 -0
  244. tnfr/py.typed +0 -0
  245. tnfr/recipes/__init__.py +22 -0
  246. tnfr/recipes/cookbook.py +743 -0
  247. tnfr/rng.py +75 -151
  248. tnfr/rng.pyi +26 -0
  249. tnfr/schemas/__init__.py +8 -0
  250. tnfr/schemas/grammar.json +94 -0
  251. tnfr/sdk/__init__.py +107 -0
  252. tnfr/sdk/__init__.pyi +19 -0
  253. tnfr/sdk/adaptive_system.py +173 -0
  254. tnfr/sdk/adaptive_system.pyi +21 -0
  255. tnfr/sdk/builders.py +370 -0
  256. tnfr/sdk/builders.pyi +51 -0
  257. tnfr/sdk/fluent.py +1121 -0
  258. tnfr/sdk/fluent.pyi +74 -0
  259. tnfr/sdk/templates.py +342 -0
  260. tnfr/sdk/templates.pyi +41 -0
  261. tnfr/sdk/utils.py +341 -0
  262. tnfr/secure_config.py +46 -0
  263. tnfr/security/__init__.py +70 -0
  264. tnfr/security/database.py +514 -0
  265. tnfr/security/subprocess.py +503 -0
  266. tnfr/security/validation.py +290 -0
  267. tnfr/selector.py +59 -22
  268. tnfr/selector.pyi +19 -0
  269. tnfr/sense.py +92 -67
  270. tnfr/sense.pyi +23 -0
  271. tnfr/services/__init__.py +17 -0
  272. tnfr/services/orchestrator.py +325 -0
  273. tnfr/sparse/__init__.py +39 -0
  274. tnfr/sparse/representations.py +492 -0
  275. tnfr/structural.py +639 -263
  276. tnfr/structural.pyi +83 -0
  277. tnfr/telemetry/__init__.py +35 -0
  278. tnfr/telemetry/cache_metrics.py +226 -0
  279. tnfr/telemetry/cache_metrics.pyi +64 -0
  280. tnfr/telemetry/nu_f.py +422 -0
  281. tnfr/telemetry/nu_f.pyi +108 -0
  282. tnfr/telemetry/verbosity.py +36 -0
  283. tnfr/telemetry/verbosity.pyi +15 -0
  284. tnfr/tokens.py +2 -4
  285. tnfr/tokens.pyi +36 -0
  286. tnfr/tools/__init__.py +20 -0
  287. tnfr/tools/domain_templates.py +478 -0
  288. tnfr/tools/sequence_generator.py +846 -0
  289. tnfr/topology/__init__.py +13 -0
  290. tnfr/topology/asymmetry.py +151 -0
  291. tnfr/trace.py +300 -126
  292. tnfr/trace.pyi +42 -0
  293. tnfr/tutorials/__init__.py +38 -0
  294. tnfr/tutorials/autonomous_evolution.py +285 -0
  295. tnfr/tutorials/interactive.py +1576 -0
  296. tnfr/tutorials/structural_metabolism.py +238 -0
  297. tnfr/types.py +743 -12
  298. tnfr/types.pyi +357 -0
  299. tnfr/units.py +68 -0
  300. tnfr/units.pyi +13 -0
  301. tnfr/utils/__init__.py +282 -0
  302. tnfr/utils/__init__.pyi +215 -0
  303. tnfr/utils/cache.py +4223 -0
  304. tnfr/utils/cache.pyi +470 -0
  305. tnfr/{callback_utils.py → utils/callbacks.py} +26 -39
  306. tnfr/utils/callbacks.pyi +49 -0
  307. tnfr/utils/chunks.py +108 -0
  308. tnfr/utils/chunks.pyi +22 -0
  309. tnfr/utils/data.py +428 -0
  310. tnfr/utils/data.pyi +74 -0
  311. tnfr/utils/graph.py +85 -0
  312. tnfr/utils/graph.pyi +10 -0
  313. tnfr/utils/init.py +821 -0
  314. tnfr/utils/init.pyi +80 -0
  315. tnfr/utils/io.py +559 -0
  316. tnfr/utils/io.pyi +66 -0
  317. tnfr/{helpers → utils}/numeric.py +51 -24
  318. tnfr/utils/numeric.pyi +21 -0
  319. tnfr/validation/__init__.py +257 -0
  320. tnfr/validation/__init__.pyi +85 -0
  321. tnfr/validation/compatibility.py +460 -0
  322. tnfr/validation/compatibility.pyi +6 -0
  323. tnfr/validation/config.py +73 -0
  324. tnfr/validation/graph.py +139 -0
  325. tnfr/validation/graph.pyi +18 -0
  326. tnfr/validation/input_validation.py +755 -0
  327. tnfr/validation/invariants.py +712 -0
  328. tnfr/validation/rules.py +253 -0
  329. tnfr/validation/rules.pyi +44 -0
  330. tnfr/validation/runtime.py +279 -0
  331. tnfr/validation/runtime.pyi +28 -0
  332. tnfr/validation/sequence_validator.py +162 -0
  333. tnfr/validation/soft_filters.py +170 -0
  334. tnfr/validation/soft_filters.pyi +32 -0
  335. tnfr/validation/spectral.py +164 -0
  336. tnfr/validation/spectral.pyi +42 -0
  337. tnfr/validation/validator.py +1266 -0
  338. tnfr/validation/window.py +39 -0
  339. tnfr/validation/window.pyi +1 -0
  340. tnfr/visualization/__init__.py +98 -0
  341. tnfr/visualization/cascade_viz.py +256 -0
  342. tnfr/visualization/hierarchy.py +284 -0
  343. tnfr/visualization/sequence_plotter.py +784 -0
  344. tnfr/viz/__init__.py +60 -0
  345. tnfr/viz/matplotlib.py +278 -0
  346. tnfr/viz/matplotlib.pyi +35 -0
  347. tnfr-8.5.0.dist-info/METADATA +573 -0
  348. tnfr-8.5.0.dist-info/RECORD +353 -0
  349. {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/entry_points.txt +1 -0
  350. {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/licenses/LICENSE.md +1 -1
  351. tnfr/collections_utils.py +0 -300
  352. tnfr/config.py +0 -32
  353. tnfr/grammar.py +0 -344
  354. tnfr/graph_utils.py +0 -84
  355. tnfr/helpers/__init__.py +0 -71
  356. tnfr/import_utils.py +0 -228
  357. tnfr/json_utils.py +0 -162
  358. tnfr/logging_utils.py +0 -116
  359. tnfr/presets.py +0 -60
  360. tnfr/validators.py +0 -84
  361. tnfr/value_utils.py +0 -59
  362. tnfr-4.5.2.dist-info/METADATA +0 -379
  363. tnfr-4.5.2.dist-info/RECORD +0 -67
  364. {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/WHEEL +0 -0
  365. {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/top_level.txt +0 -0
tnfr/trace.py CHANGED
@@ -3,62 +3,139 @@
3
3
  Field helpers avoid unnecessary copying by reusing dictionaries stored on
4
4
  the graph whenever possible. Callers are expected to treat returned
5
5
  structures as immutable snapshots.
6
+
7
+ Immutability Guarantees
8
+ -----------------------
9
+ Trace field producers return mappings wrapped in ``MappingProxyType`` to
10
+ prevent accidental mutation. These proxies enforce immutability while avoiding
11
+ unnecessary data copying. Consumers that need to modify trace data should
12
+ create mutable copies using ``dict(proxy)`` or merge patterns like
13
+ ``{**proxy1, **proxy2, "new_key": value}``.
14
+
15
+ Example safe mutation patterns::
16
+
17
+ # Get immutable trace data
18
+ result = gamma_field(G)
19
+ gamma_proxy = result["gamma"]
20
+
21
+ # Cannot mutate directly (TypeError will be raised)
22
+ # gamma_proxy["new_key"] = value # ❌ Error!
23
+
24
+ # Safe pattern: create mutable copy
25
+ mutable = dict(gamma_proxy)
26
+ mutable["new_key"] = value # ✓ OK
27
+
28
+ # Safe pattern: merge with new data
29
+ combined = {**gamma_proxy, "new_key": value} # ✓ OK
6
30
  """
7
31
 
8
32
  from __future__ import annotations
9
33
 
10
- from functools import partial
11
- from typing import Any, Callable, Optional, Protocol, NamedTuple, TypedDict, cast
34
+ import warnings
12
35
  from collections.abc import Iterable, Mapping
36
+ from types import MappingProxyType
37
+ from typing import Any, NamedTuple, Protocol, cast
13
38
 
14
39
  from .constants import TRACE
15
- from .glyph_history import ensure_history, count_glyphs, append_metric
16
- from .import_utils import cached_import
17
- from .graph_utils import get_graph_mapping
18
- from .collections_utils import is_non_string_sequence
40
+ from .glyph_history import append_metric, count_glyphs, ensure_history
41
+ from .metrics.sense_index import _normalise_si_sensitivity_mapping
42
+ from .telemetry.verbosity import (
43
+ TELEMETRY_VERBOSITY_DEFAULT,
44
+ TelemetryVerbosity,
45
+ )
46
+ from .types import (
47
+ SigmaVector,
48
+ TNFRGraph,
49
+ TraceCallback,
50
+ TraceFieldFn,
51
+ TraceFieldMap,
52
+ TraceFieldRegistry,
53
+ TraceMetadata,
54
+ TraceSnapshot,
55
+ )
56
+ from .utils import cached_import, get_graph_mapping, is_non_string_sequence
57
+ from .utils.callbacks import CallbackSpec
19
58
 
20
59
 
21
60
  class _KuramotoFn(Protocol):
22
- def __call__(self, G: Any) -> tuple[float, float]: ...
61
+ def __call__(self, G: TNFRGraph) -> tuple[float, float]: ...
23
62
 
24
63
 
25
64
  class _SigmaVectorFn(Protocol):
26
- def __call__(
27
- self, G: Any, weight_mode: str | None = None
28
- ) -> dict[str, float]: ...
65
+ def __call__(self, G: TNFRGraph, weight_mode: str | None = None) -> SigmaVector: ...
66
+
67
+
68
+ class TraceFieldSpec(NamedTuple):
69
+ """Declarative specification for a trace field producer."""
29
70
 
71
+ name: str
72
+ phase: str
73
+ producer: TraceFieldFn
74
+ tiers: tuple[TelemetryVerbosity, ...]
30
75
 
31
- class CallbackSpec(NamedTuple):
32
- """Specification for a registered callback."""
33
76
 
34
- name: str | None
35
- func: Callable[..., Any]
77
+ TRACE_VERBOSITY_DEFAULT = TELEMETRY_VERBOSITY_DEFAULT
78
+ TRACE_VERBOSITY_PRESETS: dict[str, tuple[str, ...]] = {}
79
+ _TRACE_CAPTURE_ALIASES: Mapping[str, str] = MappingProxyType(
80
+ {
81
+ "glyphs": "glyph_counts",
82
+ }
83
+ )
36
84
 
37
85
 
38
- class TraceMetadata(TypedDict, total=False):
39
- """Metadata captured by trace field functions."""
86
+ def _canonical_capture_name(name: str) -> str:
87
+ """Return the canonical capture field name for ``name``."""
40
88
 
41
- gamma: Mapping[str, Any]
42
- grammar: Mapping[str, Any]
43
- selector: str | None
44
- dnfr_weights: Mapping[str, Any]
45
- si_weights: Mapping[str, Any]
46
- si_sensitivity: Mapping[str, Any]
47
- callbacks: Mapping[str, list[str] | None]
48
- thol_open_nodes: int
49
- kuramoto: Mapping[str, float]
50
- sigma: Mapping[str, float]
51
- glyphs: Mapping[str, int]
89
+ stripped = name.strip()
90
+ alias = _TRACE_CAPTURE_ALIASES.get(stripped)
91
+ if alias is not None:
92
+ return alias
52
93
 
94
+ lowered = stripped.lower()
95
+ alias = _TRACE_CAPTURE_ALIASES.get(lowered)
96
+ if alias is not None:
97
+ return alias
53
98
 
54
- class TraceSnapshot(TraceMetadata, total=False):
55
- """Trace snapshot stored in the history."""
99
+ return stripped
56
100
 
57
- t: float
58
- phase: str
59
101
 
102
+ def _normalise_capture_spec(raw: Any) -> set[str]:
103
+ """Coerce custom capture payloads to a ``set`` of field names."""
60
104
 
61
- def _kuramoto_fallback(G: Any) -> tuple[float, float]:
105
+ if raw is None:
106
+ return set()
107
+ if isinstance(raw, Mapping):
108
+ return {_canonical_capture_name(str(name)) for name in raw.keys()}
109
+ if isinstance(raw, str):
110
+ return {_canonical_capture_name(raw)}
111
+ if isinstance(raw, Iterable):
112
+ return {_canonical_capture_name(str(name)) for name in raw}
113
+ return {_canonical_capture_name(str(raw))}
114
+
115
+
116
+ def _resolve_trace_capture(cfg: Mapping[str, Any]) -> set[str]:
117
+ """Return the capture set declared by ``cfg`` respecting verbosity."""
118
+
119
+ if "capture" in cfg:
120
+ return _normalise_capture_spec(cfg.get("capture"))
121
+
122
+ raw_verbosity = cfg.get("verbosity", TRACE_VERBOSITY_DEFAULT)
123
+ verbosity = str(raw_verbosity).lower()
124
+ fields = TRACE_VERBOSITY_PRESETS.get(verbosity)
125
+ if fields is None:
126
+ warnings.warn(
127
+ (
128
+ "Unknown TRACE verbosity %r; falling back to %s"
129
+ % (raw_verbosity, TRACE_VERBOSITY_DEFAULT)
130
+ ),
131
+ UserWarning,
132
+ stacklevel=3,
133
+ )
134
+ fields = TRACE_VERBOSITY_PRESETS[TRACE_VERBOSITY_DEFAULT]
135
+ return set(fields)
136
+
137
+
138
+ def _kuramoto_fallback(G: TNFRGraph) -> tuple[float, float]:
62
139
  return 0.0, 0.0
63
140
 
64
141
 
@@ -68,9 +145,7 @@ kuramoto_R_psi: _KuramotoFn = cast(
68
145
  )
69
146
 
70
147
 
71
- def _sigma_fallback(
72
- G: Any, _weight_mode: str | None = None
73
- ) -> dict[str, float]:
148
+ def _sigma_fallback(G: TNFRGraph, _weight_mode: str | None = None) -> SigmaVector:
74
149
  """Return a null sigma vector regardless of ``_weight_mode``."""
75
150
 
76
151
  return {"x": 0.0, "y": 0.0, "mag": 0.0, "angle": 0.0, "n": 0}
@@ -79,6 +154,7 @@ def _sigma_fallback(
79
154
  # Public exports for this module
80
155
  __all__ = (
81
156
  "CallbackSpec",
157
+ "TraceFieldSpec",
82
158
  "TraceMetadata",
83
159
  "TraceSnapshot",
84
160
  "register_trace",
@@ -94,24 +170,28 @@ __all__ = (
94
170
 
95
171
 
96
172
  def _trace_setup(
97
- G,
173
+ G: TNFRGraph,
98
174
  ) -> tuple[
99
- Optional[dict[str, Any]], set[str], Optional[dict[str, Any]], Optional[str]
175
+ Mapping[str, Any] | None,
176
+ set[str],
177
+ dict[str, Any] | None,
178
+ str | None,
100
179
  ]:
101
- """Common configuration for trace snapshots.
180
+ """Prepare common configuration for trace snapshots.
102
181
 
103
182
  Returns the active configuration, capture set, history and key under
104
183
  which metadata will be stored. If tracing is disabled returns
105
184
  ``(None, set(), None, None)``.
106
185
  """
107
186
 
108
- cfg = G.graph.get("TRACE", TRACE)
187
+ cfg_raw = G.graph.get("TRACE", TRACE)
188
+ cfg = cfg_raw if isinstance(cfg_raw, Mapping) else TRACE
109
189
  if not cfg.get("enabled", True):
110
190
  return None, set(), None, None
111
191
 
112
- capture: set[str] = set(cfg.get("capture", []))
192
+ capture = _resolve_trace_capture(cfg)
113
193
  hist = ensure_history(G)
114
- key = cfg.get("history_key", "trace_meta")
194
+ key = cast(str | None, cfg.get("history_key", "trace_meta"))
115
195
  return cfg, capture, hist, key
116
196
 
117
197
 
@@ -122,21 +202,22 @@ def _callback_names(
122
202
  if isinstance(callbacks, Mapping):
123
203
  callbacks = callbacks.values()
124
204
  return [
125
- cb.name
126
- if cb.name is not None
127
- else str(getattr(cb.func, "__name__", "fn"))
205
+ cb.name if cb.name is not None else str(getattr(cb.func, "__name__", "fn"))
128
206
  for cb in callbacks
129
207
  ]
130
208
 
131
209
 
132
- def mapping_field(G: Any, graph_key: str, out_key: str) -> TraceMetadata:
133
- """Helper to copy mappings from ``G.graph`` into trace output."""
210
+ EMPTY_MAPPING: Mapping[str, Any] = MappingProxyType({})
211
+
212
+
213
+ def mapping_field(G: TNFRGraph, graph_key: str, out_key: str) -> TraceMetadata:
214
+ """Copy mappings from ``G.graph`` into trace output."""
134
215
  mapping = get_graph_mapping(
135
- G, graph_key, f"G.graph[{graph_key!r}] no es un mapeo; se ignora"
216
+ G, graph_key, f"G.graph[{graph_key!r}] is not a mapping; ignoring"
136
217
  )
137
218
  if mapping is None:
138
219
  return {}
139
- return cast(TraceMetadata, {out_key: mapping})
220
+ return {out_key: mapping}
140
221
 
141
222
 
142
223
  # -------------------------
@@ -145,10 +226,8 @@ def mapping_field(G: Any, graph_key: str, out_key: str) -> TraceMetadata:
145
226
 
146
227
 
147
228
  def _new_trace_meta(
148
- G, phase: str
149
- ) -> Optional[
150
- tuple[TraceSnapshot, set[str], Optional[dict[str, Any]], Optional[str]]
151
- ]:
229
+ G: TNFRGraph, phase: str
230
+ ) -> tuple[TraceSnapshot, set[str], dict[str, Any] | None, str | None] | None:
152
231
  """Initialise trace metadata for a ``phase``.
153
232
 
154
233
  Wraps :func:`_trace_setup` and creates the base structure with timestamp
@@ -168,9 +247,7 @@ def _new_trace_meta(
168
247
  # -------------------------
169
248
 
170
249
 
171
- def _trace_capture(
172
- G, phase: str, fields: Mapping[str, Callable[[Any], TraceMetadata]]
173
- ) -> None:
250
+ def _trace_capture(G: TNFRGraph, phase: str, fields: TraceFieldMap) -> None:
174
251
  """Capture ``fields`` for ``phase`` and store the snapshot.
175
252
 
176
253
  A :class:`TraceSnapshot` is appended to the configured history when
@@ -187,7 +264,7 @@ def _trace_capture(
187
264
  return
188
265
  for name, getter in fields.items():
189
266
  if name in capture:
190
- meta.update(cast(TraceSnapshot, getter(G)))
267
+ meta.update(getter(G))
191
268
  if hist is None or key is None:
192
269
  return
193
270
  append_metric(hist, key, meta)
@@ -197,55 +274,70 @@ def _trace_capture(
197
274
  # Registry
198
275
  # -------------------------
199
276
 
200
-
201
- TRACE_FIELDS: dict[str, dict[str, Callable[[Any], TraceMetadata]]] = {}
277
+ TRACE_FIELDS: TraceFieldRegistry = {}
202
278
 
203
279
 
204
- def register_trace_field(
205
- phase: str, name: str, func: Callable[[Any], TraceMetadata]
206
- ) -> None:
280
+ def register_trace_field(phase: str, name: str, func: TraceFieldFn) -> None:
207
281
  """Register ``func`` to populate trace field ``name`` during ``phase``."""
208
282
 
209
283
  TRACE_FIELDS.setdefault(phase, {})[name] = func
210
284
 
211
285
 
212
- gamma_field = partial(mapping_field, graph_key="GAMMA", out_key="gamma")
286
+ def gamma_field(G: TNFRGraph) -> TraceMetadata:
287
+ """Expose γ-field metadata stored under ``G.graph['GAMMA']``."""
213
288
 
289
+ return mapping_field(G, "GAMMA", "gamma")
214
290
 
215
- grammar_field = partial(mapping_field, graph_key="GRAMMAR_CANON", out_key="grammar")
216
291
 
292
+ def grammar_field(G: TNFRGraph) -> TraceMetadata:
293
+ """Expose canonical grammar metadata for trace emission."""
217
294
 
218
- dnfr_weights_field = partial(
219
- mapping_field, graph_key="DNFR_WEIGHTS", out_key="dnfr_weights"
220
- )
295
+ return mapping_field(G, "GRAMMAR_CANON", "grammar")
221
296
 
222
297
 
223
- def selector_field(G: Any) -> TraceMetadata:
298
+ def dnfr_weights_field(G: TNFRGraph) -> TraceMetadata:
299
+ return mapping_field(G, "DNFR_WEIGHTS", "dnfr_weights")
300
+
301
+
302
+ def selector_field(G: TNFRGraph) -> TraceMetadata:
224
303
  sel = G.graph.get("glyph_selector")
225
- return cast(TraceMetadata, {"selector": getattr(sel, "__name__", str(sel)) if sel else None})
304
+ selector_name = getattr(sel, "__name__", str(sel)) if sel else None
305
+ return {"selector": selector_name}
226
306
 
227
307
 
228
- _si_weights_field = partial(mapping_field, graph_key="_Si_weights", out_key="si_weights")
308
+ def _si_weights_field(G: TNFRGraph) -> TraceMetadata:
309
+ weights = mapping_field(G, "_Si_weights", "si_weights")
310
+ if weights:
311
+ return weights
312
+ return {"si_weights": EMPTY_MAPPING}
229
313
 
230
314
 
231
- _si_sensitivity_field = partial(
232
- mapping_field, graph_key="_Si_sensitivity", out_key="si_sensitivity"
233
- )
315
+ def _si_sensitivity_field(G: TNFRGraph) -> TraceMetadata:
316
+ mapping = get_graph_mapping(
317
+ G,
318
+ "_Si_sensitivity",
319
+ "G.graph['_Si_sensitivity'] is not a mapping; ignoring",
320
+ )
321
+ if mapping is None:
322
+ return {"si_sensitivity": EMPTY_MAPPING}
234
323
 
324
+ normalised = _normalise_si_sensitivity_mapping(mapping, warn=True)
235
325
 
236
- def si_weights_field(G: Any) -> TraceMetadata:
326
+ if normalised != mapping:
327
+ G.graph["_Si_sensitivity"] = normalised
328
+
329
+ return {"si_sensitivity": MappingProxyType(normalised)}
330
+
331
+
332
+ def si_weights_field(G: TNFRGraph) -> TraceMetadata:
237
333
  """Return sense-plane weights and sensitivity."""
238
334
 
239
- return cast(
240
- TraceMetadata,
241
- {
242
- **(_si_weights_field(G) or {"si_weights": {}}),
243
- **(_si_sensitivity_field(G) or {"si_sensitivity": {}}),
244
- },
245
- )
335
+ weights = _si_weights_field(G)
336
+ sensitivity = _si_sensitivity_field(G)
337
+ return {**weights, **sensitivity}
246
338
 
247
339
 
248
- def callbacks_field(G: Any) -> TraceMetadata:
340
+ def callbacks_field(G: TNFRGraph) -> TraceMetadata:
249
341
  cb = G.graph.get("callbacks")
250
342
  if not isinstance(cb, Mapping):
251
343
  return {}
@@ -255,24 +347,24 @@ def callbacks_field(G: Any) -> TraceMetadata:
255
347
  out[phase] = _callback_names(cb_map)
256
348
  else:
257
349
  out[phase] = None
258
- return cast(TraceMetadata, {"callbacks": out})
350
+ return {"callbacks": out}
259
351
 
260
352
 
261
- def thol_state_field(G: Any) -> TraceMetadata:
353
+ def thol_state_field(G: TNFRGraph) -> TraceMetadata:
262
354
  th_open = 0
263
355
  for _, nd in G.nodes(data=True):
264
356
  st = nd.get("_GRAM", {})
265
357
  if st.get("thol_open", False):
266
358
  th_open += 1
267
- return cast(TraceMetadata, {"thol_open_nodes": th_open})
359
+ return {"thol_open_nodes": th_open}
268
360
 
269
361
 
270
- def kuramoto_field(G: Any) -> TraceMetadata:
362
+ def kuramoto_field(G: TNFRGraph) -> TraceMetadata:
271
363
  R, psi = kuramoto_R_psi(G)
272
- return cast(TraceMetadata, {"kuramoto": {"R": float(R), "psi": float(psi)}})
364
+ return {"kuramoto": {"R": float(R), "psi": float(psi)}}
273
365
 
274
366
 
275
- def sigma_field(G: Any) -> TraceMetadata:
367
+ def sigma_field(G: TNFRGraph) -> TraceMetadata:
276
368
  sigma_vector_from_graph: _SigmaVectorFn = cast(
277
369
  _SigmaVectorFn,
278
370
  cached_import(
@@ -282,52 +374,134 @@ def sigma_field(G: Any) -> TraceMetadata:
282
374
  ),
283
375
  )
284
376
  sv = sigma_vector_from_graph(G)
285
- return cast(
286
- TraceMetadata,
287
- {
288
- "sigma": {
289
- "x": float(sv.get("x", 0.0)),
290
- "y": float(sv.get("y", 0.0)),
291
- "mag": float(sv.get("mag", 0.0)),
292
- "angle": float(sv.get("angle", 0.0)),
293
- }
294
- },
295
- )
296
-
297
-
298
- def glyph_counts_field(G: Any) -> TraceMetadata:
299
- """Return glyph count snapshot.
300
-
301
- ``count_glyphs`` already produces a fresh mapping so no additional copy
302
- is taken. Treat the returned mapping as read-only.
377
+ return {
378
+ "sigma": {
379
+ "x": float(sv.get("x", 0.0)),
380
+ "y": float(sv.get("y", 0.0)),
381
+ "mag": float(sv.get("mag", 0.0)),
382
+ "angle": float(sv.get("angle", 0.0)),
383
+ }
384
+ }
385
+
386
+
387
+ def glyph_counts_field(G: TNFRGraph) -> TraceMetadata:
388
+ """Return structural operator application count snapshot.
389
+
390
+ Provides a snapshot of which structural operator symbols (glyphs) have been
391
+ applied in the current step. ``count_glyphs`` already produces a fresh
392
+ mapping so no additional copy is taken. Treat the returned mapping as read-only.
303
393
  """
304
394
 
305
395
  cnt = count_glyphs(G, window=1)
306
- return cast(TraceMetadata, {"glyphs": cnt})
307
-
308
-
309
- # Pre-register default fields
310
- register_trace_field("before", "gamma", gamma_field)
311
- register_trace_field("before", "grammar", grammar_field)
312
- register_trace_field("before", "selector", selector_field)
313
- register_trace_field("before", "dnfr_weights", dnfr_weights_field)
314
- register_trace_field("before", "si_weights", si_weights_field)
315
- register_trace_field("before", "callbacks", callbacks_field)
316
- register_trace_field("before", "thol_open_nodes", thol_state_field)
396
+ return {"glyphs": cnt}
397
+
398
+
399
+ TRACE_FIELD_SPECS: tuple[TraceFieldSpec, ...] = (
400
+ TraceFieldSpec(
401
+ name="gamma",
402
+ phase="before",
403
+ producer=gamma_field,
404
+ tiers=(
405
+ TelemetryVerbosity.BASIC,
406
+ TelemetryVerbosity.DETAILED,
407
+ TelemetryVerbosity.DEBUG,
408
+ ),
409
+ ),
410
+ TraceFieldSpec(
411
+ name="grammar",
412
+ phase="before",
413
+ producer=grammar_field,
414
+ tiers=(
415
+ TelemetryVerbosity.BASIC,
416
+ TelemetryVerbosity.DETAILED,
417
+ TelemetryVerbosity.DEBUG,
418
+ ),
419
+ ),
420
+ TraceFieldSpec(
421
+ name="selector",
422
+ phase="before",
423
+ producer=selector_field,
424
+ tiers=(
425
+ TelemetryVerbosity.BASIC,
426
+ TelemetryVerbosity.DETAILED,
427
+ TelemetryVerbosity.DEBUG,
428
+ ),
429
+ ),
430
+ TraceFieldSpec(
431
+ name="dnfr_weights",
432
+ phase="before",
433
+ producer=dnfr_weights_field,
434
+ tiers=(
435
+ TelemetryVerbosity.BASIC,
436
+ TelemetryVerbosity.DETAILED,
437
+ TelemetryVerbosity.DEBUG,
438
+ ),
439
+ ),
440
+ TraceFieldSpec(
441
+ name="si_weights",
442
+ phase="before",
443
+ producer=si_weights_field,
444
+ tiers=(
445
+ TelemetryVerbosity.BASIC,
446
+ TelemetryVerbosity.DETAILED,
447
+ TelemetryVerbosity.DEBUG,
448
+ ),
449
+ ),
450
+ TraceFieldSpec(
451
+ name="callbacks",
452
+ phase="before",
453
+ producer=callbacks_field,
454
+ tiers=(
455
+ TelemetryVerbosity.BASIC,
456
+ TelemetryVerbosity.DETAILED,
457
+ TelemetryVerbosity.DEBUG,
458
+ ),
459
+ ),
460
+ TraceFieldSpec(
461
+ name="thol_open_nodes",
462
+ phase="before",
463
+ producer=thol_state_field,
464
+ tiers=(
465
+ TelemetryVerbosity.BASIC,
466
+ TelemetryVerbosity.DETAILED,
467
+ TelemetryVerbosity.DEBUG,
468
+ ),
469
+ ),
470
+ TraceFieldSpec(
471
+ name="kuramoto",
472
+ phase="after",
473
+ producer=kuramoto_field,
474
+ tiers=(TelemetryVerbosity.DETAILED, TelemetryVerbosity.DEBUG),
475
+ ),
476
+ TraceFieldSpec(
477
+ name="sigma",
478
+ phase="after",
479
+ producer=sigma_field,
480
+ tiers=(TelemetryVerbosity.DETAILED, TelemetryVerbosity.DEBUG),
481
+ ),
482
+ TraceFieldSpec(
483
+ name="glyph_counts",
484
+ phase="after",
485
+ producer=glyph_counts_field,
486
+ tiers=(TelemetryVerbosity.DEBUG,),
487
+ ),
488
+ )
317
489
 
318
- register_trace_field("after", "kuramoto", kuramoto_field)
319
- register_trace_field("after", "sigma", sigma_field)
320
- register_trace_field("after", "glyph_counts", glyph_counts_field)
490
+ TRACE_VERBOSITY_PRESETS = {
491
+ level.value: tuple(spec.name for spec in TRACE_FIELD_SPECS if level in spec.tiers)
492
+ for level in TelemetryVerbosity
493
+ }
321
494
 
495
+ for spec in TRACE_FIELD_SPECS:
496
+ register_trace_field(spec.phase, spec.name, spec.producer)
322
497
 
323
498
  # -------------------------
324
499
  # API
325
500
  # -------------------------
326
501
 
327
502
 
328
- def register_trace(G) -> None:
329
- """Enable before/after-step snapshots and dump operational metadata
330
- to history.
503
+ def register_trace(G: TNFRGraph) -> None:
504
+ """Enable before/after-step snapshots and dump operational metadata to history.
331
505
 
332
506
  Trace snapshots are stored as :class:`TraceSnapshot` entries in
333
507
  ``G.graph['history'][TRACE.history_key]`` with:
@@ -349,16 +523,16 @@ def register_trace(G) -> None:
349
523
  if G.graph.get("_trace_registered"):
350
524
  return
351
525
 
352
- from .callback_utils import callback_manager
526
+ from .utils import callback_manager
353
527
 
354
528
  for phase in TRACE_FIELDS.keys():
355
529
  event = f"{phase}_step"
356
530
 
357
- def _make_cb(ph):
358
- def _cb(G, ctx: dict[str, Any] | None = None):
531
+ def _make_cb(ph: str) -> TraceCallback:
532
+ def _cb(graph: TNFRGraph, ctx: dict[str, Any]) -> None:
359
533
  del ctx
360
534
 
361
- _trace_capture(G, ph, TRACE_FIELDS.get(ph, {}))
535
+ _trace_capture(graph, ph, TRACE_FIELDS.get(ph, {}))
362
536
 
363
537
  return _cb
364
538
 
tnfr/trace.pyi ADDED
@@ -0,0 +1,42 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterable, Mapping
4
+ from typing import Any, Callable, NamedTuple
5
+
6
+ from .types import (
7
+ TNFRGraph,
8
+ TraceFieldFn,
9
+ TraceFieldMap,
10
+ TraceFieldRegistry,
11
+ TraceMetadata,
12
+ TraceSnapshot,
13
+ )
14
+
15
+ __all__: tuple[str, ...]
16
+
17
+ def __getattr__(name: str) -> Any: ...
18
+
19
+ class CallbackSpec(NamedTuple):
20
+ name: str | None
21
+ func: Callable[..., Any]
22
+
23
+ kuramoto_R_psi: Callable[[TNFRGraph], tuple[float, float]]
24
+ TRACE_FIELDS: TraceFieldRegistry
25
+
26
+ def _callback_names(
27
+ callbacks: Mapping[str, CallbackSpec] | Iterable[CallbackSpec],
28
+ ) -> list[str]: ...
29
+ def mapping_field(G: TNFRGraph, graph_key: str, out_key: str) -> TraceMetadata: ...
30
+ def _trace_capture(G: TNFRGraph, phase: str, fields: TraceFieldMap) -> None: ...
31
+ def register_trace_field(phase: str, name: str, func: TraceFieldFn) -> None: ...
32
+ def gamma_field(G: TNFRGraph) -> TraceMetadata: ...
33
+ def grammar_field(G: TNFRGraph) -> TraceMetadata: ...
34
+ def dnfr_weights_field(G: TNFRGraph) -> TraceMetadata: ...
35
+ def selector_field(G: TNFRGraph) -> TraceMetadata: ...
36
+ def si_weights_field(G: TNFRGraph) -> TraceMetadata: ...
37
+ def callbacks_field(G: TNFRGraph) -> TraceMetadata: ...
38
+ def thol_state_field(G: TNFRGraph) -> TraceMetadata: ...
39
+ def kuramoto_field(G: TNFRGraph) -> TraceMetadata: ...
40
+ def sigma_field(G: TNFRGraph) -> TraceMetadata: ...
41
+ def glyph_counts_field(G: TNFRGraph) -> TraceMetadata: ...
42
+ def register_trace(G: TNFRGraph) -> None: ...
@@ -0,0 +1,38 @@
1
+ """Interactive tutorials for learning TNFR step-by-step.
2
+
3
+ This module provides guided, hands-on tutorials that introduce TNFR
4
+ concepts progressively. Each tutorial:
5
+
6
+ 1. Explains TNFR concepts in plain language
7
+ 2. Shows working code examples
8
+ 3. Displays real-time results with interpretation
9
+ 4. Builds from simple to advanced concepts
10
+ 5. Maintains full TNFR theoretical fidelity
11
+
12
+ All tutorials respect TNFR canonical invariants and can be run
13
+ independently or as a learning sequence.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ __all__ = [
19
+ "hello_tnfr",
20
+ "biological_example",
21
+ "social_network_example",
22
+ "technology_example",
23
+ "team_communication_example",
24
+ "adaptive_ai_example",
25
+ "oz_dissonance_tutorial",
26
+ "run_all_tutorials",
27
+ ]
28
+
29
+ from .interactive import (
30
+ hello_tnfr,
31
+ biological_example,
32
+ social_network_example,
33
+ technology_example,
34
+ team_communication_example,
35
+ adaptive_ai_example,
36
+ oz_dissonance_tutorial,
37
+ run_all_tutorials,
38
+ )