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
@@ -0,0 +1,39 @@
1
+ """Primitive window validation helpers without heavy dependencies."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numbers
6
+
7
+ __all__ = ["validate_window"]
8
+
9
+
10
+ def validate_window(window: int, *, positive: bool = False) -> int:
11
+ """Validate ``window`` as an integer and return it.
12
+
13
+ Parameters
14
+ ----------
15
+ window:
16
+ Value to coerce into an integer window size.
17
+ positive:
18
+ When ``True`` the window must be strictly greater than zero; otherwise
19
+ zero is accepted. Negative values are never permitted.
20
+
21
+ Returns
22
+ -------
23
+ int
24
+ The validated integer window size.
25
+
26
+ Raises
27
+ ------
28
+ TypeError
29
+ If ``window`` is not an integer value.
30
+ ValueError
31
+ If ``window`` violates the positivity constraint.
32
+ """
33
+
34
+ if isinstance(window, bool) or not isinstance(window, numbers.Integral):
35
+ raise TypeError("'window' must be an integer")
36
+ if window < 0 or (positive and window == 0):
37
+ kind = "positive" if positive else "non-negative"
38
+ raise ValueError(f"'window'={window} must be {kind}")
39
+ return int(window)
@@ -0,0 +1 @@
1
+ def validate_window(window: int, *, positive: bool = ...) -> int: ...
@@ -0,0 +1,98 @@
1
+ """Visualization tools for TNFR operator sequences and structural analysis.
2
+
3
+ This module provides advanced visualization capabilities for:
4
+ - Sequence flow diagrams with compatibility-colored transitions
5
+ - Health metrics dashboards with radar charts and gauges
6
+ - Pattern analysis with component highlighting
7
+ - Frequency timelines showing structural evolution
8
+ - Cascade propagation and temporal dynamics
9
+ - Hierarchical bifurcation structures (NEW)
10
+
11
+ Requires matplotlib for plotting (optional). Install with::
12
+
13
+ pip install tnfr[viz]
14
+
15
+ Hierarchy visualization (ASCII) has no external dependencies.
16
+
17
+ Examples
18
+ --------
19
+ >>> from tnfr.visualization import SequenceVisualizer
20
+ >>> from tnfr.operators.grammar import validate_sequence_with_health
21
+ >>>
22
+ >>> sequence = ["emission", "reception", "coherence", "silence"]
23
+ >>> result = validate_sequence_with_health(sequence)
24
+ >>>
25
+ >>> visualizer = SequenceVisualizer()
26
+ >>> fig, ax = visualizer.plot_sequence_flow(sequence, result.health_metrics)
27
+ >>> fig.savefig("sequence_flow.png")
28
+
29
+ >>> # Cascade visualization
30
+ >>> from tnfr.visualization import plot_cascade_propagation, plot_cascade_timeline
31
+ >>> fig = plot_cascade_propagation(G)
32
+ >>> fig.savefig("cascade_propagation.png")
33
+
34
+ >>> # Hierarchy visualization (no matplotlib required)
35
+ >>> from tnfr.visualization import print_bifurcation_hierarchy
36
+ >>> print_bifurcation_hierarchy(G, node)
37
+ """
38
+
39
+ # Always available (no dependencies)
40
+ from .hierarchy import (
41
+ print_bifurcation_hierarchy,
42
+ get_hierarchy_info,
43
+ )
44
+
45
+ _import_error: ImportError | None = None
46
+
47
+ try:
48
+ from .sequence_plotter import SequenceVisualizer
49
+ from .cascade_viz import (
50
+ plot_cascade_propagation,
51
+ plot_cascade_timeline,
52
+ plot_cascade_metrics_summary,
53
+ )
54
+
55
+ __all__ = [
56
+ "SequenceVisualizer",
57
+ "plot_cascade_propagation",
58
+ "plot_cascade_timeline",
59
+ "plot_cascade_metrics_summary",
60
+ "print_bifurcation_hierarchy",
61
+ "get_hierarchy_info",
62
+ ]
63
+ except ImportError as _import_err:
64
+ _import_error = _import_err
65
+ from typing import Any as _Any
66
+
67
+ def _missing_viz_dependency(*args: _Any, **kwargs: _Any) -> None:
68
+ missing_deps = []
69
+ try:
70
+ import matplotlib # noqa: F401
71
+ except ImportError:
72
+ missing_deps.append("matplotlib")
73
+
74
+ if missing_deps:
75
+ deps_str = " and ".join(missing_deps)
76
+ raise ImportError(
77
+ f"Visualization functions require {deps_str}. "
78
+ "Install with: pip install tnfr[viz]"
79
+ ) from _import_error
80
+ else:
81
+ raise ImportError(
82
+ "Visualization functions are not available. "
83
+ "Install with: pip install tnfr[viz]"
84
+ ) from _import_error
85
+
86
+ SequenceVisualizer = _missing_viz_dependency # type: ignore[assignment]
87
+ plot_cascade_propagation = _missing_viz_dependency # type: ignore[assignment]
88
+ plot_cascade_timeline = _missing_viz_dependency # type: ignore[assignment]
89
+ plot_cascade_metrics_summary = _missing_viz_dependency # type: ignore[assignment]
90
+
91
+ __all__ = [
92
+ "SequenceVisualizer",
93
+ "plot_cascade_propagation",
94
+ "plot_cascade_timeline",
95
+ "plot_cascade_metrics_summary",
96
+ "print_bifurcation_hierarchy",
97
+ "get_hierarchy_info",
98
+ ]
@@ -0,0 +1,256 @@
1
+ """Visualization tools for THOL cascade dynamics.
2
+
3
+ Provides plotting functions to visualize cascade propagation across networks,
4
+ temporal evolution of cascades, and collective emergence patterns.
5
+
6
+ TNFR Canonical Principle
7
+ -------------------------
8
+ From "El pulso que nos atraviesa" (TNFR Manual, §2.2.10):
9
+
10
+ "THOL actúa como modulador central de plasticidad. Es el glifo que
11
+ permite a la red reorganizar su topología sin intervención externa."
12
+
13
+ These visualizations make cascade dynamics observable and traceable,
14
+ enabling scientific validation and debugging of self-organization.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from typing import TYPE_CHECKING, Any
20
+
21
+ if TYPE_CHECKING:
22
+ from ..types import TNFRGraph
23
+
24
+ import matplotlib.pyplot as plt
25
+
26
+ from ..alias import get_attr
27
+ from ..constants.aliases import ALIAS_EPI
28
+
29
+ try:
30
+ import networkx as nx
31
+
32
+ HAS_NETWORKX = True
33
+ except ImportError:
34
+ HAS_NETWORKX = False
35
+
36
+ __all__ = [
37
+ "plot_cascade_propagation",
38
+ "plot_cascade_timeline",
39
+ ]
40
+
41
+
42
+ def plot_cascade_propagation(G: TNFRGraph, figsize: tuple[int, int] = (12, 8)):
43
+ """Visualize THOL cascade propagation across network.
44
+
45
+ Creates network diagram with:
46
+ - Node size = EPI magnitude
47
+ - Node color = bifurcation occurred (red) or not (blue)
48
+ - Edge thickness = coupling strength
49
+ - Arrows = propagation direction
50
+
51
+ Parameters
52
+ ----------
53
+ G : TNFRGraph
54
+ Graph with THOL propagation history
55
+ figsize : tuple[int, int], default (12, 8)
56
+ Figure size in inches (width, height)
57
+
58
+ Returns
59
+ -------
60
+ matplotlib.figure.Figure
61
+ Figure object containing the cascade visualization
62
+
63
+ Notes
64
+ -----
65
+ TNFR Principle: Cascade propagation shows how self-organization
66
+ spreads through phase-aligned neighbors. Red nodes = bifurcation source,
67
+ blue nodes = unaffected. Arrow thickness = propagation strength.
68
+
69
+ Examples
70
+ --------
71
+ >>> # After running THOL sequence with cascades
72
+ >>> fig = plot_cascade_propagation(G)
73
+ >>> fig.savefig("cascade_propagation.png")
74
+ >>> plt.show()
75
+ """
76
+ if not HAS_NETWORKX:
77
+ raise ImportError("NetworkX required for cascade visualization")
78
+
79
+ propagations = G.graph.get("thol_propagations", [])
80
+
81
+ fig, ax = plt.subplots(figsize=figsize)
82
+
83
+ # Identify nodes that bifurcated (source nodes in propagations)
84
+ bifurcated_nodes = set()
85
+ for prop in propagations:
86
+ bifurcated_nodes.add(prop["source_node"])
87
+
88
+ # Node colors: red = bifurcated, lightblue = normal
89
+ node_colors = ["red" if n in bifurcated_nodes else "lightblue" for n in G.nodes]
90
+
91
+ # Node sizes based on EPI magnitude
92
+ node_sizes = []
93
+ for n in G.nodes:
94
+ epi = float(get_attr(G.nodes[n], ALIAS_EPI, 0.5))
95
+ node_sizes.append(1000 * epi)
96
+
97
+ # Compute layout
98
+ pos = nx.spring_layout(G, seed=42)
99
+
100
+ # Draw network structure
101
+ nx.draw_networkx_nodes(
102
+ G, pos, node_color=node_colors, node_size=node_sizes, ax=ax, alpha=0.8
103
+ )
104
+ nx.draw_networkx_edges(G, pos, alpha=0.3, ax=ax)
105
+ nx.draw_networkx_labels(G, pos, ax=ax, font_size=10)
106
+
107
+ # Draw propagation arrows
108
+ for prop in propagations:
109
+ source = prop["source_node"]
110
+ for target, strength in prop["propagations"]:
111
+ if source in pos and target in pos:
112
+ ax.annotate(
113
+ "",
114
+ xy=pos[target],
115
+ xytext=pos[source],
116
+ arrowprops=dict(
117
+ arrowstyle="->",
118
+ color="red",
119
+ lw=2 * strength,
120
+ alpha=0.7,
121
+ ),
122
+ )
123
+
124
+ ax.set_title("THOL Cascade Propagation", fontsize=14, fontweight="bold")
125
+ ax.axis("off")
126
+ plt.tight_layout()
127
+ return fig
128
+
129
+
130
+ def plot_cascade_timeline(G: TNFRGraph, figsize: tuple[int, int] = (10, 5)):
131
+ """Plot temporal evolution of cascade events.
132
+
133
+ Creates scatter plot showing:
134
+ - X-axis: Timestamp (operator sequence step)
135
+ - Y-axis: Number of propagation targets
136
+ - Size: Indicates cascade magnitude
137
+
138
+ Parameters
139
+ ----------
140
+ G : TNFRGraph
141
+ Graph with THOL propagation history
142
+ figsize : tuple[int, int], default (10, 5)
143
+ Figure size in inches (width, height)
144
+
145
+ Returns
146
+ -------
147
+ matplotlib.figure.Figure or None
148
+ Figure object containing the timeline, or None if no cascades
149
+
150
+ Notes
151
+ -----
152
+ TNFR Principle: Temporal evolution reveals cascade patterns.
153
+ Spikes indicate strong propagation events; clusters indicate
154
+ sustained collective reorganization.
155
+
156
+ Examples
157
+ --------
158
+ >>> # After running THOL sequence with cascades
159
+ >>> fig = plot_cascade_timeline(G)
160
+ >>> if fig:
161
+ ... fig.savefig("cascade_timeline.png")
162
+ ... plt.show()
163
+ """
164
+ propagations = G.graph.get("thol_propagations", [])
165
+
166
+ if not propagations:
167
+ print("No cascade events to plot")
168
+ return None
169
+
170
+ timestamps = [p["timestamp"] for p in propagations]
171
+ cascade_sizes = [len(p["propagations"]) for p in propagations]
172
+
173
+ fig, ax = plt.subplots(figsize=figsize)
174
+ ax.scatter(timestamps, cascade_sizes, s=100, alpha=0.7, color="darkred")
175
+ ax.plot(timestamps, cascade_sizes, linestyle="--", alpha=0.5, color="gray")
176
+
177
+ ax.set_xlabel("Timestamp (operator sequence step)", fontsize=12)
178
+ ax.set_ylabel("Propagation Targets", fontsize=12)
179
+ ax.set_title("THOL Cascade Evolution", fontsize=14, fontweight="bold")
180
+ ax.grid(alpha=0.3)
181
+
182
+ plt.tight_layout()
183
+ return fig
184
+
185
+
186
+ def plot_cascade_metrics_summary(
187
+ G: TNFRGraph,
188
+ node_metrics: dict[Any, dict[str, Any]],
189
+ figsize: tuple[int, int] = (14, 6),
190
+ ):
191
+ """Create comprehensive cascade metrics dashboard.
192
+
193
+ Creates multi-panel visualization showing:
194
+ - Panel 1: Cascade depth distribution
195
+ - Panel 2: Sub-EPI coherence over time
196
+ - Panel 3: Metabolic activity index
197
+
198
+ Parameters
199
+ ----------
200
+ G : TNFRGraph
201
+ Graph with THOL history
202
+ node_metrics : dict
203
+ Dictionary mapping node IDs to their THOL metrics
204
+ figsize : tuple[int, int], default (14, 6)
205
+ Figure size in inches (width, height)
206
+
207
+ Returns
208
+ -------
209
+ matplotlib.figure.Figure
210
+ Figure object containing the dashboard
211
+
212
+ Notes
213
+ -----
214
+ TNFR Principle: Complete observability requires multiple metrics.
215
+ This dashboard provides holistic view of self-organization dynamics.
216
+
217
+ Examples
218
+ --------
219
+ >>> # Collect metrics during sequence
220
+ >>> metrics_by_node = {}
221
+ >>> for node in G.nodes:
222
+ ... metrics_by_node[node] = self_organization_metrics(G, node, ...)
223
+ >>> fig = plot_cascade_metrics_summary(G, metrics_by_node)
224
+ >>> fig.savefig("cascade_metrics_dashboard.png")
225
+ """
226
+ fig, axes = plt.subplots(1, 3, figsize=figsize)
227
+
228
+ # Panel 1: Cascade depth distribution
229
+ depths = [m.get("cascade_depth", 0) for m in node_metrics.values()]
230
+ axes[0].hist(depths, bins=range(max(depths) + 2), alpha=0.7, color="steelblue")
231
+ axes[0].set_xlabel("Cascade Depth", fontsize=11)
232
+ axes[0].set_ylabel("Count", fontsize=11)
233
+ axes[0].set_title("Cascade Depth Distribution", fontsize=12, fontweight="bold")
234
+ axes[0].grid(alpha=0.3)
235
+
236
+ # Panel 2: Sub-EPI coherence
237
+ coherences = [m.get("subepi_coherence", 0) for m in node_metrics.values()]
238
+ node_ids = list(node_metrics.keys())
239
+ axes[1].bar(range(len(node_ids)), coherences, alpha=0.7, color="forestgreen")
240
+ axes[1].set_xlabel("Node Index", fontsize=11)
241
+ axes[1].set_ylabel("Coherence [0,1]", fontsize=11)
242
+ axes[1].set_title("Sub-EPI Collective Coherence", fontsize=12, fontweight="bold")
243
+ axes[1].axhline(0.5, color="red", linestyle="--", alpha=0.5, label="Threshold")
244
+ axes[1].legend()
245
+ axes[1].grid(alpha=0.3)
246
+
247
+ # Panel 3: Metabolic activity index
248
+ activities = [m.get("metabolic_activity_index", 0) for m in node_metrics.values()]
249
+ axes[2].bar(range(len(node_ids)), activities, alpha=0.7, color="darkorange")
250
+ axes[2].set_xlabel("Node Index", fontsize=11)
251
+ axes[2].set_ylabel("Activity [0,1]", fontsize=11)
252
+ axes[2].set_title("Metabolic Activity Index", fontsize=12, fontweight="bold")
253
+ axes[2].grid(alpha=0.3)
254
+
255
+ plt.tight_layout()
256
+ return fig
@@ -0,0 +1,284 @@
1
+ """Hierarchical bifurcation visualization for THOL operator.
2
+
3
+ Provides utilities to visualize and inspect nested THOL bifurcation structures,
4
+ supporting operational fractality analysis and debugging of complex emergent
5
+ hierarchies.
6
+
7
+ TNFR Canonical Principle
8
+ -------------------------
9
+ From THOL_ENCAPSULATION_GUIDE.md:
10
+
11
+ "THOL windows can be nested to arbitrary depth, reflecting TNFR's
12
+ fractal structure."
13
+
14
+ This module implements ASCII tree visualization of bifurcation hierarchies,
15
+ enabling rapid inspection of multi-level emergent structures without external
16
+ visualization dependencies.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from typing import TYPE_CHECKING
22
+
23
+ if TYPE_CHECKING:
24
+ from ..types import NodeId, TNFRGraph
25
+
26
+ from ..alias import get_attr
27
+ from ..constants.aliases import ALIAS_EPI
28
+
29
+ __all__ = [
30
+ "print_bifurcation_hierarchy",
31
+ "get_hierarchy_info",
32
+ ]
33
+
34
+
35
+ def print_bifurcation_hierarchy(
36
+ G: TNFRGraph,
37
+ node: NodeId,
38
+ indent: int = 0,
39
+ max_depth: int | None = None,
40
+ ) -> None:
41
+ """Print ASCII tree of bifurcation hierarchy.
42
+
43
+ Recursively traverses THOL bifurcations and displays them as an ASCII
44
+ tree structure, showing EPI values and bifurcation levels at each node.
45
+
46
+ Parameters
47
+ ----------
48
+ G : TNFRGraph
49
+ Graph containing bifurcation structure
50
+ node : NodeId
51
+ Root node to start visualization from
52
+ indent : int, optional
53
+ Current indentation level (used internally for recursion), by default 0
54
+ max_depth : int | None, optional
55
+ Maximum depth to display (None = unlimited), by default None
56
+
57
+ Notes
58
+ -----
59
+ TNFR Principle: Operational fractality (Invariant #7) enables recursive
60
+ bifurcation. This visualization makes hierarchical structures visible for:
61
+ - Debugging nested THOL sequences
62
+ - Validating depth constraints
63
+ - Analyzing emergent patterns
64
+ - Educational demonstrations
65
+
66
+ The format uses standard tree characters:
67
+ - ├─ for intermediate branches
68
+ - └─ for last branch at each level
69
+ - │ for vertical continuation
70
+
71
+ Examples
72
+ --------
73
+ >>> # Simple single-level bifurcation
74
+ >>> print_bifurcation_hierarchy(G, node)
75
+ Node 0 (EPI=0.82, level=0)
76
+ ├─ Sub-EPI 1 (epi=0.21, level=1)
77
+ └─ Sub-EPI 2 (epi=0.18, level=1)
78
+
79
+ >>> # Multi-level nested bifurcation
80
+ >>> print_bifurcation_hierarchy(G, node)
81
+ Node 0 (EPI=0.82, level=0)
82
+ ├─ Sub-EPI 1 (epi=0.21, level=1)
83
+ │ ├─ Sub-Sub-EPI 1.1 (epi=0.05, level=2)
84
+ │ └─ Sub-Sub-EPI 1.2 (epi=0.08, level=2)
85
+ └─ Sub-EPI 2 (epi=0.18, level=1)
86
+
87
+ >>> # Limit depth display
88
+ >>> print_bifurcation_hierarchy(G, node, max_depth=1)
89
+ Node 0 (EPI=0.82, level=0)
90
+ ├─ Sub-EPI 1 (epi=0.21, level=1) [...]
91
+ └─ Sub-EPI 2 (epi=0.18, level=1) [...]
92
+ """
93
+ # Check depth limit
94
+ if max_depth is not None and indent >= max_depth:
95
+ return
96
+
97
+ # Get node information
98
+ node_epi = float(get_attr(G.nodes[node], ALIAS_EPI, 0.0))
99
+ node_level = G.nodes[node].get("_bifurcation_level", 0)
100
+
101
+ # Print current node
102
+ prefix = " " * indent
103
+ print(f"{prefix}Node {node} (EPI={node_epi:.2f}, level={node_level})")
104
+
105
+ # Get sub-EPIs
106
+ sub_epis = G.nodes[node].get("sub_epis", [])
107
+
108
+ if not sub_epis:
109
+ return
110
+
111
+ # Print each sub-EPI
112
+ for i, sub_epi in enumerate(sub_epis):
113
+ is_last = (i == len(sub_epis) - 1)
114
+ branch = "└─" if is_last else "├─"
115
+ continuation = " " if is_last else "│ "
116
+
117
+ sub_level = sub_epi.get("bifurcation_level", 1)
118
+ sub_epi_value = sub_epi.get("epi", 0.0)
119
+
120
+ # Check if we'll hit depth limit on next level
121
+ truncated = ""
122
+ if max_depth is not None and indent + 1 >= max_depth:
123
+ # Check if sub-EPI has further nesting
124
+ sub_node_id = sub_epi.get("node_id")
125
+ if sub_node_id and sub_node_id in G.nodes:
126
+ sub_has_children = bool(G.nodes[sub_node_id].get("sub_epis", []))
127
+ if sub_has_children:
128
+ truncated = " [...]"
129
+
130
+ print(f"{prefix}{branch} Sub-EPI {i+1} "
131
+ f"(epi={sub_epi_value:.2f}, level={sub_level}){truncated}")
132
+
133
+ # Recurse into sub-node if it exists and we haven't hit depth limit
134
+ sub_node_id = sub_epi.get("node_id")
135
+ if sub_node_id and sub_node_id in G.nodes:
136
+ if max_depth is None or indent + 1 < max_depth:
137
+ # Print continuation line for all but last child
138
+ if not is_last:
139
+ child_sub_epis = G.nodes[sub_node_id].get("sub_epis", [])
140
+ if child_sub_epis:
141
+ # Prepare indentation for child's children
142
+ child_indent = indent + 1
143
+ child_prefix = " " * child_indent
144
+ # Print vertical continuation
145
+ print(f"{prefix}{continuation}")
146
+ # Recurse with continuation context
147
+ _print_sub_hierarchy(
148
+ G, sub_node_id, child_indent,
149
+ parent_continuation=continuation,
150
+ parent_prefix=prefix,
151
+ max_depth=max_depth
152
+ )
153
+ else:
154
+ # Last child - no continuation line
155
+ child_sub_epis = G.nodes[sub_node_id].get("sub_epis", [])
156
+ if child_sub_epis:
157
+ child_indent = indent + 1
158
+ _print_sub_hierarchy(
159
+ G, sub_node_id, child_indent,
160
+ parent_continuation=" ",
161
+ parent_prefix=prefix,
162
+ max_depth=max_depth
163
+ )
164
+
165
+
166
+ def _print_sub_hierarchy(
167
+ G: TNFRGraph,
168
+ node: NodeId,
169
+ indent: int,
170
+ parent_continuation: str,
171
+ parent_prefix: str,
172
+ max_depth: int | None,
173
+ ) -> None:
174
+ """Helper to print sub-hierarchy with proper indentation.
175
+
176
+ Internal function handling continuation lines for nested hierarchies.
177
+ """
178
+ sub_epis = G.nodes[node].get("sub_epis", [])
179
+
180
+ if not sub_epis:
181
+ return
182
+
183
+ if max_depth is not None and indent >= max_depth:
184
+ return
185
+
186
+ for i, sub_epi in enumerate(sub_epis):
187
+ is_last = (i == len(sub_epis) - 1)
188
+ branch = "└─" if is_last else "├─"
189
+ continuation = " " if is_last else "│ "
190
+
191
+ sub_level = sub_epi.get("bifurcation_level", 1)
192
+ sub_epi_value = sub_epi.get("epi", 0.0)
193
+
194
+ # Build prefix with parent continuation
195
+ full_prefix = parent_prefix + parent_continuation
196
+
197
+ truncated = ""
198
+ if max_depth is not None and indent + 1 >= max_depth:
199
+ sub_node_id = sub_epi.get("node_id")
200
+ if sub_node_id and sub_node_id in G.nodes:
201
+ sub_has_children = bool(G.nodes[sub_node_id].get("sub_epis", []))
202
+ if sub_has_children:
203
+ truncated = " [...]"
204
+
205
+ print(f"{full_prefix}{branch} Sub-EPI {i+1} "
206
+ f"(epi={sub_epi_value:.2f}, level={sub_level}){truncated}")
207
+
208
+ # Recurse if node exists
209
+ sub_node_id = sub_epi.get("node_id")
210
+ if sub_node_id and sub_node_id in G.nodes:
211
+ if max_depth is None or indent + 1 < max_depth:
212
+ child_sub_epis = G.nodes[sub_node_id].get("sub_epis", [])
213
+ if child_sub_epis:
214
+ _print_sub_hierarchy(
215
+ G, sub_node_id, indent + 1,
216
+ parent_continuation=parent_continuation + continuation,
217
+ parent_prefix=parent_prefix,
218
+ max_depth=max_depth
219
+ )
220
+
221
+
222
+ def get_hierarchy_info(G: TNFRGraph, node: NodeId) -> dict:
223
+ """Get hierarchical bifurcation information for a node.
224
+
225
+ Returns structured data about the bifurcation hierarchy, useful for
226
+ programmatic analysis and testing.
227
+
228
+ Parameters
229
+ ----------
230
+ G : TNFRGraph
231
+ Graph containing bifurcation structure
232
+ node : NodeId
233
+ Node to analyze
234
+
235
+ Returns
236
+ -------
237
+ dict
238
+ Hierarchy information containing:
239
+ - node: Node identifier
240
+ - epi: Current EPI value
241
+ - bifurcation_level: Current bifurcation level
242
+ - hierarchy_path: List of ancestor nodes
243
+ - sub_epi_count: Number of direct sub-EPIs
244
+ - max_depth: Maximum bifurcation depth from this node
245
+ - total_descendants: Total number of sub-EPIs at all levels
246
+
247
+ Examples
248
+ --------
249
+ >>> info = get_hierarchy_info(G, node)
250
+ >>> info["bifurcation_level"]
251
+ 0
252
+ >>> info["max_depth"]
253
+ 2
254
+ >>> info["total_descendants"]
255
+ 4
256
+ """
257
+ from ..operators.metabolism import compute_hierarchical_depth
258
+
259
+ node_epi = float(get_attr(G.nodes[node], ALIAS_EPI, 0.0))
260
+ node_level = G.nodes[node].get("_bifurcation_level", 0)
261
+ hierarchy_path = G.nodes[node].get("_hierarchy_path", [])
262
+ sub_epis = G.nodes[node].get("sub_epis", [])
263
+
264
+ # Compute max depth
265
+ max_depth = compute_hierarchical_depth(G, node)
266
+
267
+ # Count total descendants recursively
268
+ total_descendants = len(sub_epis)
269
+ for sub_epi in sub_epis:
270
+ sub_node_id = sub_epi.get("node_id")
271
+ if sub_node_id and sub_node_id in G.nodes:
272
+ # Recurse into sub-node
273
+ sub_info = get_hierarchy_info(G, sub_node_id)
274
+ total_descendants += sub_info["total_descendants"]
275
+
276
+ return {
277
+ "node": node,
278
+ "epi": node_epi,
279
+ "bifurcation_level": node_level,
280
+ "hierarchy_path": hierarchy_path,
281
+ "sub_epi_count": len(sub_epis),
282
+ "max_depth": max_depth,
283
+ "total_descendants": total_descendants,
284
+ }