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,83 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Literal, Sequence
4
+
5
+ from tnfr.types import GlyphCode, TNFRGraph
6
+
7
+ __all__: tuple[str, ...]
8
+
9
+ dnfr: Any
10
+ integrators: Any
11
+ metabolism: Any
12
+
13
+ ALIAS_D2EPI: Sequence[str]
14
+ ALIAS_DNFR: Sequence[str]
15
+ ALIAS_DSI: Sequence[str]
16
+ ALIAS_EPI: Sequence[str]
17
+ ALIAS_SI: Sequence[str]
18
+ ALIAS_VF: Sequence[str]
19
+
20
+ AbstractSelector: Any
21
+ DefaultGlyphSelector: Any
22
+ ParametricGlyphSelector: Any
23
+ StructuralFeedbackLoop: Any
24
+ AdaptiveSequenceSelector: Any
25
+ StructuralHomeostasis: Any
26
+ AdaptiveLearningSystem: Any
27
+ _SelectorPreselection: Any
28
+ _apply_glyphs: Any
29
+ _apply_selector: Any
30
+ _choose_glyph: Any
31
+ _configure_selector_weights: Any
32
+ ProcessPoolExecutor: Any
33
+ _maybe_remesh: Any
34
+ _normalize_job_overrides: Any
35
+ _prepare_dnfr: Any
36
+ _prepare_dnfr_data: Any
37
+ _prepare_selector_preselection: Any
38
+ _resolve_jobs_override: Any
39
+ _resolve_preselected_glyph: Any
40
+ _run_after_callbacks: Any
41
+ _run_before_callbacks: Any
42
+ _run_validators: Any
43
+ _selector_parallel_jobs: Any
44
+ _update_epi_hist: Any
45
+ _update_node_sample: Any
46
+ _update_nodes: Any
47
+ _compute_dnfr: Any
48
+ _compute_neighbor_means: Any
49
+ _init_dnfr_cache: Any
50
+ _refresh_dnfr_vectors: Any
51
+ adapt_vf_by_coherence: Any
52
+ coordinate_global_local_phase: Any
53
+ default_compute_delta_nfr: Any
54
+ default_glyph_selector: Any
55
+ dnfr_epi_vf_mixed: Any
56
+ dnfr_laplacian: Any
57
+ dnfr_phase_only: Any
58
+ get_numpy: Any
59
+ apply_glyph: Any
60
+ parametric_glyph_selector: Any
61
+
62
+ AbstractIntegrator: Any
63
+ DefaultIntegrator: Any
64
+
65
+ def prepare_integration_params(
66
+ G: TNFRGraph,
67
+ dt: float | None = ...,
68
+ t: float | None = ...,
69
+ method: Literal["euler", "rk4"] | None = ...,
70
+ ) -> tuple[float, int, float, Literal["euler", "rk4"]]: ...
71
+
72
+ run: Any
73
+ set_delta_nfr_hook: Any
74
+ step: Any
75
+
76
+ def update_epi_via_nodal_equation(
77
+ G: TNFRGraph,
78
+ *,
79
+ dt: float | None = ...,
80
+ t: float | None = ...,
81
+ method: Literal["euler", "rk4"] | None = ...,
82
+ n_jobs: int | None = ...,
83
+ ) -> None: ...
@@ -0,0 +1,267 @@
1
+ """νf adaptation routines for TNFR dynamics."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ from concurrent.futures import ProcessPoolExecutor
7
+ from typing import Any, cast
8
+
9
+ from ..alias import collect_attr, set_vf
10
+ from ..constants import get_graph_param
11
+ from ..utils import clamp, resolve_chunk_size
12
+ from ..metrics.common import ensure_neighbors_map
13
+ from ..types import CoherenceMetric, DeltaNFR, TNFRGraph
14
+ from ..utils import get_numpy
15
+ from .aliases import ALIAS_DNFR, ALIAS_SI, ALIAS_VF
16
+
17
+ __all__ = ("adapt_vf_by_coherence",)
18
+
19
+
20
+ def _vf_adapt_chunk(
21
+ args: tuple[list[tuple[Any, int, tuple[int, ...]]], tuple[float, ...], float],
22
+ ) -> list[tuple[Any, float]]:
23
+ """Return proposed νf updates for ``chunk`` of stable nodes."""
24
+
25
+ chunk, vf_values, mu = args
26
+ updates: list[tuple[Any, float]] = []
27
+ for node, idx, neighbor_idx in chunk:
28
+ vf = vf_values[idx]
29
+ if neighbor_idx:
30
+ mean = math.fsum(vf_values[j] for j in neighbor_idx) / len(neighbor_idx)
31
+ else:
32
+ mean = vf
33
+ updates.append((node, vf + mu * (mean - vf)))
34
+ return updates
35
+
36
+
37
+ def adapt_vf_by_coherence(G: TNFRGraph, n_jobs: int | None = None) -> None:
38
+ """Synchronise νf to the neighbour mean once ΔNFR and Si stay coherent.
39
+
40
+ Parameters
41
+ ----------
42
+ G : TNFRGraph
43
+ Graph that stores the TNFR nodes and configuration required for
44
+ adaptation. The routine reads ``VF_ADAPT_TAU`` (τ) to decide how many
45
+ consecutive stable steps a node must accumulate in ``stable_count``
46
+ before updating. The adaptation weight ``VF_ADAPT_MU`` (μ) controls how
47
+ quickly νf moves toward the neighbour mean. Stability is detected when
48
+ the absolute ΔNFR stays below ``EPS_DNFR_STABLE`` and the sense index Si
49
+ exceeds the selector threshold ``SELECTOR_THRESHOLDS['si_hi']`` (falling
50
+ back to ``GLYPH_THRESHOLDS['hi']``). Only nodes that satisfy both
51
+ thresholds for τ successive evaluations are eligible for μ-weighted
52
+ averaging.
53
+ n_jobs : int or None, optional
54
+ Number of worker processes used for eligible nodes. ``None`` (the
55
+ default) keeps the adaptation serial, ``1`` disables parallelism, and
56
+ any value greater than one dispatches chunks of nodes to a
57
+ :class:`~concurrent.futures.ProcessPoolExecutor` so large graphs can
58
+ adjust νf without blocking the main dynamic loop.
59
+
60
+ Returns
61
+ -------
62
+ None
63
+ The graph is updated in place; no value is returned.
64
+
65
+ Raises
66
+ ------
67
+ KeyError
68
+ Raised when ``G.graph`` lacks the canonical adaptation parameters and
69
+ defaults have not been injected.
70
+
71
+ Examples
72
+ --------
73
+ >>> from tnfr.constants import inject_defaults
74
+ >>> from tnfr.dynamics import adapt_vf_by_coherence
75
+ >>> from tnfr.structural import create_nfr
76
+ >>> G, seed = create_nfr("seed", vf=0.2)
77
+ >>> _, anchor = create_nfr("anchor", graph=G, vf=1.0)
78
+ >>> G.add_edge(seed, anchor)
79
+ >>> inject_defaults(G)
80
+ >>> G.graph["VF_ADAPT_TAU"] = 2 # τ: consecutive stable steps
81
+ >>> G.graph["VF_ADAPT_MU"] = 0.5 # μ: neighbour coupling strength
82
+ >>> G.graph["SELECTOR_THRESHOLDS"] = {"si_hi": 0.8}
83
+ >>> for node in G.nodes:
84
+ ... G.nodes[node]["Si"] = 0.9 # above ΔSi threshold
85
+ ... G.nodes[node]["ΔNFR"] = 0.0 # within |ΔNFR| ≤ eps guard
86
+ ... G.nodes[node]["stable_count"] = 1
87
+ >>> adapt_vf_by_coherence(G)
88
+ >>> round(G.nodes[seed]["νf"], 2), round(G.nodes[anchor]["νf"], 2)
89
+ (0.6, 0.6)
90
+ >>> G.nodes[seed]["stable_count"], G.nodes[anchor]["stable_count"] >= 2
91
+ (2, True)
92
+ """
93
+
94
+ required_keys = ("VF_ADAPT_TAU", "VF_ADAPT_MU")
95
+ missing_keys = [key for key in required_keys if key not in G.graph]
96
+ if missing_keys:
97
+ missing_list = ", ".join(sorted(missing_keys))
98
+ raise KeyError(
99
+ "adapt_vf_by_coherence requires graph parameters "
100
+ f"{missing_list}; call tnfr.constants.inject_defaults(G) "
101
+ "before adaptation."
102
+ )
103
+
104
+ tau = get_graph_param(G, "VF_ADAPT_TAU", int)
105
+ mu = float(get_graph_param(G, "VF_ADAPT_MU"))
106
+ eps_dnfr = cast(DeltaNFR, get_graph_param(G, "EPS_DNFR_STABLE"))
107
+ thr_sel = get_graph_param(G, "SELECTOR_THRESHOLDS", dict)
108
+ thr_def = get_graph_param(G, "GLYPH_THRESHOLDS", dict)
109
+ si_hi = cast(
110
+ CoherenceMetric,
111
+ float(thr_sel.get("si_hi", thr_def.get("hi", 0.66))),
112
+ )
113
+ vf_min = float(get_graph_param(G, "VF_MIN"))
114
+ vf_max = float(get_graph_param(G, "VF_MAX"))
115
+
116
+ nodes = list(G.nodes)
117
+ if not nodes:
118
+ return
119
+
120
+ neighbors_map = ensure_neighbors_map(G)
121
+ node_count = len(nodes)
122
+ node_index = {node: idx for idx, node in enumerate(nodes)}
123
+
124
+ jobs: int | None
125
+ if n_jobs is None:
126
+ jobs = None
127
+ else:
128
+ try:
129
+ jobs = int(n_jobs)
130
+ except (TypeError, ValueError):
131
+ jobs = None
132
+ else:
133
+ if jobs <= 1:
134
+ jobs = None
135
+
136
+ np_mod = get_numpy()
137
+ use_np = np_mod is not None
138
+
139
+ si_values = collect_attr(G, nodes, ALIAS_SI, 0.0, np=np_mod if use_np else None)
140
+ dnfr_values = collect_attr(G, nodes, ALIAS_DNFR, 0.0, np=np_mod if use_np else None)
141
+ vf_values = collect_attr(G, nodes, ALIAS_VF, 0.0, np=np_mod if use_np else None)
142
+
143
+ if use_np:
144
+ np = np_mod # type: ignore[assignment]
145
+ assert np is not None
146
+ si_arr = si_values.astype(float, copy=False)
147
+ dnfr_arr = np.abs(dnfr_values.astype(float, copy=False))
148
+ vf_arr = vf_values.astype(float, copy=False)
149
+
150
+ prev_counts = np.fromiter(
151
+ (int(G.nodes[node].get("stable_count", 0)) for node in nodes),
152
+ dtype=int,
153
+ count=node_count,
154
+ )
155
+ stable_mask = (si_arr >= si_hi) & (dnfr_arr <= eps_dnfr)
156
+ new_counts = np.where(stable_mask, prev_counts + 1, 0)
157
+
158
+ for node, count in zip(nodes, new_counts.tolist()):
159
+ G.nodes[node]["stable_count"] = int(count)
160
+
161
+ eligible_mask = new_counts >= tau
162
+ if not bool(eligible_mask.any()):
163
+ return
164
+
165
+ max_degree = 0
166
+ if node_count:
167
+ degree_counts = np.fromiter(
168
+ (len(neighbors_map.get(node, ())) for node in nodes),
169
+ dtype=int,
170
+ count=node_count,
171
+ )
172
+ if degree_counts.size:
173
+ max_degree = int(degree_counts.max())
174
+
175
+ if max_degree > 0:
176
+ neighbor_indices = np.zeros((node_count, max_degree), dtype=int)
177
+ mask = np.zeros((node_count, max_degree), dtype=bool)
178
+ for idx, node in enumerate(nodes):
179
+ neigh = neighbors_map.get(node, ())
180
+ if not neigh:
181
+ continue
182
+ idxs = [node_index[nbr] for nbr in neigh if nbr in node_index]
183
+ if not idxs:
184
+ continue
185
+ length = len(idxs)
186
+ neighbor_indices[idx, :length] = idxs
187
+ mask[idx, :length] = True
188
+ neighbor_values = vf_arr[neighbor_indices]
189
+ sums = (neighbor_values * mask).sum(axis=1)
190
+ counts = mask.sum(axis=1)
191
+ neighbor_means = np.where(counts > 0, sums / counts, vf_arr)
192
+ else:
193
+ neighbor_means = vf_arr
194
+
195
+ vf_updates = vf_arr + mu * (neighbor_means - vf_arr)
196
+ for idx in np.nonzero(eligible_mask)[0]:
197
+ node = nodes[int(idx)]
198
+ vf_new = clamp(float(vf_updates[int(idx)]), vf_min, vf_max)
199
+ set_vf(G, node, vf_new)
200
+ return
201
+
202
+ si_list = [float(val) for val in si_values]
203
+ dnfr_list = [abs(float(val)) for val in dnfr_values]
204
+ vf_list = [float(val) for val in vf_values]
205
+
206
+ prev_counts = [int(G.nodes[node].get("stable_count", 0)) for node in nodes]
207
+ stable_flags = [
208
+ si >= si_hi and dnfr <= eps_dnfr for si, dnfr in zip(si_list, dnfr_list)
209
+ ]
210
+ new_counts = [
211
+ prev + 1 if flag else 0 for prev, flag in zip(prev_counts, stable_flags)
212
+ ]
213
+
214
+ for node, count in zip(nodes, new_counts):
215
+ G.nodes[node]["stable_count"] = int(count)
216
+
217
+ eligible_nodes = [node for node, count in zip(nodes, new_counts) if count >= tau]
218
+ if not eligible_nodes:
219
+ return
220
+
221
+ if jobs is None:
222
+ for node in eligible_nodes:
223
+ idx = node_index[node]
224
+ neigh_indices = [
225
+ node_index[nbr]
226
+ for nbr in neighbors_map.get(node, ())
227
+ if nbr in node_index
228
+ ]
229
+ if neigh_indices:
230
+ total = math.fsum(vf_list[i] for i in neigh_indices)
231
+ mean = total / len(neigh_indices)
232
+ else:
233
+ mean = vf_list[idx]
234
+ vf_new = vf_list[idx] + mu * (mean - vf_list[idx])
235
+ set_vf(G, node, clamp(float(vf_new), vf_min, vf_max))
236
+ return
237
+
238
+ work_items: list[tuple[Any, int, tuple[int, ...]]] = []
239
+ for node in eligible_nodes:
240
+ idx = node_index[node]
241
+ neigh_indices = tuple(
242
+ node_index[nbr] for nbr in neighbors_map.get(node, ()) if nbr in node_index
243
+ )
244
+ work_items.append((node, idx, neigh_indices))
245
+
246
+ approx_chunk = math.ceil(len(work_items) / jobs) if jobs else None
247
+ chunk_size = resolve_chunk_size(
248
+ approx_chunk,
249
+ len(work_items),
250
+ minimum=1,
251
+ )
252
+ chunks = [
253
+ work_items[i : i + chunk_size] for i in range(0, len(work_items), chunk_size)
254
+ ]
255
+ vf_tuple = tuple(vf_list)
256
+ updates: dict[Any, float] = {}
257
+ with ProcessPoolExecutor(max_workers=jobs) as executor:
258
+ args = ((chunk, vf_tuple, mu) for chunk in chunks)
259
+ for chunk_updates in executor.map(_vf_adapt_chunk, args):
260
+ for node, value in chunk_updates:
261
+ updates[node] = float(value)
262
+
263
+ for node in eligible_nodes:
264
+ vf_new = updates.get(node)
265
+ if vf_new is None:
266
+ continue
267
+ set_vf(G, node, clamp(float(vf_new), vf_min, vf_max))
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+ from ..types import TNFRGraph
4
+
5
+ __all__ = ["adapt_vf_by_coherence"]
6
+
7
+ def adapt_vf_by_coherence(G: TNFRGraph, n_jobs: int | None = None) -> None: ...
@@ -0,0 +1,189 @@
1
+ """Adaptive sequence selection for TNFR operator trajectories.
2
+
3
+ This module implements learning-based selection of operator sequences.
4
+ Rather than executing fixed sequences, the system learns which sequences
5
+ work best for given contexts and adapts its selection over time.
6
+
7
+ The approach combines predefined canonical sequences with epsilon-greedy
8
+ exploration to balance exploitation (use known good sequences) with
9
+ exploration (try new patterns).
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import random
15
+ from typing import TYPE_CHECKING, Any, Dict, List
16
+
17
+ if TYPE_CHECKING:
18
+ from ..types import TNFRGraph, NodeId
19
+
20
+ try:
21
+ import numpy as np
22
+ except ImportError:
23
+ np = None # type: ignore[assignment]
24
+
25
+ from ..config.operator_names import (
26
+ COHERENCE,
27
+ DISSONANCE,
28
+ EMISSION,
29
+ MUTATION,
30
+ RECEPTION,
31
+ RECURSIVITY,
32
+ RESONANCE,
33
+ SELF_ORGANIZATION,
34
+ SILENCE,
35
+ TRANSITION,
36
+ )
37
+
38
+ __all__ = ["AdaptiveSequenceSelector"]
39
+
40
+
41
+ class AdaptiveSequenceSelector:
42
+ """Learns and selects optimal operator sequences based on context.
43
+
44
+ This class maintains a pool of canonical operator sequences and tracks
45
+ their performance over time. It uses epsilon-greedy selection to balance
46
+ exploitation of known-good sequences with exploration of alternatives.
47
+
48
+ **Selection Strategy:**
49
+
50
+ - **Exploitation (80%)**: Choose sequence with best historical performance
51
+ - **Exploration (20%)**: Random selection to discover new patterns
52
+
53
+ Parameters
54
+ ----------
55
+ graph : TNFRGraph
56
+ Graph containing the node
57
+ node : NodeId
58
+ Identifier of the node
59
+
60
+ Attributes
61
+ ----------
62
+ G : TNFRGraph
63
+ Graph reference
64
+ node : NodeId
65
+ Node identifier
66
+ sequences : dict[str, list[str]]
67
+ Pool of canonical operator sequences
68
+ performance : dict[str, list[float]]
69
+ Historical performance for each sequence
70
+
71
+ Examples
72
+ --------
73
+ >>> from tnfr.structural import create_nfr
74
+ >>> from tnfr.dynamics.adaptive_sequences import AdaptiveSequenceSelector
75
+ >>> G, node = create_nfr("test_node")
76
+ >>> selector = AdaptiveSequenceSelector(G, node)
77
+ >>> context = {"goal": "stability", "urgency": 0.5}
78
+ >>> sequence = selector.select_sequence(context)
79
+ >>> selector.record_performance("basic_activation", 0.85)
80
+ """
81
+
82
+ def __init__(self, graph: TNFRGraph, node: NodeId) -> None:
83
+ self.G = graph
84
+ self.node = node
85
+
86
+ # Canonical operator sequences
87
+ # Note: Sequences are designed to comply with TNFR grammar rules
88
+ self.sequences: Dict[str, List[str]] = {
89
+ "basic_activation": [EMISSION, COHERENCE],
90
+ "deep_learning": [EMISSION, RECEPTION, COHERENCE],
91
+ "exploration": [EMISSION, DISSONANCE, COHERENCE],
92
+ "consolidation": [COHERENCE, SILENCE, RECURSIVITY],
93
+ "mutation": [COHERENCE, MUTATION, TRANSITION, COHERENCE],
94
+ }
95
+
96
+ # Performance history: sequence_name -> [coherence_gains]
97
+ self.performance: Dict[str, List[float]] = {
98
+ k: [] for k in self.sequences.keys()
99
+ }
100
+
101
+ def select_sequence(self, context: Dict[str, Any]) -> List[str]:
102
+ """Select optimal sequence based on context and historical performance.
103
+
104
+ Uses goal-based filtering and epsilon-greedy selection:
105
+
106
+ 1. Filter sequences appropriate for goal
107
+ 2. With probability 0.8: select best-performing sequence
108
+ 3. With probability 0.2: select random sequence (exploration)
109
+
110
+ Parameters
111
+ ----------
112
+ context : dict
113
+ Context with keys:
114
+
115
+ - **goal** (str): "stability", "growth", or "adaptation"
116
+ - **urgency** (float): Urgency level (0-1), currently unused
117
+
118
+ Returns
119
+ -------
120
+ list[str]
121
+ Sequence of operator names to execute
122
+
123
+ Notes
124
+ -----
125
+ Goal-to-sequence mapping follows TNFR principles:
126
+
127
+ - **stability**: Sequences emphasizing IL (Coherence) and SHA (Silence)
128
+ - **growth**: Sequences with AL (Emission) and THOL (Self-organization)
129
+ - **adaptation**: Sequences with ZHIR (Mutation) and learning cycles
130
+ """
131
+ goal = context.get("goal", "stability")
132
+
133
+ # Map goals to appropriate sequence candidates
134
+ if goal == "stability":
135
+ candidates = ["basic_activation", "consolidation"]
136
+ elif goal == "growth":
137
+ candidates = ["deep_learning", "exploration"]
138
+ elif goal == "adaptation":
139
+ candidates = ["mutation", "deep_learning"]
140
+ else:
141
+ candidates = list(self.sequences.keys())
142
+
143
+ # Epsilon-greedy selection (20% exploration, 80% exploitation)
144
+ epsilon = 0.2
145
+
146
+ if np is not None:
147
+ random_val = np.random.random()
148
+ else:
149
+ random_val = random.random()
150
+
151
+ if random_val < epsilon:
152
+ # Exploration: random selection
153
+ if np is not None:
154
+ selected = str(np.random.choice(candidates))
155
+ else:
156
+ selected = random.choice(candidates)
157
+ else:
158
+ # Exploitation: select best-performing sequence
159
+ avg_perf = {
160
+ k: (
161
+ sum(self.performance[k]) / len(self.performance[k])
162
+ if self.performance[k]
163
+ else 0.0
164
+ )
165
+ for k in candidates
166
+ }
167
+ selected = max(avg_perf, key=avg_perf.get) # type: ignore[arg-type]
168
+
169
+ return self.sequences[selected]
170
+
171
+ def record_performance(self, sequence_name: str, coherence_gain: float) -> None:
172
+ """Record performance metric for a sequence to enable learning.
173
+
174
+ Parameters
175
+ ----------
176
+ sequence_name : str
177
+ Name of the sequence that was executed
178
+ coherence_gain : float
179
+ Achieved coherence improvement or other performance metric
180
+
181
+ Notes
182
+ -----
183
+ Maintains a sliding window of the last 20 executions to adapt to
184
+ changing dynamics. Older performance data is discarded.
185
+ """
186
+ if sequence_name in self.performance:
187
+ self.performance[sequence_name].append(float(coherence_gain))
188
+ # Keep only last 20 executions (sliding window)
189
+ self.performance[sequence_name] = self.performance[sequence_name][-20:]
@@ -0,0 +1,14 @@
1
+ """Type stubs for tnfr.dynamics.adaptive_sequences module."""
2
+
3
+ from typing import Any, Dict, List
4
+ from ..types import TNFRGraph, NodeId
5
+
6
+ class AdaptiveSequenceSelector:
7
+ G: TNFRGraph
8
+ node: NodeId
9
+ sequences: Dict[str, List[str]]
10
+ performance: Dict[str, List[float]]
11
+
12
+ def __init__(self, graph: TNFRGraph, node: NodeId) -> None: ...
13
+ def select_sequence(self, context: Dict[str, Any]) -> List[str]: ...
14
+ def record_performance(self, sequence_name: str, coherence_gain: float) -> None: ...
@@ -0,0 +1,23 @@
1
+ """Shared alias tokens used across TNFR dynamics submodules."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..constants.aliases import (
6
+ ALIAS_D2EPI,
7
+ ALIAS_DNFR,
8
+ ALIAS_DSI,
9
+ ALIAS_EPI,
10
+ ALIAS_SI,
11
+ ALIAS_THETA,
12
+ ALIAS_VF,
13
+ )
14
+
15
+ __all__ = (
16
+ "ALIAS_VF",
17
+ "ALIAS_DNFR",
18
+ "ALIAS_EPI",
19
+ "ALIAS_SI",
20
+ "ALIAS_THETA",
21
+ "ALIAS_D2EPI",
22
+ "ALIAS_DSI",
23
+ )
@@ -0,0 +1,19 @@
1
+ from ..constants.aliases import (
2
+ ALIAS_D2EPI as ALIAS_D2EPI,
3
+ ALIAS_DNFR as ALIAS_DNFR,
4
+ ALIAS_DSI as ALIAS_DSI,
5
+ ALIAS_EPI as ALIAS_EPI,
6
+ ALIAS_SI as ALIAS_SI,
7
+ ALIAS_THETA as ALIAS_THETA,
8
+ ALIAS_VF as ALIAS_VF,
9
+ )
10
+
11
+ __all__ = [
12
+ "ALIAS_VF",
13
+ "ALIAS_DNFR",
14
+ "ALIAS_EPI",
15
+ "ALIAS_SI",
16
+ "ALIAS_THETA",
17
+ "ALIAS_D2EPI",
18
+ "ALIAS_DSI",
19
+ ]