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,459 @@
1
+ """TNFR-aware network partitioning for parallel computation.
2
+
3
+ Partitions networks respecting structural coherence rather than classical graph
4
+ metrics. Communities are grown based on phase synchrony and frequency alignment
5
+ to preserve the fractal organization inherent in TNFR.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import math
11
+ from typing import TYPE_CHECKING, Any, List, Optional, Set, Tuple
12
+
13
+ if TYPE_CHECKING: # pragma: no cover
14
+ from ..types import TNFRGraph
15
+
16
+ try:
17
+ import numpy as np
18
+
19
+ HAS_NUMPY = True
20
+ except ImportError:
21
+ np = None # type: ignore
22
+ HAS_NUMPY = False
23
+
24
+ try:
25
+ from scipy.spatial import KDTree
26
+
27
+ HAS_SCIPY = True
28
+ except ImportError:
29
+ HAS_SCIPY = False
30
+ KDTree = None # type: ignore
31
+
32
+ from ..alias import get_attr
33
+ from ..constants.aliases import ALIAS_THETA, ALIAS_VF
34
+
35
+
36
+ class FractalPartitioner:
37
+ """Partitions TNFR networks respecting structural coherence.
38
+
39
+ This partitioner detects communities based on TNFR metrics (frequency and
40
+ phase) rather than classical graph metrics. It ensures that nodes with
41
+ similar structural frequencies and synchronized phases are grouped together,
42
+ preserving operational fractality during parallel processing.
43
+
44
+ Parameters
45
+ ----------
46
+ max_partition_size : int, default=100
47
+ Maximum number of nodes per partition. Larger partitions reduce
48
+ communication overhead but may limit parallelism. If None, uses
49
+ adaptive partitioning based on network density.
50
+ coherence_threshold : float, default=0.3
51
+ Minimum coherence score for adding a node to a community. Higher values
52
+ create tighter communities but may result in more partitions.
53
+ use_spatial_index : bool, default=True
54
+ Whether to use spatial indexing (KDTree) for O(n log n) neighbor
55
+ finding. Requires scipy. Falls back to O(n²) if unavailable.
56
+ adaptive : bool, default=True
57
+ Whether to use adaptive partitioning that adjusts partition size
58
+ based on network density and clustering coefficient.
59
+
60
+ Examples
61
+ --------
62
+ >>> import networkx as nx
63
+ >>> from tnfr.parallel import FractalPartitioner
64
+ >>> G = nx.Graph()
65
+ >>> G.add_edges_from([("a", "b"), ("b", "c")])
66
+ >>> for node in G.nodes():
67
+ ... G.nodes[node]["vf"] = 1.0
68
+ ... G.nodes[node]["phase"] = 0.0
69
+ >>> partitioner = FractalPartitioner(max_partition_size=50)
70
+ >>> partitions = partitioner.partition_network(G)
71
+ >>> len(partitions) >= 1
72
+ True
73
+
74
+ Notes
75
+ -----
76
+ Spatial indexing provides O(n log n) complexity for large networks
77
+ compared to O(n²) without it. Adaptive partitioning automatically
78
+ adjusts partition size based on network characteristics.
79
+ """
80
+
81
+ def __init__(
82
+ self,
83
+ max_partition_size: Optional[int] = 100,
84
+ coherence_threshold: float = 0.3,
85
+ use_spatial_index: bool = True,
86
+ adaptive: bool = True,
87
+ ):
88
+ self.max_partition_size = max_partition_size
89
+ self.coherence_threshold = coherence_threshold
90
+ self.use_spatial_index = use_spatial_index and HAS_SCIPY and HAS_NUMPY
91
+ self.adaptive = adaptive
92
+ self._kdtree = None
93
+ self._node_index_map = None
94
+
95
+ def partition_network(self, graph: TNFRGraph) -> List[Tuple[Set[Any], TNFRGraph]]:
96
+ """Partition network into coherent subgraphs.
97
+
98
+ Parameters
99
+ ----------
100
+ graph : TNFRGraph
101
+ TNFR network to partition. Nodes must have 'vf' and 'phase' attrs.
102
+
103
+ Returns
104
+ -------
105
+ List[Tuple[Set[Any], TNFRGraph]]
106
+ List of (node_set, subgraph) tuples for parallel processing.
107
+
108
+ Notes
109
+ -----
110
+ Maintains TNFR structural invariants:
111
+ - Communities formed by resonance (not just topology)
112
+ - Phase coherence preserved within partitions
113
+ - Frequency alignment respected
114
+
115
+ Uses spatial indexing for O(n log n) complexity when available.
116
+ Adapts partition size based on network density when adaptive=True.
117
+ """
118
+ import networkx as nx
119
+
120
+ if len(graph) == 0:
121
+ return []
122
+
123
+ # Determine optimal partition size adaptively
124
+ if self.adaptive:
125
+ partition_size = self._compute_adaptive_partition_size(graph)
126
+ else:
127
+ partition_size = self.max_partition_size or 100
128
+
129
+ # Build spatial index if requested and available
130
+ if self.use_spatial_index:
131
+ self._build_spatial_index(graph)
132
+
133
+ # Detect TNFR communities
134
+ communities = self._detect_tnfr_communities(graph)
135
+
136
+ # Create balanced partitions
137
+ partitions = []
138
+ current_partition = set()
139
+
140
+ for community in communities:
141
+ if len(current_partition) + len(community) <= partition_size:
142
+ current_partition.update(community)
143
+ else:
144
+ if current_partition:
145
+ subgraph = graph.subgraph(current_partition).copy()
146
+ partitions.append((current_partition.copy(), subgraph))
147
+ current_partition = community.copy()
148
+
149
+ # Add final partition
150
+ if current_partition:
151
+ subgraph = graph.subgraph(current_partition).copy()
152
+ partitions.append((current_partition, subgraph))
153
+
154
+ # Clean up spatial index
155
+ self._kdtree = None
156
+ self._node_index_map = None
157
+
158
+ return partitions
159
+
160
+ def _compute_adaptive_partition_size(self, graph: TNFRGraph) -> int:
161
+ """Compute optimal partition size based on network characteristics.
162
+
163
+ Adapts partition size based on:
164
+ - Network density (sparse vs dense)
165
+ - Clustering coefficient (community structure)
166
+ - Total network size
167
+
168
+ Returns
169
+ -------
170
+ int
171
+ Recommended partition size for this network
172
+ """
173
+ import networkx as nx
174
+
175
+ n_nodes = len(graph)
176
+
177
+ # Base size from configuration or defaults
178
+ if self.max_partition_size:
179
+ base_size = self.max_partition_size
180
+ else:
181
+ # Default adaptive sizing
182
+ if n_nodes < 100:
183
+ base_size = n_nodes # Don't partition small networks
184
+ elif n_nodes < 1000:
185
+ base_size = 100
186
+ else:
187
+ base_size = 200
188
+
189
+ # Adjust based on density
190
+ density = nx.density(graph)
191
+
192
+ if density > 0.5:
193
+ # Dense networks: smaller partitions reduce communication overhead
194
+ size_multiplier = 0.5
195
+ elif density > 0.1:
196
+ # Medium density: balanced partitioning
197
+ size_multiplier = 1.0
198
+ else:
199
+ # Sparse networks: larger partitions okay
200
+ size_multiplier = 1.5
201
+
202
+ # Adjust based on clustering
203
+ try:
204
+ avg_clustering = nx.average_clustering(graph)
205
+ if avg_clustering > 0.6:
206
+ # High clustering: communities are well-defined, can use smaller partitions
207
+ size_multiplier *= 0.8
208
+ elif avg_clustering < 0.2:
209
+ # Low clustering: use larger partitions
210
+ size_multiplier *= 1.2
211
+ except:
212
+ # If clustering calculation fails, skip adjustment
213
+ pass
214
+
215
+ adapted_size = int(base_size * size_multiplier)
216
+ # Ensure reasonable bounds
217
+ return max(10, min(adapted_size, 500))
218
+
219
+ def _build_spatial_index(self, graph: TNFRGraph) -> None:
220
+ """Build KDTree spatial index for O(n log n) neighbor finding.
221
+
222
+ Constructs a 2D spatial index using (νf, phase) coordinates
223
+ to enable fast nearest-neighbor queries.
224
+ """
225
+ if not HAS_SCIPY or not HAS_NUMPY:
226
+ return
227
+
228
+ nodes = list(graph.nodes())
229
+ if len(nodes) == 0:
230
+ return
231
+
232
+ # Extract νf and phase coordinates
233
+ def _get_node_attr(
234
+ node_id: Any, alias: tuple, fallback_key: str, default: float
235
+ ) -> float:
236
+ """Get node attribute via TNFR alias or direct access."""
237
+ return float(
238
+ get_attr(graph.nodes[node_id], alias, None)
239
+ or graph.nodes[node_id].get(fallback_key, default)
240
+ )
241
+
242
+ coords = np.array(
243
+ [
244
+ [
245
+ _get_node_attr(node, ALIAS_VF, "vf", 1.0),
246
+ _get_node_attr(node, ALIAS_THETA, "phase", 0.0),
247
+ ]
248
+ for node in nodes
249
+ ]
250
+ )
251
+
252
+ # Normalize coordinates for better distance metrics
253
+ # νf: normalize by mean
254
+ if coords[:, 0].std() > 0:
255
+ coords[:, 0] = (coords[:, 0] - coords[:, 0].mean()) / coords[:, 0].std()
256
+
257
+ # phase: wrap to [-π, π] for periodicity
258
+ coords[:, 1] = np.arctan2(np.sin(coords[:, 1]), np.cos(coords[:, 1]))
259
+
260
+ # Build KDTree
261
+ self._kdtree = KDTree(coords)
262
+ self._node_index_map = {i: node for i, node in enumerate(nodes)}
263
+
264
+ def _find_coherent_neighbors_spatial(
265
+ self, graph: TNFRGraph, seed: Any, available: Set[Any], k: int = 20
266
+ ) -> List[Any]:
267
+ """Find k nearest coherent neighbors using spatial index.
268
+
269
+ Uses KDTree for O(log n) nearest neighbor finding instead of O(n).
270
+
271
+ Parameters
272
+ ----------
273
+ graph : TNFRGraph
274
+ Network graph
275
+ seed : Any
276
+ Seed node
277
+ available : Set[Any]
278
+ Available nodes to consider
279
+ k : int
280
+ Number of nearest neighbors to find
281
+
282
+ Returns
283
+ -------
284
+ List[Any]
285
+ List of up to k nearest coherent neighbors
286
+ """
287
+ if self._kdtree is None or self._node_index_map is None:
288
+ # Fallback to graph neighbors
289
+ return list(set(graph.neighbors(seed)) & available)
290
+
291
+ # Find seed index
292
+ seed_idx = None
293
+ for idx, node in self._node_index_map.items():
294
+ if node == seed:
295
+ seed_idx = idx
296
+ break
297
+
298
+ if seed_idx is None:
299
+ return []
300
+
301
+ # Query k nearest neighbors (k+1 to exclude seed itself)
302
+ distances, indices = self._kdtree.query(
303
+ self._kdtree.data[seed_idx], k=min(k + 1, len(self._node_index_map))
304
+ )
305
+
306
+ # Filter to available nodes and exclude seed
307
+ neighbors = []
308
+ for idx in indices:
309
+ if idx == seed_idx:
310
+ continue
311
+ node = self._node_index_map[idx]
312
+ if node in available:
313
+ neighbors.append(node)
314
+
315
+ return neighbors
316
+
317
+ def _detect_tnfr_communities(self, graph: TNFRGraph) -> List[Set[Any]]:
318
+ """Detect communities using TNFR coherence metrics.
319
+
320
+ Uses structural frequency and phase to grow coherent communities rather
321
+ than classical modularity or betweenness metrics.
322
+ """
323
+ communities = []
324
+ unprocessed = set(graph.nodes())
325
+
326
+ while unprocessed:
327
+ # Select seed node
328
+ seed = next(iter(unprocessed))
329
+ community = self._grow_coherent_community(graph, seed, unprocessed)
330
+ communities.append(community)
331
+ unprocessed -= community
332
+
333
+ return communities
334
+
335
+ def _grow_coherent_community(
336
+ self, graph: TNFRGraph, seed: Any, available: Set[Any]
337
+ ) -> Set[Any]:
338
+ """Grow community from seed based on structural coherence.
339
+
340
+ Parameters
341
+ ----------
342
+ graph : TNFRGraph
343
+ Full network graph
344
+ seed : Any
345
+ Starting node for community growth
346
+ available : Set[Any]
347
+ Nodes that haven't been assigned to communities yet
348
+
349
+ Returns
350
+ -------
351
+ Set[Any]
352
+ Set of nodes forming a coherent community
353
+
354
+ Notes
355
+ -----
356
+ Uses spatial indexing for O(log n) neighbor finding when available,
357
+ falling back to O(n) graph neighbors otherwise.
358
+ """
359
+ community = {seed}
360
+
361
+ # Use spatial index if available for faster neighbor finding
362
+ if self.use_spatial_index and self._kdtree is not None:
363
+ candidates = set(
364
+ self._find_coherent_neighbors_spatial(graph, seed, available, k=50)
365
+ )
366
+ else:
367
+ neighbors = graph.neighbors(seed)
368
+ candidates = set(neighbors) & available
369
+
370
+ while candidates:
371
+ # Find most coherent candidate
372
+ best_candidate = None
373
+ best_coherence = -1.0
374
+
375
+ for candidate in candidates:
376
+ coherence = self._compute_community_coherence(
377
+ graph, community, candidate
378
+ )
379
+ if coherence > best_coherence:
380
+ best_coherence = coherence
381
+ best_candidate = candidate
382
+
383
+ # Add if above threshold
384
+ if best_coherence > self.coherence_threshold:
385
+ community.add(best_candidate)
386
+ candidates.remove(best_candidate)
387
+
388
+ # Add new neighbors as candidates
389
+ if self.use_spatial_index and self._kdtree is not None:
390
+ new_neighbors = set(
391
+ self._find_coherent_neighbors_spatial(
392
+ graph, best_candidate, available, k=50
393
+ )
394
+ )
395
+ else:
396
+ new_neighbors = set(graph.neighbors(best_candidate)) & available
397
+
398
+ candidates.update(new_neighbors - community)
399
+ else:
400
+ break # No more coherent candidates
401
+
402
+ return community
403
+
404
+ def _compute_community_coherence(
405
+ self, graph: TNFRGraph, community: Set[Any], candidate: Any
406
+ ) -> float:
407
+ """Compute coherence between candidate and existing community.
408
+
409
+ Uses TNFR metrics: frequency alignment (νf) and phase synchrony.
410
+
411
+ Parameters
412
+ ----------
413
+ graph : TNFRGraph
414
+ Network graph
415
+ community : Set[Any]
416
+ Existing community nodes
417
+ candidate : Any
418
+ Candidate node to evaluate
419
+
420
+ Returns
421
+ -------
422
+ float
423
+ Coherence score in [0, 1], where higher means better alignment
424
+ """
425
+ if not community:
426
+ return 0.0
427
+
428
+ def _get_node_attr(
429
+ node_id: Any, alias: tuple, fallback_key: str, default: float
430
+ ) -> float:
431
+ """Get node attribute via TNFR alias or direct access."""
432
+ return float(
433
+ get_attr(graph.nodes[node_id], alias, None)
434
+ or graph.nodes[node_id].get(fallback_key, default)
435
+ )
436
+
437
+ candidate_vf = _get_node_attr(candidate, ALIAS_VF, "vf", 1.0)
438
+ candidate_phase = _get_node_attr(candidate, ALIAS_THETA, "phase", 0.0)
439
+
440
+ coherences = []
441
+ for member in community:
442
+ member_vf = _get_node_attr(member, ALIAS_VF, "vf", 1.0)
443
+ member_phase = _get_node_attr(member, ALIAS_THETA, "phase", 0.0)
444
+
445
+ # Frequency coherence: inversely proportional to difference
446
+ vf_diff = abs(candidate_vf - member_vf)
447
+ vf_coherence = 1.0 / (1.0 + vf_diff)
448
+
449
+ # Phase coherence: cosine of phase difference
450
+ phase_diff = candidate_phase - member_phase
451
+ if HAS_NUMPY:
452
+ phase_coherence = float(np.cos(phase_diff))
453
+ else:
454
+ phase_coherence = math.cos(phase_diff)
455
+
456
+ # Weighted combination: prioritize frequency alignment
457
+ coherences.append(0.6 * vf_coherence + 0.4 * phase_coherence)
458
+
459
+ return sum(coherences) / len(coherences) if coherences else 0.0
tnfr/py.typed ADDED
File without changes
@@ -0,0 +1,22 @@
1
+ """TNFR Pattern Cookbook - Validated recipes organized by domain.
2
+
3
+ This module provides programmatic access to the TNFR Pattern Cookbook,
4
+ a comprehensive library of pre-validated operator sequences with health
5
+ metrics, use cases, and domain-specific context.
6
+
7
+ Examples
8
+ --------
9
+ >>> from tnfr.recipes import TNFRCookbook
10
+ >>> cookbook = TNFRCookbook()
11
+ >>> recipe = cookbook.get_recipe("therapeutic", "crisis_intervention")
12
+ >>> print(f"{recipe.name}: Health {recipe.health_metrics.overall_health:.3f}")
13
+ Crisis Intervention: Health 0.786
14
+ """
15
+
16
+ from .cookbook import TNFRCookbook, CookbookRecipe, RecipeVariation
17
+
18
+ __all__ = [
19
+ "TNFRCookbook",
20
+ "CookbookRecipe",
21
+ "RecipeVariation",
22
+ ]