tnfr 3.0.3__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 (360) hide show
  1. tnfr/__init__.py +375 -56
  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 +723 -0
  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 +171 -0
  15. tnfr/cache.pyi +13 -0
  16. tnfr/cli/__init__.py +110 -0
  17. tnfr/cli/__init__.pyi +26 -0
  18. tnfr/cli/arguments.py +489 -0
  19. tnfr/cli/arguments.pyi +29 -0
  20. tnfr/cli/execution.py +914 -0
  21. tnfr/cli/execution.pyi +70 -0
  22. tnfr/cli/interactive_validator.py +614 -0
  23. tnfr/cli/utils.py +51 -0
  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/config/constants.py +104 -0
  34. tnfr/config/constants.pyi +12 -0
  35. tnfr/config/defaults.py +54 -0
  36. tnfr/config/defaults_core.py +212 -0
  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 +92 -0
  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 +33 -0
  57. tnfr/constants/init.pyi +12 -0
  58. tnfr/constants/metric.py +104 -0
  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 +238 -0
  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 +3034 -0
  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 +661 -0
  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 +36 -0
  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 +223 -0
  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 +262 -0
  125. tnfr/flatten.pyi +21 -0
  126. tnfr/gamma.py +354 -0
  127. tnfr/gamma.pyi +36 -0
  128. tnfr/glyph_history.py +377 -0
  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 +218 -0
  133. tnfr/immutable.pyi +36 -0
  134. tnfr/initialization.py +203 -0
  135. tnfr/initialization.pyi +65 -0
  136. tnfr/io.py +10 -0
  137. tnfr/io.pyi +13 -0
  138. tnfr/locking.py +37 -0
  139. tnfr/locking.pyi +7 -0
  140. tnfr/mathematics/__init__.py +79 -0
  141. tnfr/mathematics/backend.py +453 -0
  142. tnfr/mathematics/backend.pyi +99 -0
  143. tnfr/mathematics/dynamics.py +408 -0
  144. tnfr/mathematics/dynamics.pyi +90 -0
  145. tnfr/mathematics/epi.py +391 -0
  146. tnfr/mathematics/epi.pyi +65 -0
  147. tnfr/mathematics/generators.py +242 -0
  148. tnfr/mathematics/generators.pyi +29 -0
  149. tnfr/mathematics/metrics.py +119 -0
  150. tnfr/mathematics/metrics.pyi +16 -0
  151. tnfr/mathematics/operators.py +239 -0
  152. tnfr/mathematics/operators.pyi +59 -0
  153. tnfr/mathematics/operators_factory.py +124 -0
  154. tnfr/mathematics/operators_factory.pyi +11 -0
  155. tnfr/mathematics/projection.py +87 -0
  156. tnfr/mathematics/projection.pyi +33 -0
  157. tnfr/mathematics/runtime.py +182 -0
  158. tnfr/mathematics/runtime.pyi +64 -0
  159. tnfr/mathematics/spaces.py +256 -0
  160. tnfr/mathematics/spaces.pyi +83 -0
  161. tnfr/mathematics/transforms.py +305 -0
  162. tnfr/mathematics/transforms.pyi +62 -0
  163. tnfr/metrics/__init__.py +79 -0
  164. tnfr/metrics/__init__.pyi +20 -0
  165. tnfr/metrics/buffer_cache.py +163 -0
  166. tnfr/metrics/buffer_cache.pyi +24 -0
  167. tnfr/metrics/cache_utils.py +214 -0
  168. tnfr/metrics/coherence.py +2009 -0
  169. tnfr/metrics/coherence.pyi +129 -0
  170. tnfr/metrics/common.py +158 -0
  171. tnfr/metrics/common.pyi +35 -0
  172. tnfr/metrics/core.py +316 -0
  173. tnfr/metrics/core.pyi +13 -0
  174. tnfr/metrics/diagnosis.py +833 -0
  175. tnfr/metrics/diagnosis.pyi +86 -0
  176. tnfr/metrics/emergence.py +245 -0
  177. tnfr/metrics/export.py +179 -0
  178. tnfr/metrics/export.pyi +7 -0
  179. tnfr/metrics/glyph_timing.py +379 -0
  180. tnfr/metrics/glyph_timing.pyi +81 -0
  181. tnfr/metrics/learning_metrics.py +280 -0
  182. tnfr/metrics/learning_metrics.pyi +21 -0
  183. tnfr/metrics/phase_coherence.py +351 -0
  184. tnfr/metrics/phase_compatibility.py +349 -0
  185. tnfr/metrics/reporting.py +183 -0
  186. tnfr/metrics/reporting.pyi +25 -0
  187. tnfr/metrics/sense_index.py +1203 -0
  188. tnfr/metrics/sense_index.pyi +9 -0
  189. tnfr/metrics/trig.py +373 -0
  190. tnfr/metrics/trig.pyi +13 -0
  191. tnfr/metrics/trig_cache.py +233 -0
  192. tnfr/metrics/trig_cache.pyi +10 -0
  193. tnfr/multiscale/__init__.py +32 -0
  194. tnfr/multiscale/hierarchical.py +517 -0
  195. tnfr/node.py +763 -0
  196. tnfr/node.pyi +139 -0
  197. tnfr/observers.py +255 -130
  198. tnfr/observers.pyi +31 -0
  199. tnfr/ontosim.py +144 -137
  200. tnfr/ontosim.pyi +28 -0
  201. tnfr/operators/__init__.py +1672 -0
  202. tnfr/operators/__init__.pyi +31 -0
  203. tnfr/operators/algebra.py +277 -0
  204. tnfr/operators/canonical_patterns.py +420 -0
  205. tnfr/operators/cascade.py +267 -0
  206. tnfr/operators/cycle_detection.py +358 -0
  207. tnfr/operators/definitions.py +4108 -0
  208. tnfr/operators/definitions.pyi +78 -0
  209. tnfr/operators/grammar.py +1164 -0
  210. tnfr/operators/grammar.pyi +140 -0
  211. tnfr/operators/hamiltonian.py +710 -0
  212. tnfr/operators/health_analyzer.py +809 -0
  213. tnfr/operators/jitter.py +272 -0
  214. tnfr/operators/jitter.pyi +11 -0
  215. tnfr/operators/lifecycle.py +314 -0
  216. tnfr/operators/metabolism.py +618 -0
  217. tnfr/operators/metrics.py +2138 -0
  218. tnfr/operators/network_analysis/__init__.py +27 -0
  219. tnfr/operators/network_analysis/source_detection.py +186 -0
  220. tnfr/operators/nodal_equation.py +395 -0
  221. tnfr/operators/pattern_detection.py +660 -0
  222. tnfr/operators/patterns.py +669 -0
  223. tnfr/operators/postconditions/__init__.py +38 -0
  224. tnfr/operators/postconditions/mutation.py +236 -0
  225. tnfr/operators/preconditions/__init__.py +1226 -0
  226. tnfr/operators/preconditions/coherence.py +305 -0
  227. tnfr/operators/preconditions/dissonance.py +236 -0
  228. tnfr/operators/preconditions/emission.py +128 -0
  229. tnfr/operators/preconditions/mutation.py +580 -0
  230. tnfr/operators/preconditions/reception.py +125 -0
  231. tnfr/operators/preconditions/resonance.py +364 -0
  232. tnfr/operators/registry.py +74 -0
  233. tnfr/operators/registry.pyi +9 -0
  234. tnfr/operators/remesh.py +1809 -0
  235. tnfr/operators/remesh.pyi +26 -0
  236. tnfr/operators/structural_units.py +268 -0
  237. tnfr/operators/unified_grammar.py +105 -0
  238. tnfr/parallel/__init__.py +54 -0
  239. tnfr/parallel/auto_scaler.py +234 -0
  240. tnfr/parallel/distributed.py +384 -0
  241. tnfr/parallel/engine.py +238 -0
  242. tnfr/parallel/gpu_engine.py +420 -0
  243. tnfr/parallel/monitoring.py +248 -0
  244. tnfr/parallel/partitioner.py +459 -0
  245. tnfr/py.typed +0 -0
  246. tnfr/recipes/__init__.py +22 -0
  247. tnfr/recipes/cookbook.py +743 -0
  248. tnfr/rng.py +178 -0
  249. tnfr/rng.pyi +26 -0
  250. tnfr/schemas/__init__.py +8 -0
  251. tnfr/schemas/grammar.json +94 -0
  252. tnfr/sdk/__init__.py +107 -0
  253. tnfr/sdk/__init__.pyi +19 -0
  254. tnfr/sdk/adaptive_system.py +173 -0
  255. tnfr/sdk/adaptive_system.pyi +21 -0
  256. tnfr/sdk/builders.py +370 -0
  257. tnfr/sdk/builders.pyi +51 -0
  258. tnfr/sdk/fluent.py +1121 -0
  259. tnfr/sdk/fluent.pyi +74 -0
  260. tnfr/sdk/templates.py +342 -0
  261. tnfr/sdk/templates.pyi +41 -0
  262. tnfr/sdk/utils.py +341 -0
  263. tnfr/secure_config.py +46 -0
  264. tnfr/security/__init__.py +70 -0
  265. tnfr/security/database.py +514 -0
  266. tnfr/security/subprocess.py +503 -0
  267. tnfr/security/validation.py +290 -0
  268. tnfr/selector.py +247 -0
  269. tnfr/selector.pyi +19 -0
  270. tnfr/sense.py +378 -0
  271. tnfr/sense.pyi +23 -0
  272. tnfr/services/__init__.py +17 -0
  273. tnfr/services/orchestrator.py +325 -0
  274. tnfr/sparse/__init__.py +39 -0
  275. tnfr/sparse/representations.py +492 -0
  276. tnfr/structural.py +705 -0
  277. tnfr/structural.pyi +83 -0
  278. tnfr/telemetry/__init__.py +35 -0
  279. tnfr/telemetry/cache_metrics.py +226 -0
  280. tnfr/telemetry/cache_metrics.pyi +64 -0
  281. tnfr/telemetry/nu_f.py +422 -0
  282. tnfr/telemetry/nu_f.pyi +108 -0
  283. tnfr/telemetry/verbosity.py +36 -0
  284. tnfr/telemetry/verbosity.pyi +15 -0
  285. tnfr/tokens.py +58 -0
  286. tnfr/tokens.pyi +36 -0
  287. tnfr/tools/__init__.py +20 -0
  288. tnfr/tools/domain_templates.py +478 -0
  289. tnfr/tools/sequence_generator.py +846 -0
  290. tnfr/topology/__init__.py +13 -0
  291. tnfr/topology/asymmetry.py +151 -0
  292. tnfr/trace.py +543 -0
  293. tnfr/trace.pyi +42 -0
  294. tnfr/tutorials/__init__.py +38 -0
  295. tnfr/tutorials/autonomous_evolution.py +285 -0
  296. tnfr/tutorials/interactive.py +1576 -0
  297. tnfr/tutorials/structural_metabolism.py +238 -0
  298. tnfr/types.py +775 -0
  299. tnfr/types.pyi +357 -0
  300. tnfr/units.py +68 -0
  301. tnfr/units.pyi +13 -0
  302. tnfr/utils/__init__.py +282 -0
  303. tnfr/utils/__init__.pyi +215 -0
  304. tnfr/utils/cache.py +4223 -0
  305. tnfr/utils/cache.pyi +470 -0
  306. tnfr/utils/callbacks.py +375 -0
  307. tnfr/utils/callbacks.pyi +49 -0
  308. tnfr/utils/chunks.py +108 -0
  309. tnfr/utils/chunks.pyi +22 -0
  310. tnfr/utils/data.py +428 -0
  311. tnfr/utils/data.pyi +74 -0
  312. tnfr/utils/graph.py +85 -0
  313. tnfr/utils/graph.pyi +10 -0
  314. tnfr/utils/init.py +821 -0
  315. tnfr/utils/init.pyi +80 -0
  316. tnfr/utils/io.py +559 -0
  317. tnfr/utils/io.pyi +66 -0
  318. tnfr/utils/numeric.py +114 -0
  319. tnfr/utils/numeric.pyi +21 -0
  320. tnfr/validation/__init__.py +257 -0
  321. tnfr/validation/__init__.pyi +85 -0
  322. tnfr/validation/compatibility.py +460 -0
  323. tnfr/validation/compatibility.pyi +6 -0
  324. tnfr/validation/config.py +73 -0
  325. tnfr/validation/graph.py +139 -0
  326. tnfr/validation/graph.pyi +18 -0
  327. tnfr/validation/input_validation.py +755 -0
  328. tnfr/validation/invariants.py +712 -0
  329. tnfr/validation/rules.py +253 -0
  330. tnfr/validation/rules.pyi +44 -0
  331. tnfr/validation/runtime.py +279 -0
  332. tnfr/validation/runtime.pyi +28 -0
  333. tnfr/validation/sequence_validator.py +162 -0
  334. tnfr/validation/soft_filters.py +170 -0
  335. tnfr/validation/soft_filters.pyi +32 -0
  336. tnfr/validation/spectral.py +164 -0
  337. tnfr/validation/spectral.pyi +42 -0
  338. tnfr/validation/validator.py +1266 -0
  339. tnfr/validation/window.py +39 -0
  340. tnfr/validation/window.pyi +1 -0
  341. tnfr/visualization/__init__.py +98 -0
  342. tnfr/visualization/cascade_viz.py +256 -0
  343. tnfr/visualization/hierarchy.py +284 -0
  344. tnfr/visualization/sequence_plotter.py +784 -0
  345. tnfr/viz/__init__.py +60 -0
  346. tnfr/viz/matplotlib.py +278 -0
  347. tnfr/viz/matplotlib.pyi +35 -0
  348. tnfr-8.5.0.dist-info/METADATA +573 -0
  349. tnfr-8.5.0.dist-info/RECORD +353 -0
  350. tnfr-8.5.0.dist-info/entry_points.txt +3 -0
  351. tnfr-3.0.3.dist-info/licenses/LICENSE.txt → tnfr-8.5.0.dist-info/licenses/LICENSE.md +1 -1
  352. tnfr/constants.py +0 -183
  353. tnfr/dynamics.py +0 -543
  354. tnfr/helpers.py +0 -198
  355. tnfr/main.py +0 -37
  356. tnfr/operators.py +0 -296
  357. tnfr-3.0.3.dist-info/METADATA +0 -35
  358. tnfr-3.0.3.dist-info/RECORD +0 -13
  359. {tnfr-3.0.3.dist-info → tnfr-8.5.0.dist-info}/WHEEL +0 -0
  360. {tnfr-3.0.3.dist-info → tnfr-8.5.0.dist-info}/top_level.txt +0 -0
