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,364 @@
1
+ """Strict precondition validation for RA (Resonance) operator.
2
+
3
+ This module implements canonical precondition validation for the Resonance (RA)
4
+ structural operator according to TNFR theory. RA requires specific structural
5
+ conditions to maintain TNFR operational fidelity:
6
+
7
+ 1. **Coherent source EPI**: Node must have sufficient structural form for propagation
8
+ 2. **Network connectivity**: Edges must exist for resonance to propagate through
9
+ 3. **Phase compatibility**: Node must be synchronized with neighbors (coupling)
10
+ 4. **Controlled dissonance**: ΔNFR must not be excessive (stable resonance)
11
+ 5. **Sufficient νf**: Structural frequency must support propagation dynamics
12
+
13
+ These validations protect structural integrity by ensuring RA is only applied to
14
+ nodes in the appropriate state for coherence propagation through the network.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import warnings
20
+ from typing import TYPE_CHECKING, Any
21
+
22
+ if TYPE_CHECKING:
23
+ from ...types import TNFRGraph
24
+
25
+ __all__ = ["validate_resonance_strict", "diagnose_resonance_readiness"]
26
+
27
+
28
+ def validate_resonance_strict(
29
+ G: TNFRGraph,
30
+ node: Any,
31
+ *,
32
+ min_epi: float | None = None,
33
+ require_coupling: bool = True,
34
+ max_dissonance: float | None = None,
35
+ warn_phase_misalignment: bool = True,
36
+ ) -> None:
37
+ """Validate strict canonical preconditions for RA (Resonance) operator.
38
+
39
+ According to TNFR theory, Resonance (RA - Resonancia) requires:
40
+
41
+ 1. **Coherent source**: EPI >= threshold (sufficient structure to propagate)
42
+ 2. **Network connectivity**: degree > 0 (edges for propagation)
43
+ 3. **Phase compatibility**: alignment with neighbors (synchronization)
44
+ 4. **Controlled dissonance**: |ΔNFR| < threshold (stable for resonance)
45
+ 5. **Sufficient νf**: νf > threshold (capacity for propagation dynamics)
46
+
47
+ Canonical sequences that satisfy preconditions:
48
+ - **UM → RA**: Coupling establishes connections, then resonance propagates
49
+ - **AL → RA**: Emission activates source, then resonance broadcasts
50
+ - **IL → RA**: Coherence stabilizes, then propagates stable form
51
+
52
+ Parameters
53
+ ----------
54
+ G : TNFRGraph
55
+ Graph containing the node to validate
56
+ node : Any
57
+ Node identifier for validation
58
+ min_epi : float, optional
59
+ Minimum EPI magnitude for resonance source
60
+ Default: Uses G.graph["RA_MIN_SOURCE_EPI"] or 0.1
61
+ require_coupling : bool, default True
62
+ If True, validates that node has edges (connectivity)
63
+ max_dissonance : float, optional
64
+ Maximum allowed |ΔNFR| for resonance
65
+ Default: Uses G.graph["RA_MAX_DISSONANCE"] or 0.5
66
+ warn_phase_misalignment : bool, default True
67
+ If True, warns when phase difference with neighbors is high
68
+
69
+ Raises
70
+ ------
71
+ ValueError
72
+ If EPI < min_epi (insufficient structure to propagate)
73
+ If require_coupling=True and node has no edges
74
+ If |ΔNFR| > max_dissonance (too unstable for resonance)
75
+ If νf < threshold (insufficient structural frequency)
76
+
77
+ Warnings
78
+ --------
79
+ UserWarning
80
+ If phase misalignment with neighbors exceeds threshold (suboptimal resonance)
81
+ If node is isolated but require_coupling=False
82
+
83
+ Notes
84
+ -----
85
+ Thresholds are configurable via graph metadata:
86
+ - ``RA_MIN_SOURCE_EPI``: Minimum EPI for source (default: 0.1)
87
+ - ``RA_MAX_DISSONANCE``: Maximum |ΔNFR| (default: 0.5)
88
+ - ``RA_MAX_PHASE_DIFF``: Maximum phase difference in radians (default: 1.0)
89
+ - ``RA_MIN_VF``: Minimum structural frequency (default: 0.01)
90
+
91
+ Examples
92
+ --------
93
+ >>> from tnfr.structural import create_nfr
94
+ >>> from tnfr.operators.preconditions.resonance import validate_resonance_strict
95
+ >>>
96
+ >>> # Valid node for resonance
97
+ >>> G, node = create_nfr("source", epi=0.8, vf=0.9)
98
+ >>> neighbor = "neighbor"
99
+ >>> G.add_node(neighbor, epi=0.5, vf=0.8, theta=0.1, dnfr=0.05, epi_kind="seed")
100
+ >>> G.add_edge(node, neighbor)
101
+ >>> G.nodes[node]["dnfr"] = 0.1
102
+ >>> validate_resonance_strict(G, node) # OK
103
+
104
+ >>> # Invalid: EPI too low
105
+ >>> G2, node2 = create_nfr("weak_source", epi=0.05, vf=0.9)
106
+ >>> neighbor2 = "neighbor2"
107
+ >>> G2.add_node(neighbor2, epi=0.5, vf=0.8, theta=0.1, dnfr=0.05, epi_kind="seed")
108
+ >>> G2.add_edge(node2, neighbor2)
109
+ >>> validate_resonance_strict(G2, node2) # doctest: +SKIP
110
+ Traceback (most recent call last):
111
+ ...
112
+ ValueError: RA requires coherent source with EPI >= 0.1 (current: 0.050). Apply IL or THOL first.
113
+
114
+ >>> # Invalid: No connectivity
115
+ >>> G3, node3 = create_nfr("isolated", epi=0.8, vf=0.9)
116
+ >>> validate_resonance_strict(G3, node3) # doctest: +SKIP
117
+ Traceback (most recent call last):
118
+ ...
119
+ ValueError: RA requires network connectivity (node has no edges). Apply UM (Coupling) first.
120
+
121
+ See Also
122
+ --------
123
+ tnfr.operators.definitions.Resonance : Resonance operator implementation
124
+ tnfr.operators.definitions.Coupling : Establishes connectivity for RA
125
+ diagnose_resonance_readiness : Diagnostic function for RA readiness
126
+ """
127
+ from ...alias import get_attr
128
+ from ...constants.aliases import ALIAS_DNFR, ALIAS_EPI, ALIAS_THETA, ALIAS_VF
129
+ from ...utils.numeric import angle_diff
130
+
131
+ # Get configuration with defensive fallbacks
132
+ if min_epi is None:
133
+ min_epi = float(G.graph.get("RA_MIN_SOURCE_EPI", 0.1))
134
+ if max_dissonance is None:
135
+ max_dissonance = float(G.graph.get("RA_MAX_DISSONANCE", 0.5))
136
+ min_vf = float(G.graph.get("RA_MIN_VF", 0.01))
137
+ max_phase_diff = float(G.graph.get("RA_MAX_PHASE_DIFF", 1.0)) # ~60 degrees
138
+
139
+ # 1. Validate coherent source EPI
140
+ epi = abs(float(get_attr(G.nodes[node], ALIAS_EPI, 0.0)))
141
+ if epi < min_epi:
142
+ raise ValueError(
143
+ f"RA requires coherent source with EPI >= {min_epi:.1f} "
144
+ f"(current: {epi:.3f}). Apply IL or THOL first."
145
+ )
146
+
147
+ # 2. Validate network connectivity
148
+ neighbors = list(G.neighbors(node))
149
+ if require_coupling:
150
+ if not neighbors:
151
+ raise ValueError(
152
+ f"RA requires network connectivity (node has no edges). "
153
+ "Apply UM (Coupling) first to establish resonant links."
154
+ )
155
+ elif not neighbors:
156
+ # Node is isolated but require_coupling=False - issue warning
157
+ warnings.warn(
158
+ f"Node {node} is isolated - RA will have no propagation effect. "
159
+ "Consider applying UM (Coupling) first.",
160
+ UserWarning,
161
+ stacklevel=3,
162
+ )
163
+
164
+ # 3. Validate sufficient structural frequency
165
+ vf = float(get_attr(G.nodes[node], ALIAS_VF, 0.0))
166
+ if vf < min_vf:
167
+ raise ValueError(
168
+ f"RA requires sufficient structural frequency νf >= {min_vf:.2f} "
169
+ f"(current: {vf:.3f}). Apply AL (Emission) or VAL (Expansion) first."
170
+ )
171
+
172
+ # 4. Validate controlled dissonance
173
+ dnfr = abs(float(get_attr(G.nodes[node], ALIAS_DNFR, 0.0)))
174
+ if dnfr > max_dissonance:
175
+ raise ValueError(
176
+ f"RA requires controlled dissonance with |ΔNFR| <= {max_dissonance:.1f} "
177
+ f"(current: {dnfr:.3f}). Apply IL (Coherence) first to stabilize."
178
+ )
179
+
180
+ # 5. Validate phase compatibility (warning only, neighbors exist)
181
+ if warn_phase_misalignment and neighbors:
182
+ try:
183
+ from ...metrics.trig import neighbor_phase_mean
184
+
185
+ theta_node = float(get_attr(G.nodes[node], ALIAS_THETA, 0.0))
186
+ theta_neighbors = neighbor_phase_mean(G, node)
187
+ phase_diff = abs(angle_diff(theta_neighbors, theta_node))
188
+
189
+ if phase_diff > max_phase_diff:
190
+ warnings.warn(
191
+ f"RA phase misalignment: Δφ = {phase_diff:.2f} > {max_phase_diff:.2f}. "
192
+ "Consider applying UM (Coupling) first for better resonance.",
193
+ UserWarning,
194
+ stacklevel=3,
195
+ )
196
+ except Exception:
197
+ # Phase validation is optional, don't fail if unavailable
198
+ pass
199
+
200
+
201
+ def diagnose_resonance_readiness(G: TNFRGraph, node: Any) -> dict[str, Any]:
202
+ """Diagnose node readiness for RA (Resonance) operator.
203
+
204
+ Provides comprehensive diagnostic report with readiness status and
205
+ actionable recommendations for RA operator application.
206
+
207
+ Parameters
208
+ ----------
209
+ G : TNFRGraph
210
+ Graph containing the node
211
+ node : Any
212
+ Node to diagnose
213
+
214
+ Returns
215
+ -------
216
+ dict
217
+ Diagnostic report with:
218
+ - ``ready``: bool - overall readiness status
219
+ - ``checks``: dict - individual check results (passed/failed/warning)
220
+ - ``values``: dict - current node state values
221
+ - ``recommendations``: list - actionable steps to achieve readiness
222
+ - ``canonical_sequences``: list - suggested operator sequences
223
+
224
+ Examples
225
+ --------
226
+ >>> from tnfr.structural import create_nfr
227
+ >>> from tnfr.operators.preconditions.resonance import diagnose_resonance_readiness
228
+ >>>
229
+ >>> # Diagnose weak source
230
+ >>> G, node = create_nfr("weak", epi=0.05, vf=0.9)
231
+ >>> diag = diagnose_resonance_readiness(G, node)
232
+ >>> diag["ready"]
233
+ False
234
+ >>> "coherent_source" in diag["checks"]
235
+ True
236
+ >>> diag["checks"]["coherent_source"]
237
+ 'failed'
238
+ >>> "Apply IL (Coherence) or THOL (Self-organization)" in diag["recommendations"][0]
239
+ True
240
+
241
+ See Also
242
+ --------
243
+ validate_resonance_strict : Strict precondition validator
244
+ """
245
+ from ...alias import get_attr
246
+ from ...constants.aliases import ALIAS_DNFR, ALIAS_EPI, ALIAS_THETA, ALIAS_VF
247
+ from ...utils.numeric import angle_diff
248
+
249
+ # Get thresholds
250
+ min_epi = float(G.graph.get("RA_MIN_SOURCE_EPI", 0.1))
251
+ max_dissonance = float(G.graph.get("RA_MAX_DISSONANCE", 0.5))
252
+ min_vf = float(G.graph.get("RA_MIN_VF", 0.01))
253
+ max_phase_diff = float(G.graph.get("RA_MAX_PHASE_DIFF", 1.0))
254
+
255
+ # Get current state
256
+ epi = abs(float(get_attr(G.nodes[node], ALIAS_EPI, 0.0)))
257
+ vf = float(get_attr(G.nodes[node], ALIAS_VF, 0.0))
258
+ dnfr = abs(float(get_attr(G.nodes[node], ALIAS_DNFR, 0.0)))
259
+ theta = float(get_attr(G.nodes[node], ALIAS_THETA, 0.0))
260
+ neighbors = list(G.neighbors(node))
261
+ neighbor_count = len(neighbors)
262
+
263
+ # Initialize checks
264
+ checks = {}
265
+ recommendations = []
266
+
267
+ # Check 1: Coherent source
268
+ if epi >= min_epi:
269
+ checks["coherent_source"] = "passed"
270
+ else:
271
+ checks["coherent_source"] = "failed"
272
+ recommendations.append(
273
+ f"Apply IL (Coherence) or THOL (Self-organization) to increase EPI "
274
+ f"from {epi:.3f} to >= {min_epi:.1f}"
275
+ )
276
+
277
+ # Check 2: Network connectivity
278
+ if neighbor_count > 0:
279
+ checks["network_connectivity"] = "passed"
280
+ else:
281
+ checks["network_connectivity"] = "failed"
282
+ recommendations.append(
283
+ "Apply UM (Coupling) to establish network connections before RA"
284
+ )
285
+
286
+ # Check 3: Structural frequency
287
+ if vf >= min_vf:
288
+ checks["structural_frequency"] = "passed"
289
+ else:
290
+ checks["structural_frequency"] = "failed"
291
+ recommendations.append(
292
+ f"Apply AL (Emission) or VAL (Expansion) to increase νf "
293
+ f"from {vf:.3f} to >= {min_vf:.2f}"
294
+ )
295
+
296
+ # Check 4: Controlled dissonance
297
+ if dnfr <= max_dissonance:
298
+ checks["controlled_dissonance"] = "passed"
299
+ else:
300
+ checks["controlled_dissonance"] = "failed"
301
+ recommendations.append(
302
+ f"Apply IL (Coherence) to reduce |ΔNFR| from {dnfr:.3f} "
303
+ f"to <= {max_dissonance:.1f}"
304
+ )
305
+
306
+ # Check 5: Phase alignment (warning only)
307
+ phase_diff = None
308
+ if neighbor_count > 0:
309
+ try:
310
+ from ...metrics.trig import neighbor_phase_mean
311
+
312
+ theta_neighbors = neighbor_phase_mean(G, node)
313
+ phase_diff = abs(angle_diff(theta_neighbors, theta))
314
+
315
+ if phase_diff <= max_phase_diff:
316
+ checks["phase_alignment"] = "passed"
317
+ else:
318
+ checks["phase_alignment"] = "warning"
319
+ recommendations.append(
320
+ f"Consider applying UM (Coupling) to improve phase alignment "
321
+ f"(current: Δφ = {phase_diff:.2f}, optimal: <= {max_phase_diff:.2f})"
322
+ )
323
+ except Exception:
324
+ checks["phase_alignment"] = "unavailable"
325
+ else:
326
+ checks["phase_alignment"] = "n/a"
327
+
328
+ # Determine overall readiness
329
+ critical_checks = [
330
+ "coherent_source",
331
+ "network_connectivity",
332
+ "structural_frequency",
333
+ "controlled_dissonance",
334
+ ]
335
+ ready = all(checks.get(check) == "passed" for check in critical_checks)
336
+
337
+ # Canonical sequences
338
+ canonical_sequences = [
339
+ "UM → RA (Coupling then Resonance)",
340
+ "AL → RA (Emission then Resonance)",
341
+ "IL → RA (Coherence then Resonance)",
342
+ "AL → EN → IL → UM → RA (Full activation sequence)",
343
+ ]
344
+
345
+ return {
346
+ "ready": ready,
347
+ "checks": checks,
348
+ "values": {
349
+ "epi": epi,
350
+ "vf": vf,
351
+ "dnfr": dnfr,
352
+ "theta": theta,
353
+ "neighbor_count": neighbor_count,
354
+ "phase_diff": phase_diff,
355
+ },
356
+ "recommendations": recommendations,
357
+ "canonical_sequences": canonical_sequences,
358
+ "thresholds": {
359
+ "min_epi": min_epi,
360
+ "max_dissonance": max_dissonance,
361
+ "min_vf": min_vf,
362
+ "max_phase_diff": max_phase_diff,
363
+ },
364
+ }
@@ -0,0 +1,74 @@
1
+ """Registry mapping operator names to their classes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib
6
+ import pkgutil
7
+ from typing import TYPE_CHECKING
8
+
9
+ from ..config.operator_names import canonical_operator_name
10
+
11
+ if TYPE_CHECKING: # pragma: no cover - type checking only
12
+ from .definitions import Operator
13
+
14
+ OPERATORS: dict[str, type["Operator"]] = {}
15
+
16
+
17
+ def register_operator(cls: type["Operator"]) -> type["Operator"]:
18
+ """Register ``cls`` under its declared ``name`` in :data:`OPERATORS`."""
19
+
20
+ name = getattr(cls, "name", None)
21
+ if not isinstance(name, str) or not name:
22
+ raise ValueError(
23
+ f"Operator {cls.__name__} must declare a non-empty 'name' attribute"
24
+ )
25
+
26
+ existing = OPERATORS.get(name)
27
+ if existing is not None and existing is not cls:
28
+ raise ValueError(f"Operator '{name}' is already registered")
29
+
30
+ OPERATORS[name] = cls
31
+ return cls
32
+
33
+
34
+ def get_operator_class(name: str) -> type["Operator"]:
35
+ """Return the operator class registered for ``name`` or its canonical alias."""
36
+
37
+ try:
38
+ return OPERATORS[name]
39
+ except KeyError:
40
+ canonical = canonical_operator_name(name)
41
+ if canonical == name:
42
+ raise
43
+ try:
44
+ return OPERATORS[canonical]
45
+ except KeyError as exc: # pragma: no cover - defensive branch
46
+ raise KeyError(name) from exc
47
+
48
+
49
+ def discover_operators() -> None:
50
+ """Import all operator submodules so their decorators run."""
51
+
52
+ package = importlib.import_module("tnfr.operators")
53
+ package_path = getattr(package, "__path__", None)
54
+ if not package_path:
55
+ return
56
+
57
+ if getattr(package, "_operators_discovered", False): # pragma: no cover - cache
58
+ return
59
+
60
+ prefix = f"{package.__name__}."
61
+ for module_info in pkgutil.walk_packages(package_path, prefix):
62
+ if module_info.name == f"{prefix}registry":
63
+ continue
64
+ importlib.import_module(module_info.name)
65
+
66
+ setattr(package, "_operators_discovered", True)
67
+
68
+
69
+ __all__ = (
70
+ "OPERATORS",
71
+ "register_operator",
72
+ "discover_operators",
73
+ "get_operator_class",
74
+ )
@@ -0,0 +1,9 @@
1
+ from .definitions import Operator
2
+
3
+ __all__ = ["OPERATORS", "register_operator", "discover_operators", "get_operator_class"]
4
+
5
+ OPERATORS: dict[str, type[Operator]]
6
+
7
+ def register_operator(cls: type[Operator]) -> type[Operator]: ...
8
+ def get_operator_class(name: str) -> type[Operator]: ...
9
+ def discover_operators() -> None: ...