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,232 @@
1
+ """Bifurcation dynamics and structural path selection for TNFR operators.
2
+
3
+ This module provides utilities for detecting bifurcation readiness and
4
+ determining viable structural reorganization paths after OZ-induced dissonance.
5
+
6
+ According to TNFR canonical theory (§2.3.3, R4), when ∂²EPI/∂t² > τ,
7
+ the system enters a bifurcation state enabling multiple reorganization
8
+ trajectories. This module implements path selection based on nodal state.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import TYPE_CHECKING
14
+
15
+ if TYPE_CHECKING:
16
+ from ..types import Glyph, NodeId, TNFRGraph
17
+
18
+ from ..alias import get_attr
19
+ from ..constants.aliases import ALIAS_DNFR, ALIAS_EPI, ALIAS_VF
20
+ from ..types import Glyph
21
+
22
+ __all__ = [
23
+ "get_bifurcation_paths",
24
+ "compute_bifurcation_score",
25
+ ]
26
+
27
+
28
+ def get_bifurcation_paths(G: "TNFRGraph", node: "NodeId") -> list["Glyph"]:
29
+ """Return viable structural paths after OZ-induced bifurcation.
30
+
31
+ When OZ (Dissonance) creates bifurcation readiness (∂²EPI/∂t² > τ),
32
+ this function determines which operators can resolve the dissonance
33
+ based on current nodal state.
34
+
35
+ Parameters
36
+ ----------
37
+ G : TNFRGraph
38
+ Graph containing the node
39
+ node : NodeId
40
+ Node identifier
41
+
42
+ Returns
43
+ -------
44
+ list[Glyph]
45
+ List of viable operator glyphs for structural reorganization.
46
+ Empty list if node is not in bifurcation state.
47
+
48
+ Notes
49
+ -----
50
+ **Canonical bifurcation paths:**
51
+
52
+ - **ZHIR (Mutation)**: Viable if νf > 0.8 (sufficient for controlled transformation)
53
+ - **NUL (Contraction)**: Viable if EPI < 0.5 (safe collapse window)
54
+ - **IL (Coherence)**: Always viable (universal resolution path)
55
+ - **THOL (Self-organization)**: Viable if degree >= 2 (network support)
56
+
57
+ The node must have `_bifurcation_ready = True` flag, typically set by
58
+ OZ precondition validation when ∂²EPI/∂t² exceeds threshold τ.
59
+
60
+ Examples
61
+ --------
62
+ >>> from tnfr.structural import create_nfr
63
+ >>> from tnfr.operators.definitions import Dissonance
64
+ >>> from tnfr.dynamics.bifurcation import get_bifurcation_paths
65
+ >>> G, node = create_nfr("test", epi=0.4, vf=1.0)
66
+ >>> # Set up bifurcation conditions
67
+ >>> G.nodes[node]["epi_history"] = [0.2, 0.35, 0.55]
68
+ >>> Dissonance()(G, node, validate_preconditions=True)
69
+ >>> paths = get_bifurcation_paths(G, node)
70
+ >>> # Returns viable operators: [ZHIR, NUL, IL, THOL] or subset
71
+
72
+ See Also
73
+ --------
74
+ tnfr.operators.preconditions.validate_dissonance : Sets bifurcation_ready flag
75
+ tnfr.operators.definitions.SelfOrganization : Spawns sub-EPIs on bifurcation
76
+ """
77
+ # Check if bifurcation active
78
+ if not G.nodes[node].get("_bifurcation_ready", False):
79
+ return [] # No bifurcation active
80
+
81
+ # Get node state for path evaluation
82
+ dnfr = abs(float(get_attr(G.nodes[node], ALIAS_DNFR, 0.0)))
83
+ epi = float(get_attr(G.nodes[node], ALIAS_EPI, 0.0))
84
+ vf = float(get_attr(G.nodes[node], ALIAS_VF, 0.0))
85
+ degree = G.degree(node)
86
+
87
+ paths = []
88
+
89
+ # ZHIR (Mutation) viable if sufficient νf for controlled transformation
90
+ zhir_threshold = float(G.graph.get("ZHIR_BIFURCATION_VF_THRESHOLD", 0.8))
91
+ if vf > zhir_threshold:
92
+ paths.append(Glyph.ZHIR)
93
+
94
+ # NUL (Contraction) viable if EPI low enough for safe collapse
95
+ nul_threshold = float(G.graph.get("NUL_BIFURCATION_EPI_THRESHOLD", 0.5))
96
+ if epi < nul_threshold:
97
+ paths.append(Glyph.NUL)
98
+
99
+ # IL (Coherence) always viable as universal resolution path
100
+ paths.append(Glyph.IL)
101
+
102
+ # THOL (Self-organization) viable if network connectivity supports it
103
+ thol_min_degree = int(G.graph.get("THOL_BIFURCATION_MIN_DEGREE", 2))
104
+ if degree >= thol_min_degree:
105
+ paths.append(Glyph.THOL)
106
+
107
+ return paths
108
+
109
+
110
+ def compute_bifurcation_score(
111
+ d2epi: float,
112
+ dnfr: float,
113
+ vf: float,
114
+ epi: float,
115
+ tau: float = 0.5,
116
+ ) -> float:
117
+ """Compute quantitative bifurcation potential [0,1].
118
+
119
+ Integrates multiple structural indicators to assess bifurcation readiness.
120
+ According to TNFR canonical theory (§2.3.3, R4), bifurcation occurs when
121
+ ∂²EPI/∂t² > τ (acceleration exceeds threshold). This function extends that
122
+ binary condition into a continuous score that accounts for multiple factors.
123
+
124
+ Parameters
125
+ ----------
126
+ d2epi : float
127
+ Structural acceleration (∂²EPI/∂t²). Primary indicator of bifurcation.
128
+ When |d2epi| > τ, the system enters a bifurcation state enabling
129
+ multiple reorganization trajectories.
130
+ dnfr : float
131
+ Internal reorganization operator (ΔNFR). Magnitude indicates instability
132
+ level. Higher |ΔNFR| means stronger reorganization pressure.
133
+ vf : float
134
+ Structural frequency (νf) in Hz_str units. Determines capacity to respond
135
+ to bifurcation. Higher νf enables faster reorganization along new paths.
136
+ epi : float
137
+ Primary Information Structure. Provides structural substrate for
138
+ bifurcation. Higher EPI indicates more material to reorganize.
139
+ tau : float, default 0.5
140
+ Bifurcation acceleration threshold. When |d2epi| > tau, bifurcation
141
+ becomes active. Default 0.5 is the canonical TNFR threshold.
142
+
143
+ Returns
144
+ -------
145
+ float
146
+ Bifurcation score in range [0.0, 1.0]:
147
+ - 0.0 = no bifurcation potential (stable)
148
+ - 0.5 = bifurcation threshold (critical)
149
+ - 1.0 = maximal bifurcation readiness (multiple paths viable)
150
+
151
+ Notes
152
+ -----
153
+ The bifurcation score is a weighted combination of four factors:
154
+
155
+ 1. **Acceleration factor** (40%): |∂²EPI/∂t²| / τ
156
+ Primary indicator. Measures how close the system is to or beyond
157
+ the bifurcation threshold.
158
+
159
+ 2. **Instability factor** (30%): |ΔNFR|
160
+ Secondary indicator. Measures reorganization pressure that drives
161
+ bifurcation exploration.
162
+
163
+ 3. **Capacity factor** (20%): νf / 2.0
164
+ Measures structural reorganization capacity. Higher νf enables faster
165
+ response to bifurcation opportunities.
166
+
167
+ 4. **Substrate factor** (10%): EPI / 0.8
168
+ Measures available structural material. Higher EPI provides more
169
+ degrees of freedom for bifurcation paths.
170
+
171
+ Formula:
172
+ score = 0.4 * accel + 0.3 * instability + 0.2 * capacity + 0.1 * substrate
173
+
174
+ All factors are normalized to [0, 1] and clipped before combination.
175
+
176
+ Examples
177
+ --------
178
+ >>> from tnfr.dynamics.bifurcation import compute_bifurcation_score
179
+ >>>
180
+ >>> # Low bifurcation potential (stable state)
181
+ >>> score = compute_bifurcation_score(
182
+ ... d2epi=0.1, # Low acceleration
183
+ ... dnfr=0.05, # Low instability
184
+ ... vf=0.5, # Moderate capacity
185
+ ... epi=0.3, # Low substrate
186
+ ... )
187
+ >>> assert score < 0.3 # Low score
188
+ >>>
189
+ >>> # High bifurcation potential (critical state)
190
+ >>> score = compute_bifurcation_score(
191
+ ... d2epi=0.7, # High acceleration (> tau)
192
+ ... dnfr=0.6, # High instability
193
+ ... vf=1.8, # High capacity
194
+ ... epi=0.7, # High substrate
195
+ ... )
196
+ >>> assert score > 0.7 # High score
197
+
198
+ See Also
199
+ --------
200
+ get_bifurcation_paths : Determine viable operators after bifurcation
201
+ tnfr.operators.metrics.dissonance_metrics : Uses score in OZ metrics
202
+ """
203
+ from ..utils import get_numpy
204
+
205
+ np = get_numpy()
206
+
207
+ # 1. Acceleration factor (primary indicator)
208
+ # Normalized by tau threshold
209
+ accel_factor = min(abs(d2epi) / tau, 1.0) if tau > 0 else 0.0
210
+
211
+ # 2. Instability factor (secondary indicator)
212
+ # Already dimensionless, clip to [0, 1]
213
+ instability_factor = min(abs(dnfr), 1.0)
214
+
215
+ # 3. Capacity factor (reorganization capability)
216
+ # Normalize by 2.0 (typical high νf value)
217
+ capacity_factor = min(vf / 2.0, 1.0) if vf >= 0 else 0.0
218
+
219
+ # 4. Substrate factor (structural material available)
220
+ # Normalize by 0.8 (typical high EPI value)
221
+ substrate_factor = min(epi / 0.8, 1.0) if epi >= 0 else 0.0
222
+
223
+ # Weighted combination (percentages sum to 100%)
224
+ score = (
225
+ 0.4 * accel_factor # 40% - primary
226
+ + 0.3 * instability_factor # 30% - secondary
227
+ + 0.2 * capacity_factor # 20% - capability
228
+ + 0.1 * substrate_factor # 10% - material
229
+ )
230
+
231
+ # Ensure result is in [0, 1] range
232
+ return float(np.clip(score, 0.0, 1.0))
@@ -0,0 +1,229 @@
1
+ """Canonical TNFR nodal equation implementation.
2
+
3
+ This module provides the explicit, canonical implementation of the fundamental
4
+ TNFR nodal equation as specified in the theory:
5
+
6
+ ∂EPI/∂t = νf · ΔNFR(t)
7
+
8
+ Where:
9
+ - EPI: Primary Information Structure (coherent form)
10
+ - νf: Structural frequency in Hz_str (structural hertz)
11
+ - ΔNFR: Nodal gradient (reorganization operator)
12
+ - t: Structural time (not chronological time)
13
+
14
+ This implementation ensures theoretical fidelity to the TNFR paradigm by:
15
+ 1. Making the canonical equation explicit in code
16
+ 2. Validating dimensional consistency (Hz_str units)
17
+ 3. Providing clear mapping between theory and implementation
18
+ 4. Maintaining reproducibility and traceability
19
+
20
+ TNFR Invariants (from AGENTS.md):
21
+ - EPI as coherent form: changes only via structural operators
22
+ - Structural units: νf expressed in Hz_str (structural hertz)
23
+ - ΔNFR semantics: sign and magnitude modulate reorganization rate
24
+ - Operator closure: composition yields valid TNFR states
25
+
26
+ References:
27
+ - TNFR.pdf: Canonical nodal equation specification
28
+ - AGENTS.md: Section 3 (Canonical invariants)
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ import math
34
+ from typing import TYPE_CHECKING, NamedTuple
35
+
36
+ if TYPE_CHECKING:
37
+ from ..types import GraphLike
38
+
39
+ __all__ = (
40
+ "NodalEquationResult",
41
+ "compute_canonical_nodal_derivative",
42
+ "validate_structural_frequency",
43
+ "validate_nodal_gradient",
44
+ )
45
+
46
+
47
+ class NodalEquationResult(NamedTuple):
48
+ """Result of canonical nodal equation evaluation.
49
+
50
+ Attributes:
51
+ derivative: ∂EPI/∂t computed from νf · ΔNFR(t)
52
+ nu_f: Structural frequency (Hz_str) used in computation
53
+ delta_nfr: Nodal gradient (ΔNFR) used in computation
54
+ validated: Whether units and bounds were validated
55
+ """
56
+
57
+ derivative: float
58
+ nu_f: float
59
+ delta_nfr: float
60
+ validated: bool
61
+
62
+
63
+ def compute_canonical_nodal_derivative(
64
+ nu_f: float,
65
+ delta_nfr: float,
66
+ *,
67
+ validate_units: bool = True,
68
+ graph: GraphLike | None = None,
69
+ ) -> NodalEquationResult:
70
+ """Compute ∂EPI/∂t using the canonical TNFR nodal equation.
71
+
72
+ This is the explicit implementation of the fundamental equation:
73
+ ∂EPI/∂t = νf · ΔNFR(t)
74
+
75
+ The function computes the time derivative of the Primary Information
76
+ Structure (EPI) as the product of:
77
+ - νf: structural frequency (reorganization rate in Hz_str)
78
+ - ΔNFR: nodal gradient (reorganization need/operator)
79
+
80
+ Args:
81
+ nu_f: Structural frequency in Hz_str (must be non-negative)
82
+ delta_nfr: Nodal gradient (reorganization operator)
83
+ validate_units: If True, validates that inputs are in valid ranges
84
+ graph: Optional graph for context-aware validation
85
+
86
+ Returns:
87
+ NodalEquationResult containing the computed derivative and metadata
88
+
89
+ Raises:
90
+ ValueError: If validation is enabled and inputs are invalid
91
+
92
+ Notes:
93
+ - This function is the canonical reference implementation
94
+ - The result represents the instantaneous rate of EPI evolution
95
+ - Units: [∂EPI/∂t] = Hz_str (structural reorganization rate)
96
+ - The product νf·ΔNFR must preserve TNFR operator closure
97
+
98
+ Examples:
99
+ >>> # Basic computation
100
+ >>> result = compute_canonical_nodal_derivative(1.0, 0.5)
101
+ >>> result.derivative
102
+ 0.5
103
+
104
+ >>> # With explicit validation
105
+ >>> result = compute_canonical_nodal_derivative(
106
+ ... nu_f=1.2,
107
+ ... delta_nfr=-0.3,
108
+ ... validate_units=True
109
+ ... )
110
+ >>> result.validated
111
+ True
112
+ """
113
+ validated = False
114
+
115
+ if validate_units:
116
+ nu_f = validate_structural_frequency(nu_f, graph=graph)
117
+ delta_nfr = validate_nodal_gradient(delta_nfr, graph=graph)
118
+ validated = True
119
+
120
+ # Canonical TNFR nodal equation: ∂EPI/∂t = νf · ΔNFR(t)
121
+ derivative = float(nu_f) * float(delta_nfr)
122
+
123
+ return NodalEquationResult(
124
+ derivative=derivative,
125
+ nu_f=nu_f,
126
+ delta_nfr=delta_nfr,
127
+ validated=validated,
128
+ )
129
+
130
+
131
+ def validate_structural_frequency(
132
+ nu_f: float,
133
+ *,
134
+ graph: GraphLike | None = None,
135
+ ) -> float:
136
+ """Validate that structural frequency is in valid range.
137
+
138
+ Structural frequency (νf) must satisfy TNFR constraints:
139
+ - Non-negative (νf ≥ 0)
140
+ - Expressed in Hz_str (structural hertz)
141
+ - Finite and well-defined
142
+
143
+ Args:
144
+ nu_f: Structural frequency to validate
145
+ graph: Optional graph for context-aware bounds checking
146
+
147
+ Returns:
148
+ Validated structural frequency value
149
+
150
+ Raises:
151
+ ValueError: If nu_f is negative, infinite, or NaN
152
+ TypeError: If nu_f cannot be converted to float
153
+
154
+ Notes:
155
+ - νf = 0 is valid and represents structural silence
156
+ - Units must be Hz_str (not classical Hz)
157
+ - For Hz↔Hz_str conversion, use tnfr.units module
158
+ """
159
+ try:
160
+ value = float(nu_f)
161
+ except TypeError as exc:
162
+ # Non-convertible type (e.g., None, object())
163
+ raise TypeError(
164
+ f"Structural frequency must be numeric, got {type(nu_f).__name__}"
165
+ ) from exc
166
+ except ValueError as exc:
167
+ # Invalid string value (e.g., "invalid")
168
+ raise ValueError(
169
+ f"Structural frequency must be a valid number, got {nu_f!r}"
170
+ ) from exc
171
+
172
+ # Check for NaN or infinity using math.isfinite
173
+ if not math.isfinite(value):
174
+ raise ValueError(f"Structural frequency must be finite, got νf={value}")
175
+
176
+ if value < 0:
177
+ raise ValueError(f"Structural frequency must be non-negative, got νf={value}")
178
+
179
+ return value
180
+
181
+
182
+ def validate_nodal_gradient(
183
+ delta_nfr: float,
184
+ *,
185
+ graph: GraphLike | None = None,
186
+ ) -> float:
187
+ """Validate that nodal gradient is well-defined.
188
+
189
+ The nodal gradient (ΔNFR) represents the internal reorganization
190
+ operator and must be:
191
+ - Finite and well-defined
192
+ - Sign indicates reorganization direction
193
+ - Magnitude indicates reorganization intensity
194
+
195
+ Args:
196
+ delta_nfr: Nodal gradient to validate
197
+ graph: Optional graph for context-aware validation
198
+
199
+ Returns:
200
+ Validated nodal gradient value
201
+
202
+ Raises:
203
+ ValueError: If delta_nfr is infinite or NaN
204
+ TypeError: If delta_nfr cannot be converted to float
205
+
206
+ Notes:
207
+ - ΔNFR can be positive (expansion) or negative (contraction)
208
+ - ΔNFR = 0 indicates equilibrium (no reorganization)
209
+ - Do NOT reinterpret as classical "error gradient"
210
+ - Semantics: operator over EPI, not optimization target
211
+ """
212
+ try:
213
+ value = float(delta_nfr)
214
+ except TypeError as exc:
215
+ # Non-convertible type (e.g., None, object())
216
+ raise TypeError(
217
+ f"Nodal gradient must be numeric, got {type(delta_nfr).__name__}"
218
+ ) from exc
219
+ except ValueError as exc:
220
+ # Invalid string value (e.g., "invalid")
221
+ raise ValueError(
222
+ f"Nodal gradient must be a valid number, got {delta_nfr!r}"
223
+ ) from exc
224
+
225
+ # Check for NaN or infinity using math.isfinite
226
+ if not math.isfinite(value):
227
+ raise ValueError(f"Nodal gradient must be finite, got ΔNFR={value}")
228
+
229
+ return value
@@ -0,0 +1,48 @@
1
+ """Type stubs for canonical TNFR nodal equation implementation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import NamedTuple
6
+
7
+ from ..types import GraphLike
8
+
9
+ __all__ = (
10
+ "NodalEquationResult",
11
+ "compute_canonical_nodal_derivative",
12
+ "validate_structural_frequency",
13
+ "validate_nodal_gradient",
14
+ )
15
+
16
+ class NodalEquationResult(NamedTuple):
17
+ """Result of canonical nodal equation evaluation."""
18
+
19
+ derivative: float
20
+ nu_f: float
21
+ delta_nfr: float
22
+ validated: bool
23
+
24
+ def compute_canonical_nodal_derivative(
25
+ nu_f: float,
26
+ delta_nfr: float,
27
+ *,
28
+ validate_units: bool = True,
29
+ graph: GraphLike | None = None,
30
+ ) -> NodalEquationResult:
31
+ """Compute ∂EPI/∂t using the canonical TNFR nodal equation."""
32
+ ...
33
+
34
+ def validate_structural_frequency(
35
+ nu_f: float,
36
+ *,
37
+ graph: GraphLike | None = None,
38
+ ) -> float:
39
+ """Validate that structural frequency is in valid range."""
40
+ ...
41
+
42
+ def validate_nodal_gradient(
43
+ delta_nfr: float,
44
+ *,
45
+ graph: GraphLike | None = None,
46
+ ) -> float:
47
+ """Validate that nodal gradient is well-defined."""
48
+ ...