tnfr/node.pyi ADDED
@@ -0,0 +1,139 @@
1
+ from __future__ import annotations
2
+
3
+ import numpy as np
4
+ from .mathematics import (
5
+ CoherenceOperator,
6
+ FrequencyOperator,
7
+ HilbertSpace,
8
+ NFRValidator,
9
+ StateProjector,
10
+ )
11
+ from .types import (
12
+ CouplingWeight,
13
+ DeltaNFR,
14
+ EPIValue,
15
+ NodeId,
16
+ Phase,
17
+ SecondDerivativeEPI,
18
+ SenseIndex,
19
+ StructuralFrequency,
20
+ TNFRGraph,
21
+ )
22
+ from collections.abc import Hashable
23
+ from dataclasses import dataclass
24
+ from typing import (
25
+ Any,
26
+ Callable,
27
+ Iterable,
28
+ MutableMapping,
29
+ Protocol,
30
+ Sequence,
31
+ SupportsFloat,
32
+ TypeVar,
33
+ )
34
+
35
+ __all__ = ["NodeNX", "NodeProtocol", "add_edge"]
36
+
37
+ T = TypeVar("T")
38
+
39
+ @dataclass(frozen=True)
40
+ class AttrSpec:
41
+ aliases: tuple[str, ...]
42
+ default: Any = ...
43
+ getter: Callable[[MutableMapping[str, Any], tuple[str, ...], Any], Any] = ...
44
+ setter: Callable[..., None] = ...
45
+ to_python: Callable[[Any], Any] = ...
46
+ to_storage: Callable[[Any], Any] = ...
47
+ use_graph_setter: bool = ...
48
+ def build_property(self) -> property: ...
49
+
50
+ def add_edge(
51
+ graph: TNFRGraph,
52
+ n1: NodeId,
53
+ n2: NodeId,
54
+ weight: CouplingWeight | SupportsFloat | str,
55
+ overwrite: bool = False,
56
+ ) -> None: ...
57
+
58
+ class NodeProtocol(Protocol):
59
+ EPI: EPIValue
60
+ vf: StructuralFrequency
61
+ theta: Phase
62
+ Si: SenseIndex
63
+ epi_kind: str
64
+ dnfr: DeltaNFR
65
+ d2EPI: SecondDerivativeEPI
66
+ graph: MutableMapping[str, Any]
67
+ def neighbors(self) -> Iterable[NodeProtocol | Hashable]: ...
68
+ def has_edge(self, other: NodeProtocol) -> bool: ...
69
+ def add_edge(
70
+ self, other: NodeProtocol, weight: CouplingWeight, *, overwrite: bool = False
71
+ ) -> None: ...
72
+ def offset(self) -> int: ...
73
+ def all_nodes(self) -> Iterable[NodeProtocol]: ...
74
+
75
+ class NodeNX(NodeProtocol):
76
+ EPI: EPIValue
77
+ vf: StructuralFrequency
78
+ theta: Phase
79
+ Si: SenseIndex
80
+ epi_kind: str
81
+ dnfr: DeltaNFR
82
+ d2EPI: SecondDerivativeEPI
83
+ G: TNFRGraph
84
+ n: NodeId
85
+ graph: MutableMapping[str, Any]
86
+ state_projector: StateProjector
87
+ enable_math_validation: bool
88
+ hilbert_space: HilbertSpace
89
+ coherence_operator: CoherenceOperator | None
90
+ frequency_operator: FrequencyOperator | None
91
+ coherence_threshold: float | None
92
+ validator: NFRValidator | None
93
+ rng: np.random.Generator | None
94
+ def __init__(
95
+ self,
96
+ G: TNFRGraph,
97
+ n: NodeId,
98
+ *,
99
+ state_projector: StateProjector | None = None,
100
+ enable_math_validation: bool | None = None,
101
+ hilbert_space: HilbertSpace | None = None,
102
+ coherence_operator: CoherenceOperator | None = None,
103
+ coherence_dim: int | None = None,
104
+ coherence_spectrum: Sequence[float] | np.ndarray | None = None,
105
+ coherence_c_min: float | None = None,
106
+ frequency_operator: FrequencyOperator | None = None,
107
+ frequency_matrix: Sequence[Sequence[complex]] | np.ndarray | None = None,
108
+ coherence_threshold: float | None = None,
109
+ validator: NFRValidator | None = None,
110
+ rng: np.random.Generator | None = None,
111
+ ) -> None: ...
112
+ @classmethod
113
+ def from_graph(cls, G: TNFRGraph, n: NodeId) -> NodeNX: ...
114
+ def neighbors(self) -> Iterable[NodeId]: ...
115
+ def has_edge(self, other: NodeProtocol) -> bool: ...
116
+ def add_edge(
117
+ self, other: NodeProtocol, weight: CouplingWeight, *, overwrite: bool = False
118
+ ) -> None: ...
119
+ def offset(self) -> int: ...
120
+ def all_nodes(self) -> Iterable[NodeProtocol]: ...
121
+ def run_sequence_with_validation(
122
+ self,
123
+ ops: Iterable[Callable[[TNFRGraph, NodeId], None]],
124
+ *,
125
+ projector: StateProjector | None = None,
126
+ hilbert_space: HilbertSpace | None = None,
127
+ coherence_operator: CoherenceOperator | None = None,
128
+ coherence_dim: int | None = None,
129
+ coherence_spectrum: Sequence[float] | np.ndarray | None = None,
130
+ coherence_c_min: float | None = None,
131
+ coherence_threshold: float | None = None,
132
+ frequency_operator: FrequencyOperator | None = None,
133
+ frequency_matrix: Sequence[Sequence[complex]] | np.ndarray | None = None,
134
+ validator: NFRValidator | None = None,
135
+ enforce_frequency_positivity: bool | None = None,
136
+ enable_validation: bool | None = None,
137
+ rng: np.random.Generator | None = None,
138
+ log_metrics: bool = False,
139
+ ) -> dict[str, Any]: ...
tnfr/observers.py CHANGED
@@ -1,149 +1,274 @@
1
- """
2
- observers.py — TNFR canónica
1
+ """Observer management."""
3
2
 
4
- Observadores y métricas auxiliares.
5
- """
6
3
  from __future__ import annotations
