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,326 @@
1
+ """Network propagation dynamics for OZ-induced dissonance.
2
+
3
+ This module implements propagation of dissonance across network neighbors
4
+ following TNFR resonance principles. When OZ (Dissonance) is applied to a node,
5
+ structural dissonance propagates through the network based on phase compatibility,
6
+ frequency matching, and coupling strength.
7
+
8
+ According to TNFR canonical theory:
9
+ "Nodal interference: Dissonance between nodes that disrupts coherence.
10
+ Can induce reorganization or collapse."
11
+
12
+ OZ introduces topological asymmetry that propagates beyond the local node,
13
+ potentially triggering bifurcation cascades in phase-compatible neighbors.
14
+
15
+ References
16
+ ----------
17
+ - TNFR.pdf §2.3.3: OZ introduces topological dissonance
18
+ - Issue: [OZ] Implement dissonance propagation and neighborhood network effects
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import math
24
+ from typing import TYPE_CHECKING, Any
25
+
26
+ if TYPE_CHECKING:
27
+ from ..types import NodeId, TNFRGraph
28
+
29
+ from ..alias import get_attr
30
+ from ..constants.aliases import ALIAS_DNFR, ALIAS_THETA, ALIAS_VF
31
+
32
+ __all__ = [
33
+ "propagate_dissonance",
34
+ "compute_network_dissonance_field",
35
+ "detect_bifurcation_cascade",
36
+ ]
37
+
38
+
39
+ def propagate_dissonance(
40
+ G: TNFRGraph,
41
+ source_node: NodeId,
42
+ dissonance_magnitude: float,
43
+ propagation_mode: str = "phase_weighted",
44
+ ) -> set[NodeId]:
45
+ """Propagate OZ-induced dissonance to phase-compatible neighbors.
46
+
47
+ When OZ is applied to a node, structural dissonance propagates through
48
+ the network following TNFR resonance principles:
49
+
50
+ 1. **Phase compatibility**: Neighbors with |Δθ| < threshold receive more
51
+ 2. **Frequency matching**: Higher νf neighbors respond more strongly
52
+ 3. **Coupling strength**: Edge weights modulate propagation
53
+ 4. **Distance decay**: Effect diminishes with topological distance
54
+
55
+ Parameters
56
+ ----------
57
+ G : TNFRGraph
58
+ Network containing nodes
59
+ source_node : NodeId
60
+ Node where OZ was applied
61
+ dissonance_magnitude : float
62
+ |ΔNFR| increase at source (typically from OZ metrics)
63
+ propagation_mode : str
64
+ 'phase_weighted' (default), 'uniform', 'frequency_weighted'
65
+
66
+ Returns
67
+ -------
68
+ set[NodeId]
69
+ Set of affected neighbor nodes
70
+
71
+ Notes
72
+ -----
73
+ Propagation follows coupling physics:
74
+
75
+ ΔNFR_neighbor = ΔNFR_source * w_coupling * w_phase * w_frequency
76
+
77
+ Where:
78
+ - w_coupling: Edge weight (default 1.0)
79
+ - w_phase: Phase compatibility factor
80
+ - w_frequency: Frequency matching factor
81
+
82
+ Examples
83
+ --------
84
+ >>> from tnfr.structural import create_nfr
85
+ >>> from tnfr.operators.definitions import Emission, Dissonance
86
+ >>> from tnfr.dynamics.propagation import propagate_dissonance
87
+ >>>
88
+ >>> G, node0 = create_nfr("source", epi=0.5, vf=1.0)
89
+ >>> # Add neighbors
90
+ >>> for i in range(3):
91
+ ... G.add_node(f"n{i}")
92
+ ... G.add_edge(node0, f"n{i}")
93
+ ... Emission()(G, f"n{i}")
94
+ >>>
95
+ >>> # Apply OZ and propagate
96
+ >>> Dissonance()(G, node0)
97
+ >>> affected = propagate_dissonance(G, node0, 0.15)
98
+ >>> print(f"Affected neighbors: {len(affected)}")
99
+
100
+ See Also
101
+ --------
102
+ compute_network_dissonance_field : Compute field with distance decay
103
+ detect_bifurcation_cascade : Detect cascade-triggered bifurcations
104
+ """
105
+ neighbors = list(G.neighbors(source_node))
106
+ if not neighbors:
107
+ return set()
108
+
109
+ affected = set()
110
+ source_theta = float(get_attr(G.nodes[source_node], ALIAS_THETA, 0.0))
111
+ source_vf = float(get_attr(G.nodes[source_node], ALIAS_VF, 1.0))
112
+
113
+ # Propagation threshold (configurable)
114
+ phase_threshold = float(G.graph.get("OZ_PHASE_THRESHOLD", math.pi / 2))
115
+ min_propagation = float(G.graph.get("OZ_MIN_PROPAGATION", 0.05))
116
+
117
+ for neighbor in neighbors:
118
+ neighbor_theta = float(get_attr(G.nodes[neighbor], ALIAS_THETA, 0.0))
119
+ neighbor_vf = float(get_attr(G.nodes[neighbor], ALIAS_VF, 1.0))
120
+ neighbor_dnfr = float(get_attr(G.nodes[neighbor], ALIAS_DNFR, 0.0))
121
+
122
+ # Compute phase compatibility
123
+ delta_theta = abs(source_theta - neighbor_theta)
124
+ if delta_theta > phase_threshold:
125
+ continue # Phase incompatible, no propagation
126
+
127
+ phase_weight = 1.0 - (delta_theta / phase_threshold)
128
+
129
+ # Compute frequency matching
130
+ if propagation_mode == "frequency_weighted":
131
+ freq_ratio = min(neighbor_vf, source_vf) / max(
132
+ neighbor_vf, source_vf, 1e-10
133
+ )
134
+ freq_weight = freq_ratio
135
+ else:
136
+ freq_weight = 1.0
137
+
138
+ # Get edge weight (coupling strength)
139
+ edge_data = G.get_edge_data(source_node, neighbor)
140
+ coupling_weight = edge_data.get("weight", 1.0) if edge_data else 1.0
141
+
142
+ # Compute propagated dissonance
143
+ propagated_dnfr = (
144
+ dissonance_magnitude * coupling_weight * phase_weight * freq_weight
145
+ )
146
+
147
+ if abs(propagated_dnfr) >= min_propagation:
148
+ # Apply propagated dissonance to neighbor
149
+ new_dnfr = neighbor_dnfr + propagated_dnfr
150
+ # Use first alias for consistency
151
+ G.nodes[neighbor][ALIAS_DNFR[0]] = new_dnfr
152
+ affected.add(neighbor)
153
+
154
+ # Log propagation for telemetry
155
+ if "_oz_propagation" not in G.nodes[neighbor]:
156
+ G.nodes[neighbor]["_oz_propagation"] = []
157
+ G.nodes[neighbor]["_oz_propagation"].append(
158
+ {
159
+ "from_node": source_node,
160
+ "magnitude": propagated_dnfr,
161
+ "phase_weight": phase_weight,
162
+ "coupling_weight": coupling_weight,
163
+ }
164
+ )
165
+
166
+ return affected
167
+
168
+
169
+ def compute_network_dissonance_field(
170
+ G: TNFRGraph,
171
+ source_node: NodeId,
172
+ radius: int = 2,
173
+ ) -> dict[NodeId, float]:
174
+ """Compute dissonance field propagation up to radius hops.
175
+
176
+ Returns dict mapping node -> dissonance_level for all nodes
177
+ within radius hops of source.
178
+
179
+ Parameters
180
+ ----------
181
+ G : TNFRGraph
182
+ Network
183
+ source_node : NodeId
184
+ OZ application point
185
+ radius : int
186
+ Maximum propagation distance (default 2)
187
+
188
+ Returns
189
+ -------
190
+ dict[NodeId, float]
191
+ Mapping of affected nodes to dissonance level
192
+
193
+ Notes
194
+ -----
195
+ Uses exponential decay: dissonance_level = source_dnfr * (0.5 ** distance)
196
+
197
+ Only nodes reachable via paths (connected) are included in the field.
198
+
199
+ Examples
200
+ --------
201
+ >>> from tnfr.structural import create_nfr
202
+ >>> from tnfr.operators.definitions import Dissonance
203
+ >>> from tnfr.dynamics.propagation import compute_network_dissonance_field
204
+ >>>
205
+ >>> G, node0 = create_nfr("source")
206
+ >>> # Create path topology: 0-1-2-3
207
+ >>> for i in range(1, 4):
208
+ ... G.add_node(i)
209
+ ... G.add_edge(i-1, i)
210
+ >>>
211
+ >>> Dissonance()(G, node0)
212
+ >>> field = compute_network_dissonance_field(G, node0, radius=2)
213
+ >>> # Returns: {1: high, 2: medium} (node 3 beyond radius)
214
+ """
215
+ import networkx as nx
216
+
217
+ field = {}
218
+ source_dnfr = abs(float(get_attr(G.nodes[source_node], ALIAS_DNFR, 0.0)))
219
+
220
+ # Get decay factor from graph config
221
+ decay_factor = float(G.graph.get("OZ_DECAY_FACTOR", 0.5))
222
+
223
+ # BFS to propagate with distance decay
224
+ for distance in range(1, radius + 1):
225
+ # Get nodes at this distance
226
+ nodes_at_distance = set()
227
+ for node in G.nodes():
228
+ try:
229
+ path_length = nx.shortest_path_length(G, source_node, node)
230
+ if path_length == distance:
231
+ nodes_at_distance.add(node)
232
+ except nx.NetworkXNoPath:
233
+ continue
234
+
235
+ # Propagate with decay
236
+ decay = decay_factor**distance
237
+ for node in nodes_at_distance:
238
+ field[node] = source_dnfr * decay
239
+
240
+ return field
241
+
242
+
243
+ def detect_bifurcation_cascade(
244
+ G: TNFRGraph,
245
+ source_node: NodeId,
246
+ threshold: float = 0.5,
247
+ ) -> list[NodeId]:
248
+ """Detect if OZ triggers bifurcation cascade in network.
249
+
250
+ When source node undergoes bifurcation (∂²EPI/∂t² > τ), check if
251
+ propagated dissonance pushes neighbors over their own thresholds.
252
+
253
+ Parameters
254
+ ----------
255
+ G : TNFRGraph
256
+ Network containing nodes
257
+ source_node : NodeId
258
+ Node where OZ was applied
259
+ threshold : float
260
+ Bifurcation threshold τ (default 0.5)
261
+
262
+ Returns
263
+ -------
264
+ list[NodeId]
265
+ Nodes that entered bifurcation state due to cascade
266
+
267
+ Notes
268
+ -----
269
+ A node is considered in bifurcation cascade if:
270
+ - It received propagated dissonance from source
271
+ - Its ∂²EPI/∂t² now exceeds threshold τ
272
+
273
+ The function marks cascade nodes with `_bifurcation_cascade` metadata
274
+ for telemetry and further analysis.
275
+
276
+ Examples
277
+ --------
278
+ >>> from tnfr.structural import create_nfr
279
+ >>> from tnfr.operators.definitions import Emission, Dissonance
280
+ >>> from tnfr.dynamics.propagation import detect_bifurcation_cascade
281
+ >>>
282
+ >>> G, node0 = create_nfr("source", epi=0.5, vf=1.2)
283
+ >>> # Add neighbors with EPI history
284
+ >>> for i in range(3):
285
+ ... G.add_node(f"n{i}")
286
+ ... G.add_edge(node0, f"n{i}")
287
+ ... G.nodes[f"n{i}"]["_epi_history"] = [0.3, 0.45, 0.55]
288
+ >>>
289
+ >>> Dissonance()(G, node0, propagate_to_network=True)
290
+ >>> cascade = detect_bifurcation_cascade(G, node0)
291
+ >>> print(f"Cascade size: {len(cascade)}")
292
+
293
+ See Also
294
+ --------
295
+ tnfr.operators.nodal_equation.compute_d2epi_dt2 : Compute structural acceleration
296
+ tnfr.dynamics.bifurcation.get_bifurcation_paths : Identify viable paths
297
+ """
298
+ from ..operators.nodal_equation import compute_d2epi_dt2
299
+
300
+ cascade_nodes = []
301
+
302
+ # Get neighbors affected by propagation
303
+ neighbors = list(G.neighbors(source_node))
304
+
305
+ for neighbor in neighbors:
306
+ # Check if neighbor has propagation record (was affected)
307
+ if "_oz_propagation" not in G.nodes[neighbor]:
308
+ continue
309
+
310
+ # Check if neighbor now in bifurcation state
311
+ d2epi_neighbor = compute_d2epi_dt2(G, neighbor)
312
+
313
+ if abs(d2epi_neighbor) > threshold:
314
+ cascade_nodes.append(neighbor)
315
+
316
+ # Mark for telemetry
317
+ G.nodes[neighbor]["_bifurcation_cascade"] = {
318
+ "triggered_by": source_node,
319
+ "d2epi": d2epi_neighbor,
320
+ "threshold": threshold,
321
+ }
322
+
323
+ # Set bifurcation_ready flag for path detection
324
+ G.nodes[neighbor]["_bifurcation_ready"] = True
325
+
326
+ return cascade_nodes