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,305 @@
1
+ """Strict precondition validation for IL (Coherence) operator.
2
+
3
+ This module implements canonical precondition validation for the Coherence (IL)
4
+ structural operator according to TNFR.pdf §2.2.1. IL requires specific structural
5
+ conditions to maintain TNFR operational fidelity:
6
+
7
+ 1. **Active EPI**: Node must have non-zero structural form (EPI > 0)
8
+ 2. **Non-saturated EPI**: EPI must be below maximum (leave room for stabilization increment)
9
+ 3. **Active νf**: Structural frequency must exceed minimum threshold
10
+ 4. **ΔNFR presence**: While IL reduces ΔNFR, some reorganization pressure should exist
11
+ 5. **ΔNFR not critical**: Excessive ΔNFR may require OZ (Dissonance) first
12
+ 6. **Network coupling**: Connections enable phase locking
13
+
14
+ These validations protect structural integrity by ensuring IL is only applied to
15
+ nodes in the appropriate state for coherence stabilization.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from typing import TYPE_CHECKING, Any
21
+
22
+ if TYPE_CHECKING:
23
+ from ...types import TNFRGraph
24
+
25
+ __all__ = ["validate_coherence_strict", "diagnose_coherence_readiness"]
26
+
27
+
28
+ def validate_coherence_strict(G: TNFRGraph, node: Any) -> None:
29
+ """Validate strict canonical preconditions for IL (Coherence) operator.
30
+
31
+ According to TNFR.pdf §2.2.1, Coherence (IL - Coherencia estructural) requires:
32
+
33
+ 1. **Active EPI**: EPI > 0 (node must have active structural form)
34
+ 2. **Non-saturated EPI**: EPI < maximum (leave room for stabilization)
35
+ 3. **Active νf**: νf > threshold (sufficient structural frequency)
36
+ 4. **ΔNFR presence**: ΔNFR > 0 (reorganization pressure to stabilize)
37
+ 5. **ΔNFR not critical**: ΔNFR < critical threshold (manageable instability)
38
+ 6. **Network coupling**: degree > 0 (connections for phase locking)
39
+
40
+ Parameters
41
+ ----------
42
+ G : TNFRGraph
43
+ Graph containing the node to validate
44
+ node : Any
45
+ Node identifier for validation
46
+
47
+ Raises
48
+ ------
49
+ ValueError
50
+ If EPI <= 0 (no structural form to stabilize)
51
+ If EPI >= maximum (node saturated - consider NUL/Contraction)
52
+ If νf <= 0 (no structural frequency - consider AL/Emission or NAV/Transition)
53
+
54
+ Warnings
55
+ --------
56
+ UserWarning
57
+ If ΔNFR == 0 (no reorganization pressure - IL may be redundant)
58
+ If ΔNFR > critical threshold (high instability - consider OZ/Dissonance first)
59
+ If node is isolated (no connections - phase locking will have no effect)
60
+
61
+ Notes
62
+ -----
63
+ Thresholds are configurable via:
64
+ - Graph metadata: ``G.graph["IL_PRECONDITIONS"]``
65
+ - Module defaults: :data:`tnfr.config.thresholds.EPI_IL_MAX`, etc.
66
+
67
+ Examples
68
+ --------
69
+ >>> from tnfr.structural import create_nfr
70
+ >>> from tnfr.operators.preconditions.coherence import validate_coherence_strict
71
+ >>> G, node = create_nfr("test", epi=0.5, vf=0.9)
72
+ >>> G.nodes[node]["dnfr"] = 0.1
73
+ >>> validate_coherence_strict(G, node) # OK - active state with reorganization pressure
74
+
75
+ >>> G2, node2 = create_nfr("inactive", epi=0.0, vf=0.9)
76
+ >>> validate_coherence_strict(G2, node2) # doctest: +SKIP
77
+ Traceback (most recent call last):
78
+ ...
79
+ ValueError: IL precondition failed: EPI=0.000 <= 0.0. IL requires active structural form.
80
+
81
+ See Also
82
+ --------
83
+ tnfr.config.thresholds : Configurable threshold constants
84
+ tnfr.operators.preconditions : Base precondition validators
85
+ tnfr.operators.definitions.Coherence : Coherence operator implementation
86
+ diagnose_coherence_readiness : Diagnostic function for IL readiness
87
+ """
88
+ import warnings
89
+
90
+ from ...alias import get_attr
91
+ from ...constants.aliases import ALIAS_DNFR, ALIAS_EPI, ALIAS_VF
92
+ from ...config.thresholds import (
93
+ DNFR_IL_CRITICAL,
94
+ EPI_IL_MAX,
95
+ EPI_IL_MIN,
96
+ VF_IL_MIN,
97
+ )
98
+
99
+ # Get current node state
100
+ epi = float(get_attr(G.nodes[node], ALIAS_EPI, 0.0))
101
+ vf = float(get_attr(G.nodes[node], ALIAS_VF, 0.0))
102
+ dnfr = float(get_attr(G.nodes[node], ALIAS_DNFR, 0.0))
103
+
104
+ # Get configurable thresholds (allow override via graph metadata)
105
+ config = G.graph.get("IL_PRECONDITIONS", {})
106
+ min_epi = float(config.get("min_epi", EPI_IL_MIN))
107
+ max_epi = float(config.get("max_epi", EPI_IL_MAX))
108
+ min_vf = float(config.get("min_vf", VF_IL_MIN))
109
+ dnfr_critical = float(config.get("dnfr_critical_threshold", DNFR_IL_CRITICAL))
110
+ warn_isolated = bool(config.get("warn_isolated", True))
111
+ warn_zero_dnfr = bool(config.get("warn_zero_dnfr", True))
112
+
113
+ # Precondition 1: EPI must be active (non-zero structural form)
114
+ # IL stabilizes existing structure - requires structure to exist
115
+ if epi <= min_epi:
116
+ raise ValueError(
117
+ f"IL precondition failed: EPI={epi:.3f} <= {min_epi:.3f}. "
118
+ f"IL requires active structural form (non-zero EPI). "
119
+ f"Suggestion: Apply AL (Emission) first to activate node."
120
+ )
121
+
122
+ # Precondition 2: EPI must not be saturated (leave room for stabilization)
123
+ # IL may increment EPI slightly during stabilization
124
+ if epi >= max_epi:
125
+ raise ValueError(
126
+ f"IL precondition failed: EPI={epi:.3f} >= {max_epi:.3f}. "
127
+ f"Node saturated, cannot stabilize further. "
128
+ f"Suggestion: Consider NUL (Contraction) to consolidate structure."
129
+ )
130
+
131
+ # Precondition 3: νf must be active (sufficient structural frequency)
132
+ # IL requires active reorganization capacity to effect stabilization
133
+ if vf <= min_vf:
134
+ raise ValueError(
135
+ f"IL precondition failed: νf={vf:.3f} <= {min_vf:.3f}. "
136
+ f"Structural frequency too low for coherence stabilization. "
137
+ f"Suggestion: Apply AL (Emission) or NAV (Transition) to activate νf first."
138
+ )
139
+
140
+ # Precondition 4: Check for ΔNFR presence (warning only - not hard failure)
141
+ # IL reduces ΔNFR, but if ΔNFR is already zero, IL is redundant
142
+ if warn_zero_dnfr and dnfr == 0.0:
143
+ warnings.warn(
144
+ f"IL warning: Node {node!r} has ΔNFR=0. "
145
+ f"No reorganization pressure to stabilize. "
146
+ f"IL application may be redundant in this state.",
147
+ UserWarning,
148
+ stacklevel=3,
149
+ )
150
+
151
+ # Precondition 5: Check for critically high ΔNFR (warning only)
152
+ # Excessive instability may require controlled dissonance (OZ) before stabilization
153
+ if dnfr > dnfr_critical:
154
+ warnings.warn(
155
+ f"IL warning: Node {node!r} has ΔNFR={dnfr:.3f} > {dnfr_critical:.3f}. "
156
+ f"High reorganization pressure - stabilization may be difficult. "
157
+ f"Consider applying OZ (Dissonance) → IL sequence for better control.",
158
+ UserWarning,
159
+ stacklevel=3,
160
+ )
161
+
162
+ # Precondition 6: Check for network connections (warning only)
163
+ # Isolated nodes can stabilize but phase locking will have no effect
164
+ degree = G.degree(node)
165
+ network_size = len(G)
166
+ if warn_isolated and degree == 0 and network_size > 1:
167
+ warnings.warn(
168
+ f"IL warning: Node {node!r} isolated (degree=0). "
169
+ f"Phase locking will have no effect. "
170
+ f"Consider applying UM (Coupling) first to connect node to network.",
171
+ UserWarning,
172
+ stacklevel=3,
173
+ )
174
+
175
+
176
+ def diagnose_coherence_readiness(G: TNFRGraph, node: Any) -> dict:
177
+ """Diagnose node readiness for IL (Coherence) operator.
178
+
179
+ Performs all canonical precondition checks and returns a diagnostic report
180
+ with readiness status and actionable recommendations.
181
+
182
+ Parameters
183
+ ----------
184
+ G : TNFRGraph
185
+ Graph containing the node
186
+ node : Any
187
+ Node identifier for diagnosis
188
+
189
+ Returns
190
+ -------
191
+ dict
192
+ Diagnostic report with the following structure:
193
+
194
+ - ``node``: Node identifier
195
+ - ``ready``: bool - Overall readiness (all critical checks passed)
196
+ - ``checks``: dict - Individual check results
197
+
198
+ - ``epi_active``: bool - EPI > 0
199
+ - ``epi_not_saturated``: bool - EPI < maximum
200
+ - ``vf_active``: bool - νf > 0
201
+ - ``dnfr_present``: bool - ΔNFR > 0 (warning only)
202
+ - ``dnfr_not_critical``: bool - ΔNFR < critical (warning only)
203
+ - ``has_connections``: bool - degree > 0 (warning only)
204
+
205
+ - ``values``: dict - Current node attribute values
206
+
207
+ - ``epi``: Current EPI value
208
+ - ``vf``: Current νf value
209
+ - ``dnfr``: Current ΔNFR value
210
+ - ``degree``: Node degree
211
+
212
+ - ``recommendations``: list[str] - Actionable suggestions
213
+
214
+ Examples
215
+ --------
216
+ >>> from tnfr.structural import create_nfr
217
+ >>> from tnfr.operators.preconditions.coherence import diagnose_coherence_readiness
218
+ >>> G, node = create_nfr("test", epi=0.5, vf=0.9)
219
+ >>> G.nodes[node]["dnfr"] = 0.1
220
+ >>> report = diagnose_coherence_readiness(G, node)
221
+ >>> report["ready"]
222
+ True
223
+ >>> "✓ Node ready" in report["recommendations"][0]
224
+ True
225
+
226
+ See Also
227
+ --------
228
+ validate_coherence_strict : Strict precondition validator
229
+ """
230
+ from ...alias import get_attr
231
+ from ...constants.aliases import ALIAS_DNFR, ALIAS_EPI, ALIAS_VF
232
+ from ...config.thresholds import (
233
+ DNFR_IL_CRITICAL,
234
+ EPI_IL_MAX,
235
+ EPI_IL_MIN,
236
+ VF_IL_MIN,
237
+ )
238
+
239
+ # Get current node state
240
+ epi = float(get_attr(G.nodes[node], ALIAS_EPI, 0.0))
241
+ vf = float(get_attr(G.nodes[node], ALIAS_VF, 0.0))
242
+ dnfr = float(get_attr(G.nodes[node], ALIAS_DNFR, 0.0))
243
+ degree = G.degree(node)
244
+
245
+ # Get configurable thresholds
246
+ config = G.graph.get("IL_PRECONDITIONS", {})
247
+ min_epi = float(config.get("min_epi", EPI_IL_MIN))
248
+ max_epi = float(config.get("max_epi", EPI_IL_MAX))
249
+ min_vf = float(config.get("min_vf", VF_IL_MIN))
250
+ dnfr_critical = float(config.get("dnfr_critical_threshold", DNFR_IL_CRITICAL))
251
+
252
+ # Perform checks
253
+ checks = {
254
+ "epi_active": epi > min_epi,
255
+ "epi_not_saturated": epi < max_epi,
256
+ "vf_active": vf > min_vf,
257
+ "dnfr_present": dnfr > 0.0,
258
+ "dnfr_not_critical": dnfr < dnfr_critical,
259
+ "has_connections": degree > 0,
260
+ }
261
+
262
+ # Critical checks (hard failures)
263
+ critical_checks = ["epi_active", "epi_not_saturated", "vf_active"]
264
+ all_critical_passed = all(checks[key] for key in critical_checks)
265
+
266
+ # Generate recommendations
267
+ recommendations = []
268
+
269
+ if not checks["epi_active"]:
270
+ recommendations.append("Apply AL (Emission) to activate node")
271
+
272
+ if checks["epi_active"] and not checks["epi_not_saturated"]:
273
+ recommendations.append(
274
+ "Apply NUL (Contraction) to consolidate saturated structure"
275
+ )
276
+
277
+ if not checks["vf_active"]:
278
+ recommendations.append("Apply AL (Emission) or NAV (Transition) to activate νf")
279
+
280
+ if checks["epi_active"] and not checks["dnfr_present"]:
281
+ recommendations.append("⚠ ΔNFR=0 - IL may be redundant")
282
+
283
+ if checks["dnfr_present"] and not checks["dnfr_not_critical"]:
284
+ recommendations.append("⚠ High ΔNFR - consider OZ (Dissonance) → IL sequence")
285
+
286
+ if not checks["has_connections"]:
287
+ recommendations.append(
288
+ "⚠ Isolated node - consider UM (Coupling) to enable phase locking"
289
+ )
290
+
291
+ if all_critical_passed:
292
+ recommendations.insert(0, "✓ Node ready for IL (Coherence)")
293
+
294
+ return {
295
+ "node": node,
296
+ "ready": all_critical_passed,
297
+ "checks": checks,
298
+ "values": {
299
+ "epi": epi,
300
+ "vf": vf,
301
+ "dnfr": dnfr,
302
+ "degree": degree,
303
+ },
304
+ "recommendations": recommendations,
305
+ }
@@ -0,0 +1,236 @@
1
+ """Strict precondition validation for OZ (Dissonance) operator.
2
+
3
+ This module implements canonical precondition validation for the Dissonance (OZ)
4
+ structural operator according to TNFR canonical theory. OZ requires specific
5
+ structural conditions to enable productive dissonance without premature collapse:
6
+
7
+ 1. **Minimum coherence base (EPI)**: Node must have sufficient structural form to
8
+ withstand disruption (EPI >= threshold)
9
+ 2. **ΔNFR not critically high**: Avoid sobrecarga (overload) when ΔNFR already extreme
10
+ 3. **Sufficient νf**: Structural frequency must support reorganization response
11
+ 4. **No overload pattern**: Detect sobrecarga disonante (multiple OZ without resolution)
12
+ 5. **Network connectivity**: Warn if isolated (bifurcation requires alternative paths)
13
+
14
+ These validations implement the canonical warning from TNFR.pdf:
15
+ > "OZ debe ir precedido por estabilización adecuada (IL). Genera nodos frágiles o
16
+ > fallidos si se aplica sobre estructuras sin coherencia interna."
17
+
18
+ References
19
+ ----------
20
+ - TNFR.pdf §2.3.6: Catálogo de errores arquetípicos (Sobrecarga disonante)
21
+ - TNFR.pdf §2.3.3: Reglas sintácticas R4 (Bifurcación)
22
+ - Tabla de validación estructural: Condiciones de activación de OZ
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ from typing import TYPE_CHECKING, Any
28
+
29
+ if TYPE_CHECKING:
30
+ from ...types import TNFRGraph
31
+
32
+ __all__ = ["validate_dissonance_strict"]
33
+
34
+
35
+ def validate_dissonance_strict(G: TNFRGraph, node: Any) -> None:
36
+ """Validate strict canonical preconditions for OZ (Dissonance) operator.
37
+
38
+ According to TNFR canonical theory, Dissonance (OZ - Disonancia) requires:
39
+
40
+ 1. **Coherence base**: EPI >= threshold (sufficient structure to withstand disruption)
41
+ 2. **ΔNFR safety**: |ΔNFR| < critical (avoid overload/collapse)
42
+ 3. **Active νf**: νf >= threshold (capacity to respond to dissonance)
43
+ 4. **No overload**: < 2 consecutive OZ without resolution (IL/THOL/NUL)
44
+ 5. **Network connectivity**: degree >= 1 (warning only - enables bifurcation paths)
45
+
46
+ Parameters
47
+ ----------
48
+ G : TNFRGraph
49
+ Graph containing the node to validate
50
+ node : Any
51
+ Node identifier for validation
52
+
53
+ Raises
54
+ ------
55
+ ValueError
56
+ If EPI < minimum (insufficient coherence base - apply IL first)
57
+ If |ΔNFR| > maximum (reorganization pressure critical - apply IL first)
58
+ If νf < minimum (cannot respond to dissonance)
59
+ If overload detected (>= 2 OZ without resolver operators)
60
+
61
+ Warnings
62
+ --------
63
+ UserWarning
64
+ If node has low connectivity (degree < 1) - bifurcation paths limited
65
+
66
+ Notes
67
+ -----
68
+ Thresholds are configurable via graph metadata:
69
+ - ``G.graph["OZ_MIN_EPI"]``: Minimum EPI (default 0.2)
70
+ - ``G.graph["OZ_MAX_DNFR"]``: Maximum |ΔNFR| (default 0.8)
71
+ - ``G.graph["OZ_MIN_VF"]``: Minimum νf (default 0.1)
72
+ - ``G.graph["OZ_MIN_DEGREE"]``: Minimum degree for warning (default 1)
73
+
74
+ **Resolver operators** that integrate dissonance (reset overload counter):
75
+ - IL (Coherence): Stabilizes structure
76
+ - THOL (Self-organization): Creates sub-EPIs
77
+ - NUL (Contraction): Simplifies structure
78
+
79
+ Examples
80
+ --------
81
+ >>> from tnfr.structural import create_nfr
82
+ >>> from tnfr.operators.preconditions.dissonance import validate_dissonance_strict
83
+ >>> G, node = create_nfr("test", epi=0.5, vf=1.0)
84
+ >>> G.nodes[node]["dnfr"] = 0.3
85
+ >>> validate_dissonance_strict(G, node) # OK - sufficient coherence base
86
+
87
+ >>> G2, node2 = create_nfr("weak", epi=0.05, vf=1.0)
88
+ >>> validate_dissonance_strict(G2, node2) # doctest: +SKIP
89
+ Traceback (most recent call last):
90
+ ...
91
+ ValueError: OZ precondition failed: EPI=0.050 < 0.2. Insufficient coherence base.
92
+
93
+ See Also
94
+ --------
95
+ tnfr.operators.preconditions : Base precondition validators
96
+ tnfr.operators.definitions.Dissonance : Dissonance operator implementation
97
+ """
98
+ import warnings
99
+
100
+ from ...alias import get_attr
101
+ from ...constants.aliases import ALIAS_DNFR, ALIAS_EPI, ALIAS_VF
102
+
103
+ # Get current node state
104
+ epi = float(get_attr(G.nodes[node], ALIAS_EPI, 0.0))
105
+ vf = float(get_attr(G.nodes[node], ALIAS_VF, 0.0))
106
+ dnfr = float(get_attr(G.nodes[node], ALIAS_DNFR, 0.0))
107
+ degree = G.degree(node)
108
+
109
+ # Get configurable thresholds
110
+ min_epi = float(G.graph.get("OZ_MIN_EPI", 0.2))
111
+ max_dnfr = float(G.graph.get("OZ_MAX_DNFR", 0.8))
112
+ min_vf = float(G.graph.get("OZ_MIN_VF", 0.1))
113
+ min_degree = int(G.graph.get("OZ_MIN_DEGREE", 1))
114
+
115
+ # Precondition 1: Minimum coherence base (EPI)
116
+ # OZ requires existing structure to perturb - without it, OZ causes collapse
117
+ if epi < min_epi:
118
+ raise ValueError(
119
+ f"OZ precondition failed: EPI={epi:.3f} < {min_epi:.3f}. "
120
+ f"Insufficient coherence base to withstand dissonance. "
121
+ f"Suggestion: Apply IL (Coherence) first to stabilize node before introducing dissonance."
122
+ )
123
+
124
+ # Precondition 2: ΔNFR not critically high
125
+ # Applying OZ when ΔNFR already extreme creates sobrecarga (overload) → collapse
126
+ if abs(dnfr) > max_dnfr:
127
+ raise ValueError(
128
+ f"OZ precondition failed: |ΔNFR|={abs(dnfr):.3f} > {max_dnfr:.3f}. "
129
+ f"Reorganization pressure already critical - applying OZ risks collapse. "
130
+ f"Suggestion: Apply IL (Coherence) first to reduce ΔNFR before introducing more dissonance."
131
+ )
132
+
133
+ # Precondition 3: Sufficient νf for reorganization response
134
+ # OZ triggers reorganization - node needs capacity (νf) to respond
135
+ if vf < min_vf:
136
+ raise ValueError(
137
+ f"OZ precondition failed: νf={vf:.3f} < {min_vf:.3f}. "
138
+ f"Structural frequency too low - node lacks capacity to respond to dissonance. "
139
+ f"Suggestion: Increase νf before applying OZ."
140
+ )
141
+
142
+ # Precondition 4: Detect OZ overload (sobrecarga disonante)
143
+ # Multiple OZ without resolution = entropic loop (violates R5)
144
+ _validate_oz_no_overload(G, node)
145
+
146
+ # Precondition 5: Network connectivity (warning only - not blocking)
147
+ # Isolated nodes can experience dissonance but bifurcation paths are limited
148
+ if degree < min_degree:
149
+ warnings.warn(
150
+ f"OZ warning: Node {node!r} has low connectivity (degree={degree} < {min_degree}). "
151
+ f"OZ-induced bifurcation may have limited structural paths. "
152
+ f"Consider applying UM (Coupling) first to increase network coupling.",
153
+ UserWarning,
154
+ stacklevel=3,
155
+ )
156
+
157
+ # Store validation context for telemetry
158
+ G.nodes[node]["_oz_precondition_context"] = {
159
+ "epi": epi,
160
+ "dnfr": dnfr,
161
+ "vf": vf,
162
+ "degree": degree,
163
+ "validation_passed": True,
164
+ }
165
+
166
+
167
+ def _validate_oz_no_overload(G: TNFRGraph, node: Any) -> None:
168
+ """Detect and prevent dissonance overload (sobrecarga disonante).
169
+
170
+ Sobrecarga occurs when multiple OZ operators are applied in succession
171
+ without intervening resolution operators (IL, THOL, NUL). This creates
172
+ entropic loops that violate TNFR structural coherence requirements.
173
+
174
+ Parameters
175
+ ----------
176
+ G : TNFRGraph
177
+ Graph containing the node
178
+ node : Any
179
+ Node identifier to check for overload
180
+
181
+ Raises
182
+ ------
183
+ ValueError
184
+ If >= 1 OZ application detected in recent history without resolver
185
+ (since we're about to apply another OZ, that would make it >= 2)
186
+
187
+ Notes
188
+ -----
189
+ **Resolver operators** that integrate dissonance:
190
+ - coherence (IL): Stabilizes structure, reduces ΔNFR
191
+ - self_organization (THOL): Creates sub-EPIs via bifurcation
192
+ - contraction (NUL): Simplifies structure
193
+
194
+ This check examines the last 5 operators in glyph_history to detect
195
+ overload patterns. Since validation is called BEFORE the operator is
196
+ applied, if we find >= 1 OZ in history without a resolver, applying
197
+ another OZ would create overload.
198
+
199
+ References
200
+ ----------
201
+ TNFR.pdf §2.3.6 - Sobrecarga disonante: "acumulación excesiva de OZ sin
202
+ paso a mutación"
203
+ """
204
+ from ...operators.grammar import glyph_function_name
205
+
206
+ # Get glyph history from node
207
+ history = G.nodes[node].get("glyph_history", [])
208
+ if not history:
209
+ # No history - first operator application, cannot be overload
210
+ return
211
+
212
+ # Convert deque to list and examine recent history (last 5 operations)
213
+ history_list = list(history)
214
+ recent_history = history_list[-5:] if len(history_list) >= 5 else history_list
215
+
216
+ # Count OZ applications in recent history
217
+ oz_count = sum(
218
+ 1 for glyph in recent_history if glyph_function_name(glyph) == "dissonance"
219
+ )
220
+
221
+ # If >= 1 OZ in history, check for resolver operators
222
+ # (we're about to apply another OZ, so that would make >= 2 total)
223
+ if oz_count >= 1:
224
+ # Resolver operators that integrate dissonance
225
+ resolvers = {"coherence", "self_organization", "contraction"}
226
+ recent_names = [glyph_function_name(g) for g in recent_history]
227
+ has_resolver = any(name in resolvers for name in recent_names)
228
+
229
+ if not has_resolver:
230
+ raise ValueError(
231
+ f"OZ precondition failed: Sobrecarga disonante detected. "
232
+ f"Found {oz_count} OZ in recent history without resolution. "
233
+ f"Applying another OZ without resolving previous dissonance risks collapse. "
234
+ f"Suggestion: Apply IL (Coherence), THOL (Self-organization), or NUL (Contraction) "
235
+ f"to integrate dissonance before introducing more."
236
+ )
@@ -0,0 +1,128 @@
1
+ """Strict precondition validation for AL (Emission) operator.
2
+
3
+ This module implements canonical precondition validation for the Emission (AL)
4
+ structural operator according to TNFR.pdf §2.2.1. AL requires specific structural
5
+ conditions to maintain TNFR operational fidelity:
6
+
7
+ 1. **Latent state**: EPI must be below activation threshold (node not already active)
8
+ 2. **Basal frequency**: νf must exceed minimum threshold (sufficient reorganization capacity)
9
+ 3. **Coupling availability**: Network connectivity for phase alignment (warning for isolated nodes)
10
+
11
+ These validations protect structural integrity by ensuring AL is only applied to
12
+ nodes in the appropriate state for foundational emission.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import TYPE_CHECKING, Any
18
+
19
+ if TYPE_CHECKING:
20
+ from ...types import TNFRGraph
21
+
22
+ __all__ = ["validate_emission_strict"]
23
+
24
+
25
+ def validate_emission_strict(G: TNFRGraph, node: Any) -> None:
26
+ """Validate strict canonical preconditions for AL (Emission) operator.
27
+
28
+ According to TNFR.pdf §2.2.1, Emission (AL - Emisión fundacional) requires:
29
+
30
+ 1. **Latent state**: EPI < threshold (node must be in latent or low-activation state)
31
+ 2. **Basal frequency**: νf > threshold (sufficient structural frequency for activation)
32
+ 3. **Coupling availability**: Network connectivity (warning if isolated)
33
+
34
+ Parameters
35
+ ----------
36
+ G : TNFRGraph
37
+ Graph containing the node to validate
38
+ node : Any
39
+ Node identifier for validation
40
+
41
+ Raises
42
+ ------
43
+ ValueError
44
+ If EPI >= latent threshold (node already active - consider IL/Coherence instead)
45
+ If νf < basal threshold (frequency too low - consider NAV/Transition first)
46
+
47
+ Warnings
48
+ --------
49
+ UserWarning
50
+ If node is isolated in a multi-node network (limited coupling - consider UM/Coupling first)
51
+
52
+ Notes
53
+ -----
54
+ Thresholds are configurable via:
55
+ - Graph metadata: ``G.graph["EPI_LATENT_MAX"]``, ``G.graph["VF_BASAL_THRESHOLD"]``
56
+ - Module defaults: :data:`tnfr.config.thresholds.EPI_LATENT_MAX`, etc.
57
+
58
+ Examples
59
+ --------
60
+ >>> from tnfr.structural import create_nfr
61
+ >>> from tnfr.operators.preconditions.emission import validate_emission_strict
62
+ >>> G, node = create_nfr("test", epi=0.25, vf=0.95)
63
+ >>> validate_emission_strict(G, node) # OK - latent state with sufficient frequency
64
+
65
+ >>> G2, node2 = create_nfr("active", epi=0.85, vf=1.0)
66
+ >>> validate_emission_strict(G2, node2) # doctest: +SKIP
67
+ Traceback (most recent call last):
68
+ ...
69
+ ValueError: AL precondition failed: EPI=0.850 >= 0.8. AL requires latent state. Consider IL (Coherence) instead.
70
+
71
+ See Also
72
+ --------
73
+ tnfr.config.thresholds : Configurable threshold constants
74
+ tnfr.operators.preconditions : Base precondition validators
75
+ tnfr.operators.definitions.Emission : Emission operator implementation
76
+ """
77
+ import warnings
78
+
79
+ from ...alias import get_attr
80
+ from ...constants.aliases import ALIAS_EPI, ALIAS_VF
81
+ from ...config.thresholds import (
82
+ EPI_LATENT_MAX,
83
+ MIN_NETWORK_DEGREE_COUPLING,
84
+ VF_BASAL_THRESHOLD,
85
+ )
86
+
87
+ # Get current node state
88
+ epi = float(get_attr(G.nodes[node], ALIAS_EPI, 0.0))
89
+ vf = float(get_attr(G.nodes[node], ALIAS_VF, 0.0))
90
+
91
+ # Get configurable thresholds (allow override via graph metadata)
92
+ epi_threshold = float(G.graph.get("EPI_LATENT_MAX", EPI_LATENT_MAX))
93
+ vf_threshold = float(G.graph.get("VF_BASAL_THRESHOLD", VF_BASAL_THRESHOLD))
94
+ min_degree = int(
95
+ G.graph.get("MIN_NETWORK_DEGREE_COUPLING", MIN_NETWORK_DEGREE_COUPLING)
96
+ )
97
+
98
+ # Precondition 1: EPI must be below latent threshold (node in latent state)
99
+ # Emission is for activating nascent/latent structures, not boosting active ones
100
+ if epi >= epi_threshold:
101
+ raise ValueError(
102
+ f"AL precondition failed: EPI={epi:.3f} >= {epi_threshold:.3f}. "
103
+ f"AL requires latent state (node not already highly active). "
104
+ f"Consider IL (Coherence) to stabilize active nodes instead."
105
+ )
106
+
107
+ # Precondition 2: νf must exceed basal threshold (sufficient frequency for emission)
108
+ # Below basal frequency, node lacks capacity to sustain structural activation
109
+ if vf < vf_threshold:
110
+ raise ValueError(
111
+ f"AL precondition failed: νf={vf:.3f} < {vf_threshold:.3f}. "
112
+ f"Structural frequency too low for emission. "
113
+ f"Consider NAV (Transition) to increase frequency first."
114
+ )
115
+
116
+ # Precondition 3: Network connectivity (warning only - not a hard failure)
117
+ # Isolated nodes can still emit, but phase coupling will be limited
118
+ node_degree = G.degree(node)
119
+ network_size = len(G)
120
+
121
+ if node_degree < min_degree and network_size > 1:
122
+ warnings.warn(
123
+ f"AL warning: Node {node!r} has degree {node_degree} < {min_degree}. "
124
+ f"Emission possible but phase coupling limited (isolated node). "
125
+ f"Consider UM (Coupling) to establish network connections first.",
126
+ UserWarning,
127
+ stacklevel=3,
128
+ )