7
- from collections import Counter
8
- from typing import Dict, Any
9
- import math
10
4
 
11
- from constants import ALIAS_DNFR, ALIAS_EPI, ALIAS_THETA, ALIAS_dEPI
12
- from helpers import _get_attr, list_mean, register_callback
5
+ import statistics
6
+ from collections.abc import Mapping
7
+ from functools import partial
8
+ from statistics import StatisticsError, pvariance
9
+
10
+ from .alias import get_theta_attr
11
+ from .utils import CallbackEvent, callback_manager
12
+ from .config.constants import GLYPH_GROUPS
13
+ from .gamma import kuramoto_R_psi
14
+ from .glyph_history import (
15
+ append_metric,
16
+ count_glyphs,
17
+ ensure_history,
18
+ )
19
+ from .utils import angle_diff
20
+ from .metrics.common import compute_coherence
21
+ from .types import Glyph, GlyphLoadDistribution, TNFRGraph
22
+ from .utils import (
23
+ get_logger,
24
+ get_numpy,
25
+ mix_groups,
26
+ normalize_counter,
27
+ )
28
+ from .validation import validate_window
29
+ from .telemetry import ensure_nu_f_telemetry, record_nu_f_window
30
+
31
+ __all__ = (
32
+ "attach_standard_observer",
33
+ "kuramoto_metrics",
34
+ "phase_sync",
35
+ "kuramoto_order",
36
+ "glyph_load",
37
+ "wbar",
38
+ "DEFAULT_GLYPH_LOAD_SPAN",
39
+ "DEFAULT_WBAR_SPAN",
40
+ )
41
+
42
+ logger = get_logger(__name__)
43
+
44
+ DEFAULT_GLYPH_LOAD_SPAN = 50
45
+ DEFAULT_WBAR_SPAN = 25
46
+
13
47
 
