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,408 @@
1
+ """Spectral dynamics helpers driven by ΔNFR generators."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import field
6
+ from typing import Any, NamedTuple, Sequence
7
+
8
+ import numpy as np
9
+
10
+ from ..compat.dataclass import dataclass
11
+ from .backend import MathematicsBackend, ensure_array, ensure_numpy, get_backend
12
+ from .spaces import HilbertSpace
13
+
14
+ try: # pragma: no cover - optional SciPy dependency
15
+ from scipy.linalg import expm as _scipy_expm # type: ignore
16
+ except Exception: # pragma: no cover - SciPy not installed
17
+ _scipy_expm = None
18
+
19
+ __all__ = ["MathematicalDynamicsEngine", "ContractiveDynamicsEngine"]
20
+
21
+
22
+ def _has_backend_matrix_exp(backend: MathematicsBackend) -> bool:
23
+ """Return ``True`` when ``backend`` exposes a usable ``matrix_exp``."""
24
+
25
+ matrix_exp = getattr(backend, "matrix_exp", None)
26
+ if not callable(matrix_exp):
27
+ return False
28
+
29
+ try:
30
+ probe = ensure_array([[0.0]], dtype=np.complex128, backend=backend)
31
+ matrix_exp(probe)
32
+ except (AttributeError, NotImplementedError):
33
+ return False
34
+ except Exception:
35
+ # Older backends may surface missing implementations as runtime errors;
36
+ # treat them as signals to fall back to SciPy when available.
37
+ return False
38
+ return True
39
+
40
+
41
+ def _as_matrix(
42
+ matrix: Sequence[Sequence[complex]] | np.ndarray | Any,
43
+ *,
44
+ backend: MathematicsBackend,
45
+ ) -> Any:
46
+ arr = ensure_array(matrix, dtype=np.complex128, backend=backend)
47
+ shape = getattr(arr, "shape", None)
48
+ if shape is None or len(shape) != 2 or shape[0] != shape[1]:
49
+ raise ValueError("Generator matrix must be square.")
50
+ return arr
51
+
52
+
53
+ def _is_hermitian(
54
+ matrix: Any, *, atol: float = 1e-9, backend: MathematicsBackend
55
+ ) -> bool:
56
+ matrix_np = ensure_numpy(matrix, backend=backend)
57
+ return bool(np.allclose(matrix_np, matrix_np.conj().T, atol=atol))
58
+
59
+
60
+ def _vectorize_density(matrix: Any, *, backend: MathematicsBackend) -> Any:
61
+ arr = ensure_array(matrix, dtype=np.complex128, backend=backend)
62
+ return arr.transpose(1, 0).reshape((-1,))
63
+
64
+
65
+ def _devectorize_density(vector: Any, dim: int, *, backend: MathematicsBackend) -> Any:
66
+ arr = ensure_array(vector, dtype=np.complex128, backend=backend)
67
+ return arr.reshape((dim, dim)).transpose(1, 0)
68
+
69
+
70
+ class TraceValue(NamedTuple):
71
+ """Container for trace evaluations in both backend and NumPy space."""
72
+
73
+ backend: Any
74
+ numpy: complex
75
+
76
+
77
+ def _trace(matrix: Any, *, backend: MathematicsBackend) -> TraceValue:
78
+ traced_backend = backend.einsum("ii->", matrix)
79
+ traced_numpy = complex(np.asarray(ensure_numpy(traced_backend, backend=backend)))
80
+ return TraceValue(traced_backend, traced_numpy)
81
+
82
+
83
+ @dataclass(slots=True)
84
+ class MathematicalDynamicsEngine:
85
+ """Unitary evolution generated by Hermitian ΔNFR operators.
86
+
87
+ The engine accepts inputs expressed as backend-native tensors (NumPy,
88
+ :mod:`jax`, :mod:`torch`). When the configured backend supports automatic
89
+ differentiation the evolution map ``exp(-i·Δ·dt)`` remains differentiable
90
+ because native propagators are now preferred. Passing ``use_scipy=True``
91
+ explicitly opts into SciPy's exponential; we only fall back automatically
92
+ when the backend lacks a ``matrix_exp`` implementation.
93
+ """
94
+
95
+ generator: np.ndarray
96
+ hilbert_space: HilbertSpace
97
+ atol: float = 1e-9
98
+ _use_scipy: bool = False
99
+ backend: MathematicsBackend = field(init=False, repr=False)
100
+ _generator_backend: Any = field(init=False, repr=False)
101
+ _numpy_generator: np.ndarray = field(init=False, repr=False)
102
+
103
+ def __init__(
104
+ self,
105
+ generator: Sequence[Sequence[complex]] | np.ndarray | Any,
106
+ hilbert_space: HilbertSpace,
107
+ *,
108
+ atol: float = 1e-9,
109
+ use_scipy: bool | None = None,
110
+ backend: MathematicsBackend | None = None,
111
+ ) -> None:
112
+ resolved_backend = backend or get_backend()
113
+ matrix = _as_matrix(generator, backend=resolved_backend)
114
+ matrix_np = ensure_numpy(matrix, backend=resolved_backend)
115
+ if matrix_np.shape != (hilbert_space.dimension, hilbert_space.dimension):
116
+ raise ValueError("Generator dimension must match the Hilbert space.")
117
+ if not _is_hermitian(matrix, atol=atol, backend=resolved_backend):
118
+ raise ValueError("Dynamics generator must be Hermitian.")
119
+ self.backend = resolved_backend
120
+ self._generator_backend = matrix
121
+ self._numpy_generator = matrix_np
122
+ self.generator = matrix_np
123
+ self.hilbert_space = hilbert_space
124
+ self.atol = float(atol)
125
+ if use_scipy is None:
126
+ has_matrix_exp = _has_backend_matrix_exp(self.backend)
127
+ if has_matrix_exp:
128
+ self._use_scipy = False
129
+ elif _scipy_expm is not None:
130
+ self._use_scipy = True
131
+ else:
132
+ raise RuntimeError(
133
+ "Backend lacks matrix_exp and SciPy is unavailable for fallback."
134
+ )
135
+ else:
136
+ if use_scipy and _scipy_expm is None:
137
+ raise RuntimeError("SciPy expm requested but SciPy is not available.")
138
+ self._use_scipy = bool(use_scipy and _scipy_expm is not None)
139
+
140
+ def _unitary_backend(self, dt: float) -> Any:
141
+ if self._use_scipy and _scipy_expm is not None:
142
+ return ensure_array(
143
+ _scipy_expm(-1j * dt * self._numpy_generator),
144
+ backend=self.backend,
145
+ )
146
+ return self.backend.matrix_exp(-1j * dt * self._generator_backend)
147
+
148
+ def step(
149
+ self,
150
+ state: Sequence[complex] | np.ndarray | Any,
151
+ *,
152
+ dt: float = 1.0,
153
+ normalize: bool = True,
154
+ ) -> Any:
155
+ """Evolve ``state`` by ``dt`` using the unitary ``exp(-i·Δ·dt)``."""
156
+
157
+ vector = ensure_array(state, dtype=np.complex128, backend=self.backend)
158
+ if vector.shape != (self.hilbert_space.dimension,):
159
+ raise ValueError(
160
+ "State dimension mismatch: "
161
+ f"expected ({self.hilbert_space.dimension},), received {vector.shape!r}."
162
+ )
163
+ unitary = self._unitary_backend(dt)
164
+ evolved = self.backend.matmul(unitary, vector)
165
+ if normalize:
166
+ norm_backend = self.backend.norm(evolved)
167
+ norm_numpy = float(
168
+ np.asarray(ensure_numpy(norm_backend, backend=self.backend))
169
+ )
170
+ if np.isclose(norm_numpy, 0.0, atol=self.atol):
171
+ raise ValueError("Cannot normalise a null state vector.")
172
+ evolved = evolved / norm_backend
173
+ return evolved
174
+
175
+ def evolve(
176
+ self,
177
+ state: Sequence[complex] | np.ndarray | Any,
178
+ *,
179
+ steps: int,
180
+ dt: float = 1.0,
181
+ normalize: bool = True,
182
+ ) -> Any:
183
+ """Return trajectory of length ``steps + 1`` starting from ``state``."""
184
+
185
+ if steps < 0:
186
+ raise ValueError("steps must be non-negative.")
187
+ current = ensure_array(state, dtype=np.complex128, backend=self.backend)
188
+ if current.shape != (self.hilbert_space.dimension,):
189
+ raise ValueError(
190
+ "State dimension mismatch: "
191
+ f"expected ({self.hilbert_space.dimension},), received {current.shape!r}."
192
+ )
193
+ trajectory: list[Any] = [current]
194
+ for _ in range(steps):
195
+ current = self.step(current, dt=dt, normalize=normalize)
196
+ trajectory.append(current)
197
+ return self.backend.stack(trajectory, axis=0)
198
+
199
+
200
+ @dataclass(slots=True)
201
+ class ContractiveDynamicsEngine:
202
+ """Contractive semigroup evolution driven by Lindblad ΔNFR generators.
203
+
204
+ Backend-native tensors are accepted for all density operators. When the
205
+ chosen backend supports automatic differentiation we keep gradients intact
206
+ by default because native semigroup propagators are preferred. Requesting
207
+ ``use_scipy=True`` still falls back to SciPy's :func:`scipy.linalg.expm`,
208
+ primarily for generators missing backend support.
209
+ """
210
+
211
+ generator: np.ndarray
212
+ hilbert_space: HilbertSpace
213
+ atol: float = 1e-9
214
+ _use_scipy: bool = False
215
+ backend: MathematicsBackend = field(init=False, repr=False)
216
+ _generator_backend: Any = field(init=False, repr=False)
217
+ _numpy_generator: np.ndarray = field(init=False, repr=False)
218
+ _identity_backend: Any = field(init=False, repr=False)
219
+ _last_contractivity_gap: float = field(init=False, repr=False)
220
+
221
+ def __init__(
222
+ self,
223
+ generator: Sequence[Sequence[complex]] | np.ndarray | Any,
224
+ hilbert_space: HilbertSpace,
225
+ *,
226
+ atol: float = 1e-9,
227
+ ensure_contractive: bool = True,
228
+ use_scipy: bool | None = None,
229
+ backend: MathematicsBackend | None = None,
230
+ ) -> None:
231
+ resolved_backend = backend or get_backend()
232
+ matrix = _as_matrix(generator, backend=resolved_backend)
233
+ matrix_np = ensure_numpy(matrix, backend=resolved_backend)
234
+ expected = hilbert_space.dimension * hilbert_space.dimension
235
+ if matrix_np.shape != (expected, expected):
236
+ raise ValueError(
237
+ "Generator must act on vectorised density operators with dimension "
238
+ f"{expected} × {expected}."
239
+ )
240
+ self.backend = resolved_backend
241
+ self._generator_backend = matrix
242
+ self._numpy_generator = matrix_np.astype(np.complex128, copy=False)
243
+ self.generator = self._numpy_generator
244
+ self.hilbert_space = hilbert_space
245
+ self.atol = float(atol)
246
+ if use_scipy is None:
247
+ has_matrix_exp = _has_backend_matrix_exp(self.backend)
248
+ if has_matrix_exp:
249
+ self._use_scipy = False
250
+ elif _scipy_expm is not None:
251
+ self._use_scipy = True
252
+ else:
253
+ raise RuntimeError(
254
+ "Backend lacks matrix_exp and SciPy is unavailable for fallback."
255
+ )
256
+ else:
257
+ if use_scipy and _scipy_expm is None:
258
+ raise RuntimeError("SciPy expm requested but SciPy is not available.")
259
+ self._use_scipy = bool(use_scipy and _scipy_expm is not None)
260
+
261
+ self._identity_backend = ensure_array(
262
+ np.eye(hilbert_space.dimension, dtype=np.complex128),
263
+ backend=self.backend,
264
+ )
265
+ self._last_contractivity_gap = float("nan")
266
+ if ensure_contractive:
267
+ eigenvalues_backend, _ = self.backend.eig(self._generator_backend)
268
+ eigenvalues = ensure_numpy(eigenvalues_backend, backend=self.backend)
269
+ if np.max(eigenvalues.real) > self.atol:
270
+ raise ValueError(
271
+ "ΔNFR generator is not contractive: positive real eigenvalues detected."
272
+ )
273
+
274
+ def _propagator_backend(self, dt: float) -> Any:
275
+ if self._use_scipy and _scipy_expm is not None:
276
+ return ensure_array(
277
+ _scipy_expm(dt * self._numpy_generator),
278
+ backend=self.backend,
279
+ )
280
+ return self.backend.matrix_exp(dt * self._generator_backend)
281
+
282
+ def frobenius_norm(
283
+ self,
284
+ density: Sequence[Sequence[complex]] | np.ndarray | Any,
285
+ *,
286
+ center: bool = False,
287
+ ) -> float:
288
+ """Return the Frobenius norm associated with the Hilbert space."""
289
+
290
+ matrix = ensure_array(density, dtype=np.complex128, backend=self.backend)
291
+ if matrix.shape != (self.hilbert_space.dimension, self.hilbert_space.dimension):
292
+ raise ValueError(
293
+ "Density operator dimension mismatch: "
294
+ f"expected {(self.hilbert_space.dimension, self.hilbert_space.dimension)}, "
295
+ f"received {matrix.shape!r}."
296
+ )
297
+ if center:
298
+ trace_value = _trace(matrix, backend=self.backend)
299
+ trace_backend = trace_value.backend / self.hilbert_space.dimension
300
+ matrix = matrix - trace_backend * self._identity_backend
301
+ norm_backend = self.backend.norm(matrix, ord="fro")
302
+ return float(np.asarray(ensure_numpy(norm_backend, backend=self.backend)))
303
+
304
+ @property
305
+ def last_contractivity_gap(self) -> float:
306
+ """Return the latest monitored contractivity gap (NaN if unavailable)."""
307
+
308
+ return float(self._last_contractivity_gap)
309
+
310
+ def step(
311
+ self,
312
+ density: Sequence[Sequence[complex]] | np.ndarray | Any,
313
+ *,
314
+ dt: float = 1.0,
315
+ normalize_trace: bool = True,
316
+ enforce_contractivity: bool = True,
317
+ raise_on_violation: bool = False,
318
+ symmetrize: bool = True,
319
+ ) -> Any:
320
+ """Advance ``density`` by ``dt`` enforcing trace and contractivity control."""
321
+
322
+ matrix = ensure_array(density, dtype=np.complex128, backend=self.backend)
323
+ dim = self.hilbert_space.dimension
324
+ if matrix.shape != (dim, dim):
325
+ raise ValueError(
326
+ "Density operator dimension mismatch: "
327
+ f"expected {(dim, dim)}, received {matrix.shape!r}."
328
+ )
329
+
330
+ initial_norm = None
331
+ if enforce_contractivity:
332
+ trace_value = _trace(matrix, backend=self.backend)
333
+ trace_backend = trace_value.backend / dim
334
+ centered = matrix - trace_backend * self._identity_backend
335
+ initial_norm_backend = self.backend.norm(centered, ord="fro")
336
+ initial_norm = float(
337
+ np.asarray(ensure_numpy(initial_norm_backend, backend=self.backend))
338
+ )
339
+
340
+ vector = _vectorize_density(matrix, backend=self.backend)
341
+ propagator = self._propagator_backend(dt)
342
+ evolved_vec = self.backend.matmul(propagator, vector)
343
+ evolved = _devectorize_density(evolved_vec, dim, backend=self.backend)
344
+
345
+ if symmetrize:
346
+ evolved = 0.5 * (evolved + self.backend.conjugate_transpose(evolved))
347
+
348
+ if normalize_trace:
349
+ trace_value = _trace(evolved, backend=self.backend)
350
+ if np.isclose(trace_value.numpy, 0.0, atol=self.atol):
351
+ raise ValueError("Trace collapsed below tolerance during evolution.")
352
+ if not np.isclose(trace_value.numpy, 1.0, atol=10 * self.atol):
353
+ evolved = evolved / trace_value.backend
354
+
355
+ if enforce_contractivity and initial_norm is not None:
356
+ trace_value = _trace(evolved, backend=self.backend)
357
+ trace_backend = trace_value.backend / dim
358
+ centered = evolved - trace_backend * self._identity_backend
359
+ evolved_norm_backend = self.backend.norm(centered, ord="fro")
360
+ evolved_norm = float(
361
+ np.asarray(ensure_numpy(evolved_norm_backend, backend=self.backend))
362
+ )
363
+ self._last_contractivity_gap = initial_norm - evolved_norm
364
+ if raise_on_violation and self._last_contractivity_gap < -5 * self.atol:
365
+ raise ValueError(
366
+ "Contractivity violated: Frobenius norm increased beyond tolerance."
367
+ )
368
+ else:
369
+ self._last_contractivity_gap = float("nan")
370
+
371
+ return evolved
372
+
373
+ def evolve(
374
+ self,
375
+ density: Sequence[Sequence[complex]] | np.ndarray | Any,
376
+ *,
377
+ steps: int,
378
+ dt: float = 1.0,
379
+ normalize_trace: bool = True,
380
+ enforce_contractivity: bool = True,
381
+ raise_on_violation: bool = False,
382
+ symmetrize: bool = True,
383
+ ) -> Any:
384
+ """Return trajectory of density operators for the contractive semigroup."""
385
+
386
+ if steps < 0:
387
+ raise ValueError("steps must be non-negative.")
388
+
389
+ current = ensure_array(density, dtype=np.complex128, backend=self.backend)
390
+ dim = self.hilbert_space.dimension
391
+ if current.shape != (dim, dim):
392
+ raise ValueError(
393
+ "Density operator dimension mismatch: "
394
+ f"expected {(dim, dim)}, received {current.shape!r}."
395
+ )
396
+
397
+ trajectory: list[Any] = [current]
398
+ for _ in range(steps):
399
+ current = self.step(
400
+ current,
401
+ dt=dt,
402
+ normalize_trace=normalize_trace,
403
+ enforce_contractivity=enforce_contractivity,
404
+ raise_on_violation=raise_on_violation,
405
+ symmetrize=symmetrize,
406
+ )
407
+ trajectory.append(current)
408
+ return self.backend.stack(trajectory, axis=0)
@@ -0,0 +1,90 @@
1
+ from __future__ import annotations
2
+
3
+ import numpy as np
4
+ from .backend import MathematicsBackend
5
+ from .spaces import HilbertSpace
6
+ from dataclasses import dataclass, field
7
+ from typing import Any, NamedTuple, Sequence
8
+
9
+ __all__ = ["MathematicalDynamicsEngine", "ContractiveDynamicsEngine"]
10
+
11
+ class TraceValue(NamedTuple):
12
+ backend: Any
13
+ numpy: complex
14
+
15
+ @dataclass
16
+ class MathematicalDynamicsEngine:
17
+ generator: np.ndarray
18
+ hilbert_space: HilbertSpace
19
+ atol: float = ...
20
+ backend: MathematicsBackend = field(init=False, repr=False)
21
+ def __init__(
22
+ self,
23
+ generator: Sequence[Sequence[complex]] | np.ndarray | Any,
24
+ hilbert_space: HilbertSpace,
25
+ *,
26
+ atol: float = 1e-09,
27
+ use_scipy: bool | None = None,
28
+ backend: MathematicsBackend | None = None,
29
+ ) -> None: ...
30
+ def step(
31
+ self,
32
+ state: Sequence[complex] | np.ndarray | Any,
33
+ *,
34
+ dt: float = 1.0,
35
+ normalize: bool = True,
36
+ ) -> Any: ...
37
+ def evolve(
38
+ self,
39
+ state: Sequence[complex] | np.ndarray | Any,
40
+ *,
41
+ steps: int,
42
+ dt: float = 1.0,
43
+ normalize: bool = True,
44
+ ) -> Any: ...
45
+
46
+ @dataclass
47
+ class ContractiveDynamicsEngine:
48
+ generator: np.ndarray
49
+ hilbert_space: HilbertSpace
50
+ atol: float = ...
51
+ backend: MathematicsBackend = field(init=False, repr=False)
52
+ def __init__(
53
+ self,
54
+ generator: Sequence[Sequence[complex]] | np.ndarray | Any,
55
+ hilbert_space: HilbertSpace,
56
+ *,
57
+ atol: float = 1e-09,
58
+ ensure_contractive: bool = True,
59
+ use_scipy: bool | None = None,
60
+ backend: MathematicsBackend | None = None,
61
+ ) -> None: ...
62
+ def frobenius_norm(
63
+ self,
64
+ density: Sequence[Sequence[complex]] | np.ndarray | Any,
65
+ *,
66
+ center: bool = False,
67
+ ) -> float: ...
68
+ @property
69
+ def last_contractivity_gap(self) -> float: ...
70
+ def step(
71
+ self,
72
+ density: Sequence[Sequence[complex]] | np.ndarray | Any,
73
+ *,
74
+ dt: float = 1.0,
75
+ normalize_trace: bool = True,
76
+ enforce_contractivity: bool = True,
77
+ raise_on_violation: bool = False,
78
+ symmetrize: bool = True,
79
+ ) -> Any: ...
80
+ def evolve(
81
+ self,
82
+ density: Sequence[Sequence[complex]] | np.ndarray | Any,
83
+ *,
84
+ steps: int,
85
+ dt: float = 1.0,
86
+ normalize_trace: bool = True,
87
+ enforce_contractivity: bool = True,
88
+ raise_on_violation: bool = False,
89
+ symmetrize: bool = True,
90
+ ) -> Any: ...