tnfr 3.0.3__py3-none-any.whl → 8.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of tnfr might be problematic. Click here for more details.

Files changed (360) hide show
  1. tnfr/__init__.py +375 -56
  2. tnfr/__init__.pyi +33 -0
  3. tnfr/_compat.py +10 -0
  4. tnfr/_generated_version.py +34 -0
  5. tnfr/_version.py +49 -0
  6. tnfr/_version.pyi +7 -0
  7. tnfr/alias.py +723 -0
  8. tnfr/alias.pyi +108 -0
  9. tnfr/backends/__init__.py +354 -0
  10. tnfr/backends/jax_backend.py +173 -0
  11. tnfr/backends/numpy_backend.py +238 -0
  12. tnfr/backends/optimized_numpy.py +420 -0
  13. tnfr/backends/torch_backend.py +408 -0
  14. tnfr/cache.py +171 -0
  15. tnfr/cache.pyi +13 -0
  16. tnfr/cli/__init__.py +110 -0
  17. tnfr/cli/__init__.pyi +26 -0
  18. tnfr/cli/arguments.py +489 -0
  19. tnfr/cli/arguments.pyi +29 -0
  20. tnfr/cli/execution.py +914 -0
  21. tnfr/cli/execution.pyi +70 -0
  22. tnfr/cli/interactive_validator.py +614 -0
  23. tnfr/cli/utils.py +51 -0
  24. tnfr/cli/utils.pyi +7 -0
  25. tnfr/cli/validate.py +236 -0
  26. tnfr/compat/__init__.py +85 -0
  27. tnfr/compat/dataclass.py +136 -0
  28. tnfr/compat/jsonschema_stub.py +61 -0
  29. tnfr/compat/matplotlib_stub.py +73 -0
  30. tnfr/compat/numpy_stub.py +155 -0
  31. tnfr/config/__init__.py +224 -0
  32. tnfr/config/__init__.pyi +10 -0
  33. tnfr/config/constants.py +104 -0
  34. tnfr/config/constants.pyi +12 -0
  35. tnfr/config/defaults.py +54 -0
  36. tnfr/config/defaults_core.py +212 -0
  37. tnfr/config/defaults_init.py +33 -0
  38. tnfr/config/defaults_metric.py +104 -0
  39. tnfr/config/feature_flags.py +81 -0
  40. tnfr/config/feature_flags.pyi +16 -0
  41. tnfr/config/glyph_constants.py +31 -0
  42. tnfr/config/init.py +77 -0
  43. tnfr/config/init.pyi +8 -0
  44. tnfr/config/operator_names.py +254 -0
  45. tnfr/config/operator_names.pyi +36 -0
  46. tnfr/config/physics_derivation.py +354 -0
  47. tnfr/config/presets.py +83 -0
  48. tnfr/config/presets.pyi +7 -0
  49. tnfr/config/security.py +927 -0
  50. tnfr/config/thresholds.py +114 -0
  51. tnfr/config/tnfr_config.py +498 -0
  52. tnfr/constants/__init__.py +92 -0
  53. tnfr/constants/__init__.pyi +92 -0
  54. tnfr/constants/aliases.py +33 -0
  55. tnfr/constants/aliases.pyi +27 -0
  56. tnfr/constants/init.py +33 -0
  57. tnfr/constants/init.pyi +12 -0
  58. tnfr/constants/metric.py +104 -0
  59. tnfr/constants/metric.pyi +19 -0
  60. tnfr/core/__init__.py +33 -0
  61. tnfr/core/container.py +226 -0
  62. tnfr/core/default_implementations.py +329 -0
  63. tnfr/core/interfaces.py +279 -0
  64. tnfr/dynamics/__init__.py +238 -0
  65. tnfr/dynamics/__init__.pyi +83 -0
  66. tnfr/dynamics/adaptation.py +267 -0
  67. tnfr/dynamics/adaptation.pyi +7 -0
  68. tnfr/dynamics/adaptive_sequences.py +189 -0
  69. tnfr/dynamics/adaptive_sequences.pyi +14 -0
  70. tnfr/dynamics/aliases.py +23 -0
  71. tnfr/dynamics/aliases.pyi +19 -0
  72. tnfr/dynamics/bifurcation.py +232 -0
  73. tnfr/dynamics/canonical.py +229 -0
  74. tnfr/dynamics/canonical.pyi +48 -0
  75. tnfr/dynamics/coordination.py +385 -0
  76. tnfr/dynamics/coordination.pyi +25 -0
  77. tnfr/dynamics/dnfr.py +3034 -0
  78. tnfr/dynamics/dnfr.pyi +26 -0
  79. tnfr/dynamics/dynamic_limits.py +225 -0
  80. tnfr/dynamics/feedback.py +252 -0
  81. tnfr/dynamics/feedback.pyi +24 -0
  82. tnfr/dynamics/fused_dnfr.py +454 -0
  83. tnfr/dynamics/homeostasis.py +157 -0
  84. tnfr/dynamics/homeostasis.pyi +14 -0
  85. tnfr/dynamics/integrators.py +661 -0
  86. tnfr/dynamics/integrators.pyi +36 -0
  87. tnfr/dynamics/learning.py +310 -0
  88. tnfr/dynamics/learning.pyi +33 -0
  89. tnfr/dynamics/metabolism.py +254 -0
  90. tnfr/dynamics/nbody.py +796 -0
  91. tnfr/dynamics/nbody_tnfr.py +783 -0
  92. tnfr/dynamics/propagation.py +326 -0
  93. tnfr/dynamics/runtime.py +908 -0
  94. tnfr/dynamics/runtime.pyi +77 -0
  95. tnfr/dynamics/sampling.py +36 -0
  96. tnfr/dynamics/sampling.pyi +7 -0
  97. tnfr/dynamics/selectors.py +711 -0
  98. tnfr/dynamics/selectors.pyi +85 -0
  99. tnfr/dynamics/structural_clip.py +207 -0
  100. tnfr/errors/__init__.py +37 -0
  101. tnfr/errors/contextual.py +492 -0
  102. tnfr/execution.py +223 -0
  103. tnfr/execution.pyi +45 -0
  104. tnfr/extensions/__init__.py +205 -0
  105. tnfr/extensions/__init__.pyi +18 -0
  106. tnfr/extensions/base.py +173 -0
  107. tnfr/extensions/base.pyi +35 -0
  108. tnfr/extensions/business/__init__.py +71 -0
  109. tnfr/extensions/business/__init__.pyi +11 -0
  110. tnfr/extensions/business/cookbook.py +88 -0
  111. tnfr/extensions/business/cookbook.pyi +8 -0
  112. tnfr/extensions/business/health_analyzers.py +202 -0
  113. tnfr/extensions/business/health_analyzers.pyi +9 -0
  114. tnfr/extensions/business/patterns.py +183 -0
  115. tnfr/extensions/business/patterns.pyi +8 -0
  116. tnfr/extensions/medical/__init__.py +73 -0
  117. tnfr/extensions/medical/__init__.pyi +11 -0
  118. tnfr/extensions/medical/cookbook.py +88 -0
  119. tnfr/extensions/medical/cookbook.pyi +8 -0
  120. tnfr/extensions/medical/health_analyzers.py +181 -0
  121. tnfr/extensions/medical/health_analyzers.pyi +9 -0
  122. tnfr/extensions/medical/patterns.py +163 -0
  123. tnfr/extensions/medical/patterns.pyi +8 -0
  124. tnfr/flatten.py +262 -0
  125. tnfr/flatten.pyi +21 -0
  126. tnfr/gamma.py +354 -0
  127. tnfr/gamma.pyi +36 -0
  128. tnfr/glyph_history.py +377 -0
  129. tnfr/glyph_history.pyi +35 -0
  130. tnfr/glyph_runtime.py +19 -0
  131. tnfr/glyph_runtime.pyi +8 -0
  132. tnfr/immutable.py +218 -0
  133. tnfr/immutable.pyi +36 -0
  134. tnfr/initialization.py +203 -0
  135. tnfr/initialization.pyi +65 -0
  136. tnfr/io.py +10 -0
  137. tnfr/io.pyi +13 -0
  138. tnfr/locking.py +37 -0
  139. tnfr/locking.pyi +7 -0
  140. tnfr/mathematics/__init__.py +79 -0
  141. tnfr/mathematics/backend.py +453 -0
  142. tnfr/mathematics/backend.pyi +99 -0
  143. tnfr/mathematics/dynamics.py +408 -0
  144. tnfr/mathematics/dynamics.pyi +90 -0
  145. tnfr/mathematics/epi.py +391 -0
  146. tnfr/mathematics/epi.pyi +65 -0
  147. tnfr/mathematics/generators.py +242 -0
  148. tnfr/mathematics/generators.pyi +29 -0
  149. tnfr/mathematics/metrics.py +119 -0
  150. tnfr/mathematics/metrics.pyi +16 -0
  151. tnfr/mathematics/operators.py +239 -0
  152. tnfr/mathematics/operators.pyi +59 -0
  153. tnfr/mathematics/operators_factory.py +124 -0
  154. tnfr/mathematics/operators_factory.pyi +11 -0
  155. tnfr/mathematics/projection.py +87 -0
  156. tnfr/mathematics/projection.pyi +33 -0
  157. tnfr/mathematics/runtime.py +182 -0
  158. tnfr/mathematics/runtime.pyi +64 -0
  159. tnfr/mathematics/spaces.py +256 -0
  160. tnfr/mathematics/spaces.pyi +83 -0
  161. tnfr/mathematics/transforms.py +305 -0
  162. tnfr/mathematics/transforms.pyi +62 -0
  163. tnfr/metrics/__init__.py +79 -0
  164. tnfr/metrics/__init__.pyi +20 -0
  165. tnfr/metrics/buffer_cache.py +163 -0
  166. tnfr/metrics/buffer_cache.pyi +24 -0
  167. tnfr/metrics/cache_utils.py +214 -0
  168. tnfr/metrics/coherence.py +2009 -0
  169. tnfr/metrics/coherence.pyi +129 -0
  170. tnfr/metrics/common.py +158 -0
  171. tnfr/metrics/common.pyi +35 -0
  172. tnfr/metrics/core.py +316 -0
  173. tnfr/metrics/core.pyi +13 -0
  174. tnfr/metrics/diagnosis.py +833 -0
  175. tnfr/metrics/diagnosis.pyi +86 -0
  176. tnfr/metrics/emergence.py +245 -0
  177. tnfr/metrics/export.py +179 -0
  178. tnfr/metrics/export.pyi +7 -0
  179. tnfr/metrics/glyph_timing.py +379 -0
  180. tnfr/metrics/glyph_timing.pyi +81 -0
  181. tnfr/metrics/learning_metrics.py +280 -0
  182. tnfr/metrics/learning_metrics.pyi +21 -0
  183. tnfr/metrics/phase_coherence.py +351 -0
  184. tnfr/metrics/phase_compatibility.py +349 -0
  185. tnfr/metrics/reporting.py +183 -0
  186. tnfr/metrics/reporting.pyi +25 -0
  187. tnfr/metrics/sense_index.py +1203 -0
  188. tnfr/metrics/sense_index.pyi +9 -0
  189. tnfr/metrics/trig.py +373 -0
  190. tnfr/metrics/trig.pyi +13 -0
  191. tnfr/metrics/trig_cache.py +233 -0
  192. tnfr/metrics/trig_cache.pyi +10 -0
  193. tnfr/multiscale/__init__.py +32 -0
  194. tnfr/multiscale/hierarchical.py +517 -0
  195. tnfr/node.py +763 -0
  196. tnfr/node.pyi +139 -0
  197. tnfr/observers.py +255 -130
  198. tnfr/observers.pyi +31 -0
  199. tnfr/ontosim.py +144 -137
  200. tnfr/ontosim.pyi +28 -0
  201. tnfr/operators/__init__.py +1672 -0
  202. tnfr/operators/__init__.pyi +31 -0
  203. tnfr/operators/algebra.py +277 -0
  204. tnfr/operators/canonical_patterns.py +420 -0
  205. tnfr/operators/cascade.py +267 -0
  206. tnfr/operators/cycle_detection.py +358 -0
  207. tnfr/operators/definitions.py +4108 -0
  208. tnfr/operators/definitions.pyi +78 -0
  209. tnfr/operators/grammar.py +1164 -0
  210. tnfr/operators/grammar.pyi +140 -0
  211. tnfr/operators/hamiltonian.py +710 -0
  212. tnfr/operators/health_analyzer.py +809 -0
  213. tnfr/operators/jitter.py +272 -0
  214. tnfr/operators/jitter.pyi +11 -0
  215. tnfr/operators/lifecycle.py +314 -0
  216. tnfr/operators/metabolism.py +618 -0
  217. tnfr/operators/metrics.py +2138 -0
  218. tnfr/operators/network_analysis/__init__.py +27 -0
  219. tnfr/operators/network_analysis/source_detection.py +186 -0
  220. tnfr/operators/nodal_equation.py +395 -0
  221. tnfr/operators/pattern_detection.py +660 -0
  222. tnfr/operators/patterns.py +669 -0
  223. tnfr/operators/postconditions/__init__.py +38 -0
  224. tnfr/operators/postconditions/mutation.py +236 -0
  225. tnfr/operators/preconditions/__init__.py +1226 -0
  226. tnfr/operators/preconditions/coherence.py +305 -0
  227. tnfr/operators/preconditions/dissonance.py +236 -0
  228. tnfr/operators/preconditions/emission.py +128 -0
  229. tnfr/operators/preconditions/mutation.py +580 -0
  230. tnfr/operators/preconditions/reception.py +125 -0
  231. tnfr/operators/preconditions/resonance.py +364 -0
  232. tnfr/operators/registry.py +74 -0
  233. tnfr/operators/registry.pyi +9 -0
  234. tnfr/operators/remesh.py +1809 -0
  235. tnfr/operators/remesh.pyi +26 -0
  236. tnfr/operators/structural_units.py +268 -0
  237. tnfr/operators/unified_grammar.py +105 -0
  238. tnfr/parallel/__init__.py +54 -0
  239. tnfr/parallel/auto_scaler.py +234 -0
  240. tnfr/parallel/distributed.py +384 -0
  241. tnfr/parallel/engine.py +238 -0
  242. tnfr/parallel/gpu_engine.py +420 -0
  243. tnfr/parallel/monitoring.py +248 -0
  244. tnfr/parallel/partitioner.py +459 -0
  245. tnfr/py.typed +0 -0
  246. tnfr/recipes/__init__.py +22 -0
  247. tnfr/recipes/cookbook.py +743 -0
  248. tnfr/rng.py +178 -0
  249. tnfr/rng.pyi +26 -0
  250. tnfr/schemas/__init__.py +8 -0
  251. tnfr/schemas/grammar.json +94 -0
  252. tnfr/sdk/__init__.py +107 -0
  253. tnfr/sdk/__init__.pyi +19 -0
  254. tnfr/sdk/adaptive_system.py +173 -0
  255. tnfr/sdk/adaptive_system.pyi +21 -0
  256. tnfr/sdk/builders.py +370 -0
  257. tnfr/sdk/builders.pyi +51 -0
  258. tnfr/sdk/fluent.py +1121 -0
  259. tnfr/sdk/fluent.pyi +74 -0
  260. tnfr/sdk/templates.py +342 -0
  261. tnfr/sdk/templates.pyi +41 -0
  262. tnfr/sdk/utils.py +341 -0
  263. tnfr/secure_config.py +46 -0
  264. tnfr/security/__init__.py +70 -0
  265. tnfr/security/database.py +514 -0
  266. tnfr/security/subprocess.py +503 -0
  267. tnfr/security/validation.py +290 -0
  268. tnfr/selector.py +247 -0
  269. tnfr/selector.pyi +19 -0
  270. tnfr/sense.py +378 -0
  271. tnfr/sense.pyi +23 -0
  272. tnfr/services/__init__.py +17 -0
  273. tnfr/services/orchestrator.py +325 -0
  274. tnfr/sparse/__init__.py +39 -0
  275. tnfr/sparse/representations.py +492 -0
  276. tnfr/structural.py +705 -0
  277. tnfr/structural.pyi +83 -0
  278. tnfr/telemetry/__init__.py +35 -0
  279. tnfr/telemetry/cache_metrics.py +226 -0
  280. tnfr/telemetry/cache_metrics.pyi +64 -0
  281. tnfr/telemetry/nu_f.py +422 -0
  282. tnfr/telemetry/nu_f.pyi +108 -0
  283. tnfr/telemetry/verbosity.py +36 -0
  284. tnfr/telemetry/verbosity.pyi +15 -0
  285. tnfr/tokens.py +58 -0
  286. tnfr/tokens.pyi +36 -0
  287. tnfr/tools/__init__.py +20 -0
  288. tnfr/tools/domain_templates.py +478 -0
  289. tnfr/tools/sequence_generator.py +846 -0
  290. tnfr/topology/__init__.py +13 -0
  291. tnfr/topology/asymmetry.py +151 -0
  292. tnfr/trace.py +543 -0
  293. tnfr/trace.pyi +42 -0
  294. tnfr/tutorials/__init__.py +38 -0
  295. tnfr/tutorials/autonomous_evolution.py +285 -0
  296. tnfr/tutorials/interactive.py +1576 -0
  297. tnfr/tutorials/structural_metabolism.py +238 -0
  298. tnfr/types.py +775 -0
  299. tnfr/types.pyi +357 -0
  300. tnfr/units.py +68 -0
  301. tnfr/units.pyi +13 -0
  302. tnfr/utils/__init__.py +282 -0
  303. tnfr/utils/__init__.pyi +215 -0
  304. tnfr/utils/cache.py +4223 -0
  305. tnfr/utils/cache.pyi +470 -0
  306. tnfr/utils/callbacks.py +375 -0
  307. tnfr/utils/callbacks.pyi +49 -0
  308. tnfr/utils/chunks.py +108 -0
  309. tnfr/utils/chunks.pyi +22 -0
  310. tnfr/utils/data.py +428 -0
  311. tnfr/utils/data.pyi +74 -0
  312. tnfr/utils/graph.py +85 -0
  313. tnfr/utils/graph.pyi +10 -0
  314. tnfr/utils/init.py +821 -0
  315. tnfr/utils/init.pyi +80 -0
  316. tnfr/utils/io.py +559 -0
  317. tnfr/utils/io.pyi +66 -0
  318. tnfr/utils/numeric.py +114 -0
  319. tnfr/utils/numeric.pyi +21 -0
  320. tnfr/validation/__init__.py +257 -0
  321. tnfr/validation/__init__.pyi +85 -0
  322. tnfr/validation/compatibility.py +460 -0
  323. tnfr/validation/compatibility.pyi +6 -0
  324. tnfr/validation/config.py +73 -0
  325. tnfr/validation/graph.py +139 -0
  326. tnfr/validation/graph.pyi +18 -0
  327. tnfr/validation/input_validation.py +755 -0
  328. tnfr/validation/invariants.py +712 -0
  329. tnfr/validation/rules.py +253 -0
  330. tnfr/validation/rules.pyi +44 -0
  331. tnfr/validation/runtime.py +279 -0
  332. tnfr/validation/runtime.pyi +28 -0
  333. tnfr/validation/sequence_validator.py +162 -0
  334. tnfr/validation/soft_filters.py +170 -0
  335. tnfr/validation/soft_filters.pyi +32 -0
  336. tnfr/validation/spectral.py +164 -0
  337. tnfr/validation/spectral.pyi +42 -0
  338. tnfr/validation/validator.py +1266 -0
  339. tnfr/validation/window.py +39 -0
  340. tnfr/validation/window.pyi +1 -0
  341. tnfr/visualization/__init__.py +98 -0
  342. tnfr/visualization/cascade_viz.py +256 -0
  343. tnfr/visualization/hierarchy.py +284 -0
  344. tnfr/visualization/sequence_plotter.py +784 -0
  345. tnfr/viz/__init__.py +60 -0
  346. tnfr/viz/matplotlib.py +278 -0
  347. tnfr/viz/matplotlib.pyi +35 -0
  348. tnfr-8.5.0.dist-info/METADATA +573 -0
  349. tnfr-8.5.0.dist-info/RECORD +353 -0
  350. tnfr-8.5.0.dist-info/entry_points.txt +3 -0
  351. tnfr-3.0.3.dist-info/licenses/LICENSE.txt → tnfr-8.5.0.dist-info/licenses/LICENSE.md +1 -1
  352. tnfr/constants.py +0 -183
  353. tnfr/dynamics.py +0 -543
  354. tnfr/helpers.py +0 -198
  355. tnfr/main.py +0 -37
  356. tnfr/operators.py +0 -296
  357. tnfr-3.0.3.dist-info/METADATA +0 -35
  358. tnfr-3.0.3.dist-info/RECORD +0 -13
  359. {tnfr-3.0.3.dist-info → tnfr-8.5.0.dist-info}/WHEEL +0 -0
  360. {tnfr-3.0.3.dist-info → tnfr-8.5.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,349 @@
1
+ """Unified phase compatibility calculations for TNFR operators.
2
+
3
+ This module provides canonical implementations of phase-based coupling strength
4
+ calculations used by multiple TNFR operators (UM, RA, THOL). All operators that
5
+ perform phase-based coupling or propagation MUST use these functions to ensure
6
+ consistency with TNFR physics and Invariant #5.
7
+
8
+ Physical Foundation
9
+ -------------------
10
+
11
+ **Phase Compatibility in TNFR:**
12
+
13
+ Coupling between nodes requires phase synchronization. Destructive interference
14
+ occurs when phases are misaligned (antiphase), while constructive interference
15
+ occurs when phases align. The coupling strength formula reflects this physics:
16
+
17
+ .. math::
18
+ \\text{coupling_strength} = 1.0 - \\frac{|\\Delta\\phi|}{\\pi}
19
+
20
+ where Δφ is the phase difference in radians.
21
+
22
+ **Physical Interpretation:**
23
+
24
+ - Δφ = 0 (perfect alignment) → coupling = 1.0 (maximum constructive interference)
25
+ - Δφ = π/2 (orthogonal) → coupling = 0.5 (partial coupling)
26
+ - Δφ = π (antiphase) → coupling = 0.0 (destructive interference)
27
+
28
+ **TNFR Invariant #5:** "No coupling without explicit phase verification"
29
+ (see AGENTS.md). All coupling operations must verify phase compatibility
30
+ before propagating structural information.
31
+
32
+ Canonical Usage
33
+ ---------------
34
+
35
+ **Operators Using This Module:**
36
+
37
+ 1. **UM (Coupling)**: Phase synchronization and network formation
38
+ 2. **RA (Resonance)**: Coherence propagation through phase-aligned paths
39
+ 3. **THOL (Self-organization)**: Sub-EPI propagation to coupled neighbors
40
+
41
+ **Before Refactoring:**
42
+
43
+ Each operator implemented its own phase compatibility calculation, leading
44
+ to potential inconsistencies and maintenance burden.
45
+
46
+ **After Refactoring:**
47
+
48
+ All operators use the canonical functions defined here, ensuring theoretical
49
+ consistency and simplifying validation against TNFR physics.
50
+
51
+ Examples
52
+ --------
53
+
54
+ **Basic coupling strength calculation:**
55
+
56
+ >>> import math
57
+ >>> # Perfect alignment
58
+ >>> compute_phase_coupling_strength(0.0, 0.0)
59
+ 1.0
60
+ >>> # Orthogonal phases
61
+ >>> compute_phase_coupling_strength(0.0, math.pi/2)
62
+ 0.5
63
+ >>> # Antiphase (destructive)
64
+ >>> round(compute_phase_coupling_strength(0.0, math.pi), 10)
65
+ 0.0
66
+
67
+ **Phase compatibility check:**
68
+
69
+ >>> # Check if phases are compatible for coupling
70
+ >>> is_phase_compatible(0.0, 0.1, threshold=0.5)
71
+ True
72
+ >>> is_phase_compatible(0.0, math.pi, threshold=0.5)
73
+ False
74
+
75
+ **Network phase alignment:**
76
+
77
+ >>> import networkx as nx
78
+ >>> from tnfr.constants.aliases import ALIAS_THETA
79
+ >>> G = nx.Graph()
80
+ >>> G.add_edges_from([(0, 1), (1, 2)])
81
+ >>> for i, theta in enumerate([0.0, 0.1, 0.2]):
82
+ ... G.nodes[i][ALIAS_THETA] = theta
83
+ >>> alignment = compute_network_phase_alignment(G, node=1, radius=1)
84
+ >>> 0.0 <= alignment <= 1.0
85
+ True
86
+
87
+ See Also
88
+ --------
89
+
90
+ operators.definitions : Operator implementations (UM, RA, THOL)
91
+ metrics.phase_coherence : Kuramoto order parameter and phase metrics
92
+ AGENTS.md : Invariant #5 - Phase Verification requirement
93
+ UNIFIED_GRAMMAR_RULES.md : U3 - RESONANT COUPLING grammar rule
94
+
95
+ References
96
+ ----------
97
+
98
+ .. [1] TNFR.pdf § 2.3: Phase synchronization and coupling
99
+ .. [2] AGENTS.md: Invariant #5 - No coupling without phase verification
100
+ .. [3] UNIFIED_GRAMMAR_RULES.md: U3 - Resonant Coupling requires |φᵢ - φⱼ| ≤ Δφ_max
101
+ """
102
+
103
+ from __future__ import annotations
104
+
105
+ import math
106
+ from typing import TYPE_CHECKING, Any
107
+
108
+ if TYPE_CHECKING:
109
+ from ..types import TNFRGraph, NodeId
110
+
111
+ from ..utils.numeric import angle_diff
112
+
113
+ __all__ = [
114
+ "compute_phase_coupling_strength",
115
+ "is_phase_compatible",
116
+ "compute_network_phase_alignment",
117
+ ]
118
+
119
+
120
+ def compute_phase_coupling_strength(
121
+ theta_a: float,
122
+ theta_b: float,
123
+ ) -> float:
124
+ """Compute canonical coupling strength from phase difference.
125
+
126
+ This is the canonical TNFR formula for phase-based coupling strength,
127
+ representing the degree of constructive vs. destructive interference
128
+ between two oscillating nodes.
129
+
130
+ Parameters
131
+ ----------
132
+ theta_a : float
133
+ Phase of first node in radians [0, 2π)
134
+ theta_b : float
135
+ Phase of second node in radians [0, 2π)
136
+
137
+ Returns
138
+ -------
139
+ float
140
+ Coupling strength in [0, 1]:
141
+ - 1.0: Perfect phase alignment (Δφ = 0)
142
+ - 0.5: Orthogonal phases (Δφ = π/2)
143
+ - 0.0: Antiphase (Δφ = π, destructive interference)
144
+
145
+ Notes
146
+ -----
147
+ **Formula:**
148
+
149
+ .. math::
150
+ \\text{coupling_strength} = 1.0 - \\frac{|\\text{angle_diff}(\\theta_b, \\theta_a)|}{\\pi}
151
+
152
+ The formula uses :func:`~tnfr.utils.numeric.angle_diff` to compute the
153
+ shortest angular distance between phases, properly handling wrap-around
154
+ at 2π boundaries.
155
+
156
+ **Physics:**
157
+
158
+ - Based on wave interference physics: aligned phases → constructive interference
159
+ - Antiphase (Δφ = π) → destructive interference → zero coupling
160
+ - Linear interpolation between extremes reflects gradual transition
161
+
162
+ **Used By:**
163
+
164
+ - UM (Coupling): For determining link formation and synchronization strength
165
+ - RA (Resonance): For gating coherence propagation to neighbors
166
+ - THOL (Self-organization): For sub-EPI propagation through coupled nodes
167
+
168
+ **Invariant #5:** This function implements the explicit phase verification
169
+ required by TNFR Invariant #5 (AGENTS.md). All coupling operations must
170
+ verify phase compatibility before propagating structural information.
171
+
172
+ Examples
173
+ --------
174
+ >>> import math
175
+ >>> # Perfect alignment
176
+ >>> compute_phase_coupling_strength(0.0, 0.0)
177
+ 1.0
178
+ >>> # Small misalignment
179
+ >>> compute_phase_coupling_strength(0.0, 0.1) # doctest: +ELLIPSIS
180
+ 0.96...
181
+ >>> # Orthogonal phases
182
+ >>> compute_phase_coupling_strength(0.0, math.pi/2)
183
+ 0.5
184
+ >>> # Antiphase (destructive)
185
+ >>> round(compute_phase_coupling_strength(0.0, math.pi), 10)
186
+ 0.0
187
+ >>> # Wrap-around handling
188
+ >>> compute_phase_coupling_strength(0.1, 2*math.pi - 0.1) # doctest: +ELLIPSIS
189
+ 0.93...
190
+
191
+ See Also
192
+ --------
193
+ is_phase_compatible : Boolean compatibility check with threshold
194
+ angle_diff : Shortest angular distance between phases
195
+ """
196
+ phase_diff = abs(angle_diff(theta_b, theta_a))
197
+ return 1.0 - (phase_diff / math.pi)
198
+
199
+
200
+ def is_phase_compatible(
201
+ theta_a: float,
202
+ theta_b: float,
203
+ threshold: float = 0.5,
204
+ ) -> bool:
205
+ """Check if two phases are compatible for coupling/propagation.
206
+
207
+ Determines whether two nodes are sufficiently phase-aligned to support
208
+ resonant coupling, based on a configurable coupling strength threshold.
209
+
210
+ Parameters
211
+ ----------
212
+ theta_a : float
213
+ Phase of first node in radians [0, 2π)
214
+ theta_b : float
215
+ Phase of second node in radians [0, 2π)
216
+ threshold : float, default=0.5
217
+ Minimum coupling strength required for compatibility [0, 1].
218
+ Default 0.5 corresponds to maximum phase difference of π/2 (orthogonal).
219
+
220
+ Returns
221
+ -------
222
+ bool
223
+ True if coupling_strength >= threshold (nodes are compatible)
224
+ False if coupling_strength < threshold (nodes are incompatible)
225
+
226
+ Notes
227
+ -----
228
+ **Common Thresholds:**
229
+
230
+ - 0.5 (default): Allows coupling up to π/2 phase difference
231
+ - 0.7: More restrictive, requires Δφ < π/2.1 (~95°)
232
+ - 0.9: Very restrictive, requires Δφ < π/10 (~18°)
233
+
234
+ **Usage:**
235
+
236
+ - **UM (Coupling)**: Gate link formation based on phase compatibility
237
+ - **RA (Resonance)**: Filter neighbors for coherence propagation
238
+ - **THOL propagation**: Minimum coupling for sub-EPI propagation
239
+
240
+ **Invariant #5:** This function provides a boolean interface to the
241
+ phase verification requirement (AGENTS.md Invariant #5).
242
+
243
+ Examples
244
+ --------
245
+ >>> import math
246
+ >>> # In-phase: compatible
247
+ >>> is_phase_compatible(0.0, 0.1, threshold=0.5)
248
+ True
249
+ >>> # Orthogonal: at threshold boundary
250
+ >>> is_phase_compatible(0.0, math.pi/2, threshold=0.5)
251
+ True
252
+ >>> # Slightly beyond orthogonal: incompatible
253
+ >>> is_phase_compatible(0.0, math.pi/2 + 0.1, threshold=0.5)
254
+ False
255
+ >>> # Antiphase: incompatible
256
+ >>> is_phase_compatible(0.0, math.pi, threshold=0.5)
257
+ False
258
+ >>> # Higher threshold: more restrictive
259
+ >>> is_phase_compatible(0.0, math.pi/4, threshold=0.9)
260
+ False
261
+ >>> is_phase_compatible(0.0, 0.1, threshold=0.9)
262
+ True
263
+
264
+ See Also
265
+ --------
266
+ compute_phase_coupling_strength : Continuous coupling strength [0, 1]
267
+ """
268
+ coupling = compute_phase_coupling_strength(theta_a, theta_b)
269
+ return coupling >= threshold
270
+
271
+
272
+ def compute_network_phase_alignment(
273
+ G: TNFRGraph,
274
+ node: NodeId,
275
+ radius: int = 1,
276
+ ) -> float:
277
+ """Compute phase alignment in local neighborhood using Kuramoto order parameter.
278
+
279
+ This is a convenience wrapper around the existing
280
+ :func:`~tnfr.metrics.phase_coherence.compute_phase_alignment` function,
281
+ provided for API consistency within this module.
282
+
283
+ Parameters
284
+ ----------
285
+ G : TNFRGraph
286
+ TNFR network graph containing nodes with phase (theta) attributes
287
+ node : NodeId
288
+ Central node for neighborhood analysis
289
+ radius : int, default=1
290
+ Neighborhood radius in hops from central node
291
+
292
+ Returns
293
+ -------
294
+ float
295
+ Phase alignment quality in [0, 1]:
296
+ - 1.0: Perfect phase synchronization (all nodes aligned)
297
+ - 0.0: Complete phase disorder (random phases)
298
+
299
+ Notes
300
+ -----
301
+ **Kuramoto Order Parameter:**
302
+
303
+ Measures collective phase synchrony using:
304
+
305
+ .. math::
306
+ r = |\\frac{1}{N} \\sum_{j=1}^{N} e^{i\\theta_j}|
307
+
308
+ **Used By:**
309
+
310
+ - **RA (Resonance)**: Assess network coherence for propagation gating
311
+ - **IL (Coherence)**: Validate phase locking effectiveness
312
+
313
+ **Implementation:**
314
+
315
+ This function delegates to the existing implementation in
316
+ :mod:`tnfr.metrics.phase_coherence` to avoid code duplication
317
+ while providing a unified API for phase compatibility calculations.
318
+
319
+ Examples
320
+ --------
321
+ >>> import networkx as nx
322
+ >>> from tnfr.constants.aliases import ALIAS_THETA
323
+ >>> G = nx.Graph()
324
+ >>> G.add_edges_from([(0, 1), (1, 2), (2, 3)])
325
+ >>> # Highly aligned phases
326
+ >>> for i in range(4):
327
+ ... G.nodes[i][ALIAS_THETA] = i * 0.1
328
+ >>> alignment = compute_network_phase_alignment(G, node=1, radius=1)
329
+ >>> alignment > 0.9 # High alignment
330
+ True
331
+ >>> # Random phases
332
+ >>> import math
333
+ >>> G.nodes[0][ALIAS_THETA] = 0.0
334
+ >>> G.nodes[1][ALIAS_THETA] = math.pi/3
335
+ >>> G.nodes[2][ALIAS_THETA] = 2*math.pi/3
336
+ >>> G.nodes[3][ALIAS_THETA] = math.pi
337
+ >>> alignment = compute_network_phase_alignment(G, node=1, radius=1)
338
+ >>> 0.0 <= alignment <= 1.0
339
+ True
340
+
341
+ See Also
342
+ --------
343
+ metrics.phase_coherence.compute_phase_alignment : Underlying implementation
344
+ metrics.phase_coherence.compute_global_phase_coherence : Global network metric
345
+ """
346
+ # Import existing function to avoid duplication
347
+ from .phase_coherence import compute_phase_alignment
348
+
349
+ return compute_phase_alignment(G, node, radius=radius)
@@ -0,0 +1,183 @@
1
+ """Reporting helpers for collected metrics."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Sequence
6
+ from heapq import nlargest
7
+ from statistics import StatisticsError, fmean, mean
8
+ from typing import Any
9
+
10
+ from ..glyph_history import ensure_history
11
+ from ..sense import sigma_rose
12
+ from ..types import NodeId, TNFRGraph
13
+ from .glyph_timing import for_each_glyph
14
+
15
+ __all__ = [
16
+ "Tg_global",
17
+ "Tg_by_node",
18
+ "latency_series",
19
+ "glyphogram_series",
20
+ "glyph_top",
21
+ "build_metrics_summary",
22
+ ]
23
+
24
+ # ---------------------------------------------------------------------------
25
+ # Reporting functions
26
+ # ---------------------------------------------------------------------------
27
+
28
+
29
+ def Tg_global(G: TNFRGraph, normalize: bool = True) -> dict[str, float]:
30
+ """Total glyph dwell time per class."""
31
+
32
+ hist = ensure_history(G)
33
+ tg_total: dict[str, float] = hist.get("Tg_total", {})
34
+ total = sum(tg_total.values()) or 1.0
35
+ out: dict[str, float] = {}
36
+
37
+ def add(g: str) -> None:
38
+ val = float(tg_total.get(g, 0.0))
39
+ out[g] = val / total if normalize else val
40
+
41
+ for_each_glyph(add)
42
+ return out
43
+
44
+
45
+ def Tg_by_node(
46
+ G: TNFRGraph, n: NodeId, normalize: bool = False
47
+ ) -> dict[str, float] | dict[str, list[float]]:
48
+ """Per-node glyph dwell summary."""
49
+
50
+ hist = ensure_history(G)
51
+ rec = hist.get("Tg_by_node", {}).get(n, {})
52
+ if not normalize:
53
+ runs_out: dict[str, list[float]] = {}
54
+
55
+ def copy_runs(g: str) -> None:
56
+ runs_out[g] = list(rec.get(g, []))
57
+
58
+ for_each_glyph(copy_runs)
59
+ return runs_out
60
+ mean_out: dict[str, float] = {}
61
+
62
+ def add(g: str) -> None:
63
+ runs = rec.get(g, [])
64
+ mean_out[g] = float(mean(runs)) if runs else 0.0
65
+
66
+ for_each_glyph(add)
67
+ return mean_out
68
+
69
+
70
+ def latency_series(G: TNFRGraph) -> dict[str, list[float]]:
71
+ """Return latency samples as ``{"t": [...], "value": [...]}``."""
72
+
73
+ hist = ensure_history(G)
74
+ xs = hist.get("latency_index", [])
75
+ return {
76
+ "t": [float(x.get("t", i)) for i, x in enumerate(xs)],
77
+ "value": [float(x.get("value", 0.0)) for x in xs],
78
+ }
79
+
80
+
81
+ def glyphogram_series(G: TNFRGraph) -> dict[str, list[float]]:
82
+ """Return glyphogram time series keyed by glyph label."""
83
+
84
+ hist = ensure_history(G)
85
+ xs = hist.get("glyphogram", [])
86
+ if not xs:
87
+ return {"t": []}
88
+ out: dict[str, list[float]] = {
89
+ "t": [float(x.get("t", i)) for i, x in enumerate(xs)]
90
+ }
91
+
92
+ def add(g: str) -> None:
93
+ out[g] = [float(x.get(g, 0.0)) for x in xs]
94
+
95
+ for_each_glyph(add)
96
+ return out
97
+
98
+
99
+ def glyph_top(G: TNFRGraph, k: int = 3) -> list[tuple[str, float]]:
100
+ """Top-k structural operators by ``Tg_global`` fraction."""
101
+
102
+ k = int(k)
103
+ if k <= 0:
104
+ raise ValueError("k must be a positive integer")
105
+ tg = Tg_global(G, normalize=True)
106
+ return nlargest(k, tg.items(), key=lambda kv: kv[1])
107
+
108
+
109
+ def build_metrics_summary(
110
+ G: TNFRGraph, *, series_limit: int | None = None
111
+ ) -> tuple[
112
+ dict[str, float | dict[str, float] | dict[str, list[float]] | dict[str, int]], bool
113
+ ]:
114
+ """Collect a compact metrics summary for CLI reporting.
115
+
116
+ This factory aggregates various TNFR metrics into a unified summary
117
+ structure suitable for command-line display and analysis. It combines
118
+ glyph timing statistics, latency measurements, and coherence indicators.
119
+
120
+ Parameters
121
+ ----------
122
+ G : TNFRGraph
123
+ Graph containing the recorded metrics history.
124
+ series_limit : int | None, optional
125
+ Maximum number of samples to keep for each glyphogram series.
126
+ When ``None`` or non-positive, returns the full history without
127
+ trimming. Default is None.
128
+
129
+ Returns
130
+ -------
131
+ tuple[dict, bool]
132
+ A two-element tuple containing:
133
+
134
+ - **summary** (dict): Metrics dictionary with the following keys:
135
+
136
+ - ``Tg_global``: Normalized glyph dwell time per class
137
+ - ``latency_mean``: Mean latency across all samples
138
+ - ``rose``: Sigma rose coherence indicator
139
+ - ``glyphogram``: Time series of glyph activity (trimmed if limit set)
140
+
141
+ - **has_data** (bool): True if latency data is available, False otherwise
142
+
143
+ Notes
144
+ -----
145
+ The series trimming feature is useful for limiting memory usage when
146
+ tracking long-running simulations. Trimming only affects the glyphogram
147
+ time series; aggregate statistics remain computed from the full history.
148
+ """
149
+
150
+ tg = Tg_global(G, normalize=True)
151
+ latency = latency_series(G)
152
+ glyph = glyphogram_series(G)
153
+ rose = sigma_rose(G)
154
+
155
+ latency_values = latency.get("value", [])
156
+ try:
157
+ latency_mean = fmean(latency_values)
158
+ except StatisticsError:
159
+ latency_mean = 0.0
160
+
161
+ limit: int | None
162
+ if series_limit is None:
163
+ limit = None
164
+ else:
165
+ limit = int(series_limit)
166
+ if limit <= 0:
167
+ limit = None
168
+
169
+ def _trim(values: Sequence[Any]) -> list[Any]:
170
+ seq = list(values)
171
+ if limit is None:
172
+ return seq
173
+ return seq[:limit]
174
+
175
+ glyph_summary = {k: _trim(v) for k, v in glyph.items()}
176
+
177
+ summary = {
178
+ "Tg_global": tg,
179
+ "latency_mean": latency_mean,
180
+ "rose": rose,
181
+ "glyphogram": glyph_summary,
182
+ }
183
+ return summary, bool(latency_values)
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+
3
+ from ..types import NodeId, TNFRGraph
4
+
5
+ __all__ = [
6
+ "Tg_global",
7
+ "Tg_by_node",
8
+ "latency_series",
9
+ "glyphogram_series",
10
+ "glyph_top",
11
+ "build_metrics_summary",
12
+ ]
13
+
14
+ def Tg_global(G: TNFRGraph, normalize: bool = True) -> dict[str, float]: ...
15
+ def Tg_by_node(
16
+ G: TNFRGraph, n: NodeId, normalize: bool = False
17
+ ) -> dict[str, float] | dict[str, list[float]]: ...
18
+ def latency_series(G: TNFRGraph) -> dict[str, list[float]]: ...
19
+ def glyphogram_series(G: TNFRGraph) -> dict[str, list[float]]: ...
20
+ def glyph_top(G: TNFRGraph, k: int = 3) -> list[tuple[str, float]]: ...
21
+ def build_metrics_summary(
22
+ G: TNFRGraph, *, series_limit: int | None = None
23
+ ) -> tuple[
24
+ dict[str, float | dict[str, float] | dict[str, list[float]] | dict[str, int]], bool
25
+ ]: ...