14
48
  # -------------------------
15
- # Observador estándar Γ(R)
49
+ # Standard Γ(R) observer
16
50
  # -------------------------
17
- def _std_log(G, kind: str, ctx: dict):
18
- """Guarda eventos compactos en history['events']."""
19
- h = G.graph.setdefault("history", {})
20
- h.setdefault("events", []).append((kind, dict(ctx)))
21
-
22
- def std_before(G, ctx):
23
- _std_log(G, "before", ctx)
24
-
25
- def std_after(G, ctx):
26
- _std_log(G, "after", ctx)
27
-
28
- def std_on_remesh(G, ctx):
29
- _std_log(G, "remesh", ctx)
30
-
31
- def attach_standard_observer(G):
32
- """Registra callbacks estándar: before_step, after_step, on_remesh."""
33
- register_callback(G, "before_step", std_before)
34
- register_callback(G, "after_step", std_after)
35
- register_callback(G, "on_remesh", std_on_remesh)
36
- G.graph.setdefault("_STD_OBSERVER", "attached")
51
+ def _std_log(kind: str, G: TNFRGraph, ctx: Mapping[str, object]) -> None:
52
+ """Store compact events in ``history['events']``."""
53
+ h = ensure_history(G)
54
+ append_metric(h, "events", (kind, dict(ctx)))
55
+
56
+
57
+ _STD_CALLBACKS = {
58
+ CallbackEvent.BEFORE_STEP.value: partial(_std_log, "before"),
59
+ CallbackEvent.AFTER_STEP.value: partial(_std_log, "after"),
60
+ CallbackEvent.ON_REMESH.value: partial(_std_log, "remesh"),
61
+ CallbackEvent.CACHE_METRICS.value: partial(_std_log, "cache"),
62
+ }
63
+
64
+ _REORG_STATE_KEY = "_std_observer_reorg"
65
+
66
+
67
+ def _resolve_reorg_state(G: TNFRGraph) -> dict[str, object]:
68
+ state = G.graph.get(_REORG_STATE_KEY)
69
+ if not isinstance(state, dict):
70
+ state = {}
71
+ G.graph[_REORG_STATE_KEY] = state
72
+ return state
73
+
74
+
75
+ def _before_step_reorg(G: TNFRGraph, ctx: Mapping[str, object] | None) -> None:
76
+ """Capture structural time metadata before the step starts."""
77
+
78
+ ensure_nu_f_telemetry(G, confidence_level=None)
79
+ state = _resolve_reorg_state(G)
80
+ step_idx = ctx.get("step") if ctx else None
81
+ try:
82
+ state["step"] = int(step_idx) if step_idx is not None else None
83
+ except (TypeError, ValueError):
84
+ state["step"] = None
85
+ start_t = float(G.graph.get("_t", 0.0))
86
+ state["start_t"] = start_t
87
+ dt_raw = ctx.get("dt") if ctx else None
88
+ try:
89
+ state["dt"] = float(dt_raw) if dt_raw is not None else None
90
+ except (TypeError, ValueError):
91
+ state["dt"] = None
92
+
93
+
94
+ def _after_step_reorg(G: TNFRGraph, ctx: Mapping[str, object] | None) -> None:
95
+ """Record the reorganisation window for νf telemetry."""
96
+
97
+ state = _resolve_reorg_state(G)
98
+ pending_step = state.get("step")
99
+ ctx_step = ctx.get("step") if ctx else None
100
+ if pending_step is not None and ctx_step is not None and pending_step != ctx_step:
101
+ # Ignore mismatched callbacks to avoid double counting.
102
+ return
103
+
104
+ try:
105
+ start_t = float(state.get("start_t", float(G.graph.get("_t", 0.0))))
106
+ except (TypeError, ValueError):
107
+ start_t = float(G.graph.get("_t", 0.0))
108
+ end_t = float(G.graph.get("_t", start_t))
109
+ dt_raw = state.get("dt")
110
+ try:
111
+ duration = float(dt_raw) if dt_raw is not None else end_t - start_t
112
+ except (TypeError, ValueError):
113
+ duration = end_t - start_t
114
+ if duration <= 0.0:
115
+ duration = end_t - start_t
116
+ if duration <= 0.0:
117
+ return
118
+
119
+ stable_frac = ctx.get("stable_frac") if ctx else None
120
+ if stable_frac is None:
121
+ hist = ensure_history(G)
122
+ series = hist.get("stable_frac", [])
123
+ stable_frac = series[-1] if series else None
124
+ try:
125
+ stable_frac_f = float(stable_frac) if stable_frac is not None else None
126
+ except (TypeError, ValueError):
127
+ stable_frac_f = None
128
+ total_nodes = G.number_of_nodes()
129
+ if stable_frac_f is None:
130
+ reorganisations = total_nodes
131
+ else:
132
+ frac = min(max(stable_frac_f, 0.0), 1.0)
133
+ stable_nodes = int(round(frac * total_nodes))
134
+ reorganisations = max(total_nodes - stable_nodes, 0)
135
+
136
+ record_nu_f_window(
137
+ G,
138
+ reorganisations,
139
+ duration,
140
+ start=start_t,
141
+ end=end_t,
142
+ )
143
+ state["last_duration"] = duration
144
+ state["last_reorganisations"] = reorganisations
145
+ state["last_end_t"] = end_t
146
+ state["step"] = None
147
+
148
+
149
+ def attach_standard_observer(G: TNFRGraph) -> TNFRGraph:
150
+ """Register standard callbacks: before_step, after_step, on_remesh."""
151
+ if G.graph.get("_STD_OBSERVER"):
152
+ return G
153
+ for event, fn in _STD_CALLBACKS.items():
154
+ callback_manager.register_callback(G, event, fn)
155
+ callback_manager.register_callback(
156
+ G,
157
+ CallbackEvent.BEFORE_STEP.value,
158
+ _before_step_reorg,
159
+ name="std_reorg_before",
160
+ )
161
+ callback_manager.register_callback(
162
+ G,
163
+ CallbackEvent.AFTER_STEP.value,
164
+ _after_step_reorg,
165
+ name="std_reorg_after",
166
+ )
167
+ ensure_nu_f_telemetry(G, confidence_level=None)
168
+ G.graph["_STD_OBSERVER"] = "attached"
37
169
  return G
38
170
 
39
- def coherencia_global(G) -> float:
40
- """Proxy de C(t): alta cuando |ΔNFR| y |dEPI_dt| son pequeños."""
41
- dnfr = list_mean(abs(_get_attr(G.nodes[n], ALIAS_DNFR, 0.0)) for n in G.nodes())
42
- dEPI = list_mean(abs(_get_attr(G.nodes[n], ALIAS_dEPI, 0.0)) for n in G.nodes())
43
- return 1.0 / (1.0 + dnfr + dEPI)
44
171
 
172
+ def _ensure_nodes(G: TNFRGraph) -> bool:
173
+ """Return ``True`` when the graph has nodes."""
174
+ return bool(G.number_of_nodes())
175
+
176
+
177
+ def kuramoto_metrics(G: TNFRGraph) -> tuple[float, float]:
178
+ """Return Kuramoto order ``R`` and mean phase ``ψ``.
179
+
180
+ Delegates to :func:`kuramoto_R_psi` and performs the computation exactly
181
+ once per invocation.
182
+ """
183
+ return kuramoto_R_psi(G)
184
+
185
+
186
+ def phase_sync(
187
+ G: TNFRGraph,
188
+ R: float | None = None,
189
+ psi: float | None = None,
190
+ ) -> float:
191
+ """Return a [0, 1] synchrony index derived from phase dispersion."""
45
192
 
46
- def sincronía_fase(G) -> float:
47
- X = list(math.cos(_get_attr(G.nodes[n], ALIAS_THETA, 0.0)) for n in G.nodes())
48
- Y = list(math.sin(_get_attr(G.nodes[n], ALIAS_THETA, 0.0)) for n in G.nodes())
49
- if not X:
193
+ if not _ensure_nodes(G):
50
194
  return 1.0
51
- import math
52
- th = math.atan2(sum(Y)/len(Y), sum(X)/len(X))
53
- # varianza angular aproximada (0 = muy sincronizado)
54
- import statistics as st
55
- var = st.pvariance([((_get_attr(G.nodes[n], ALIAS_THETA, 0.0) - th + math.pi) % (2*math.pi) - math.pi) for n in G.nodes()]) if len(X) > 1 else 0.0
195
+ if psi is None:
196
+ _, psi = kuramoto_metrics(G)
197
+
198
+ def _theta(nd: Mapping[str, object]) -> float:
199
+ value = get_theta_attr(nd, 0.0)
200
+ return float(value) if value is not None else 0.0
201
+
202
+ diffs = (angle_diff(_theta(data), psi) for _, data in G.nodes(data=True))
203
+ # Try NumPy for a vectorised population variance
204
+ np = get_numpy()
205
+ if np is not None:
206
+ arr = np.fromiter(diffs, dtype=float)
207
+ var = float(np.var(arr)) if arr.size else 0.0
208
+ else:
209
+ try:
210
+ var = pvariance(diffs)
211
+ except StatisticsError:
212
+ var = 0.0
56
213
  return 1.0 / (1.0 + var)
57
214
 
58
- def orden_kuramoto(G) -> float:
59
- """R en [0,1], 1 = fases perfectamente alineadas."""
60
- X = [math.cos(_get_attr(G.nodes[n], ALIAS_THETA, 0.0)) for n in G.nodes()]
61
- Y = [math.sin(_get_attr(G.nodes[n], ALIAS_THETA, 0.0)) for n in G.nodes()]
62
- if not X:
215
+
216
+ def kuramoto_order(
217
+ G: TNFRGraph, R: float | None = None, psi: float | None = None
218
+ ) -> float:
219
+ """R in [0,1], 1 means perfectly aligned phases."""
220
+ if not _ensure_nodes(G):
63
221
  return 1.0
64
- R = ((sum(X)**2 + sum(Y)**2) ** 0.5) / max(1, len(X))
222
+ if R is None or psi is None:
223
+ R, psi = kuramoto_metrics(G)
65
224
  return float(R)
66
225
 
67
- def carga_glifica(G, window: int | None = None) -> dict:
68
- """Devuelve distribución de glifos aplicados en la red.
69
- - window: si se indica, cuenta solo los últimos `window` eventos por nodo; si no, usa el maxlen del deque.
70
- Retorna un dict con proporciones por glifo y agregados útiles.
226
+
227
+ def glyph_load(G: TNFRGraph, window: int | None = None) -> GlyphLoadDistribution:
228
+ """Return distribution of structural operators applied in the network.
229
+
230
+ Analyzes which structural operator symbols (glyphs) have been applied to
231
+ nodes in the network over a given time window.
232
+
233
+ - ``window``: if provided, count only the last ``window`` events per node;
234
+ otherwise use :data:`DEFAULT_GLYPH_LOAD_SPAN`.
235
+
236
+ Returns a dict with proportions per structural operator symbol and useful aggregates.
71
237
  """
72
- total = Counter()
73
- for n in G.nodes():
74
- nd = G.nodes[n]
75
- hist = nd.get("hist_glifos")
76
- if not hist:
77
- continue
78
- seq = list(hist)
79
- if window is not None and window > 0:
80
- seq = seq[-window:]
81
- total.update(seq)
82
-
83
-
84
- count = sum(total.values())
238
+ if window == 0:
239
+ return {"_count": 0.0}
240
+ if window is None:
241
+ window_int = DEFAULT_GLYPH_LOAD_SPAN
242
+ else:
243
+ window_int = validate_window(window, positive=True)
244
+ total = count_glyphs(G, window=window_int, last_only=(window_int == 1))
245
+ dist_raw, count = normalize_counter(total)
85
246
  if count == 0:
86
- return {"_count": 0}
87
-
88
-
89
- # Proporciones por glifo
90
- dist = {k: v / count for k, v in total.items()}
91
-
92
-
93
- # Agregados conceptuales (puedes ajustar categorías)
94
- estabilizadores = ["I’L", "R’A", "U’M", "SH’A"]
95
- disruptivos = ["O’Z", "Z’HIR", "NA’V", "T’HOL"]
96
-
97
-
98
- dist["_estabilizadores"] = sum(dist.get(k, 0.0) for k in estabilizadores)
99
- dist["_disruptivos"] = sum(dist.get(k, 0.0) for k in disruptivos)
100
- dist["_count"] = count
101
- return dist
102
-
103
- def sigma_vector(G, window: int | None = None) -> dict:
104
- """Vector de sentido Σ⃗ a partir de la distribución glífica reciente.
105
- Devuelve dict con x, y, mag (0..1) y angle (rad)."""
106
- # Distribución glífica (proporciones)
107
- dist = carga_glifica(G, window=window)
108
- if not dist or dist.get("_count", 0) == 0:
109
- return {"x": 0.0, "y": 0.0, "mag": 0.0, "angle": 0.0}
110
-
111
- # Mapeo polar de glifos principales en el plano de sentido
112
- # (ordenado estabilización→expansión→acoplamiento→silencio→disonancia→mutación→transición→autoorg.)
113
- angles = {
114
- "I’L": 0.0,
115
- "R’A": math.pi/4,
116
- "U’M": math.pi/2,
117
- "SH’A": 3*math.pi/4,
118
- "O’Z": math.pi,
119
- "Z’HIR": 5*math.pi/4,
120
- "NA’V": 3*math.pi/2,
121
- "T’HOL": 7*math.pi/4,
122
- }
123
- # Normaliza solo sobre glifos mapeados
124
- total = sum(dist.get(k, 0.0) for k in angles.keys())
125
- if total <= 0:
126
- return {"x": 0.0, "y": 0.0, "mag": 0.0, "angle": 0.0}
127
-
128
- x = 0.0
129
- y = 0.0
130
- for k, a in angles.items():
131
- p = dist.get(k, 0.0) / total
132
- x += p * math.cos(a)
133
- y += p * math.sin(a)
134
-
135
- mag = (x*x + y*y) ** 0.5
136
- ang = math.atan2(y, x)
137
- return {"x": float(x), "y": float(y), "mag": float(mag), "angle": float(ang)}
138
-
139
- def wbar(G, window: int | None = None) -> float:
140
- """Devuelve W̄ = media de C(t) en una ventana reciente."""
141
- hist = G.graph.get("history", {})
142
- cs = hist.get("C_steps", [])
247
+ return {"_count": 0.0}
248
+ dist = mix_groups(dist_raw, GLYPH_GROUPS)
249
+ glyph_dist: GlyphLoadDistribution = {}
250
+ for key, value in dist.items():
251
+ try:
252
+ glyph_key: Glyph | str = Glyph(key)
253
+ except ValueError:
254
+ glyph_key = key
255
+ glyph_dist[glyph_key] = value
256
+ glyph_dist["_count"] = float(count)
257
+ return glyph_dist
258
+
259
+
260
+ def wbar(G: TNFRGraph, window: int | None = None) -> float:
261
+ """Return = mean of ``C(t)`` over a recent window.
262
+
263
+ Uses :func:`ensure_history` to obtain ``G.graph['history']`` and falls back
264
+ to the instantaneous coherence when ``"C_steps"`` is missing or empty.
265
+ """
266
+ hist = ensure_history(G)
267
+ cs = list(hist.get("C_steps", []))
143
268
  if not cs:
144
- # fallback: coherencia instantánea
145
- return coherencia_global(G)
146
- if window is None:
147
- window = int(G.graph.get("WBAR_WINDOW", 25))
148
- w = min(len(cs), max(1, int(window)))
149
- return float(sum(cs[-w:]) / w)
269
+ # fallback: instantaneous coherence
270
+ return compute_coherence(G)
271
+ w_param = DEFAULT_WBAR_SPAN if window is None else window
272
+ w = validate_window(w_param, positive=True)
273
+ w = min(len(cs), w)
274
+ return float(statistics.fmean(cs[-w:]))
tnfr/observers.pyi ADDED
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from typing import Final
5
+
6
+ from .types import GlyphLoadDistribution, TNFRGraph
7
+
8
+ __all__: tuple[str, ...]
9
+
10
+ DEFAULT_GLYPH_LOAD_SPAN: Final[int]
11
+ DEFAULT_WBAR_SPAN: Final[int]
12
+
13
+ def _std_log(kind: str, G: TNFRGraph, ctx: Mapping[str, object]) -> None: ...
14
+ def attach_standard_observer(G: TNFRGraph) -> TNFRGraph: ...
15
+ def _ensure_nodes(G: TNFRGraph) -> bool: ...
16
+ def kuramoto_metrics(G: TNFRGraph) -> tuple[float, float]: ...
17
+ def phase_sync(
18
+ G: TNFRGraph,
19
+ R: float | None = ...,
20
+ psi: float | None = ...,
21
+ ) -> float: ...
22
+ def kuramoto_order(
23
+ G: TNFRGraph,
24
+ R: float | None = ...,
25
+ psi: float | None = ...,
26
+ ) -> float: ...
27
+ def glyph_load(
28
+ G: TNFRGraph,
29
+ window: int | None = ...,
30
+ ) -> GlyphLoadDistribution: ...
31
+ def wbar(G: TNFRGraph, window: int | None = ...) -> float: ...