universal-physics-tensor 0.4.5

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.
Files changed (299) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +377 -0
  3. package/dist/bridges/equations/be-11-decoherence-master.d.ts +83 -0
  4. package/dist/bridges/equations/be-11-decoherence-master.d.ts.map +1 -0
  5. package/dist/bridges/equations/be-11-decoherence-master.js +116 -0
  6. package/dist/bridges/equations/be-11-decoherence-master.js.map +1 -0
  7. package/dist/bridges/equations/be-12-coherence-length.d.ts +80 -0
  8. package/dist/bridges/equations/be-12-coherence-length.d.ts.map +1 -0
  9. package/dist/bridges/equations/be-12-coherence-length.js +128 -0
  10. package/dist/bridges/equations/be-12-coherence-length.js.map +1 -0
  11. package/dist/bridges/equations/be-13-einstein-trace.d.ts +89 -0
  12. package/dist/bridges/equations/be-13-einstein-trace.d.ts.map +1 -0
  13. package/dist/bridges/equations/be-13-einstein-trace.js +143 -0
  14. package/dist/bridges/equations/be-13-einstein-trace.js.map +1 -0
  15. package/dist/bridges/equations/be-14-ryu-takayanagi.d.ts +67 -0
  16. package/dist/bridges/equations/be-14-ryu-takayanagi.d.ts.map +1 -0
  17. package/dist/bridges/equations/be-14-ryu-takayanagi.js +112 -0
  18. package/dist/bridges/equations/be-14-ryu-takayanagi.js.map +1 -0
  19. package/dist/bridges/equations/be-15-emergence.d.ts +164 -0
  20. package/dist/bridges/equations/be-15-emergence.d.ts.map +1 -0
  21. package/dist/bridges/equations/be-15-emergence.js +204 -0
  22. package/dist/bridges/equations/be-15-emergence.js.map +1 -0
  23. package/dist/bridges/equations/be-16-landauer.d.ts +180 -0
  24. package/dist/bridges/equations/be-16-landauer.d.ts.map +1 -0
  25. package/dist/bridges/equations/be-16-landauer.js +206 -0
  26. package/dist/bridges/equations/be-16-landauer.js.map +1 -0
  27. package/dist/bridges/equations/be-17-einstein-cartan.d.ts +245 -0
  28. package/dist/bridges/equations/be-17-einstein-cartan.d.ts.map +1 -0
  29. package/dist/bridges/equations/be-17-einstein-cartan.js +304 -0
  30. package/dist/bridges/equations/be-17-einstein-cartan.js.map +1 -0
  31. package/dist/bridges/equations/be-18-higgs-mass.d.ts +65 -0
  32. package/dist/bridges/equations/be-18-higgs-mass.d.ts.map +1 -0
  33. package/dist/bridges/equations/be-18-higgs-mass.js +86 -0
  34. package/dist/bridges/equations/be-18-higgs-mass.js.map +1 -0
  35. package/dist/bridges/equations/be-19-quantum-bounce.d.ts +72 -0
  36. package/dist/bridges/equations/be-19-quantum-bounce.d.ts.map +1 -0
  37. package/dist/bridges/equations/be-19-quantum-bounce.js +151 -0
  38. package/dist/bridges/equations/be-19-quantum-bounce.js.map +1 -0
  39. package/dist/bridges/equations/be-20-vacuum-energy.d.ts +72 -0
  40. package/dist/bridges/equations/be-20-vacuum-energy.d.ts.map +1 -0
  41. package/dist/bridges/equations/be-20-vacuum-energy.js +115 -0
  42. package/dist/bridges/equations/be-20-vacuum-energy.js.map +1 -0
  43. package/dist/bridges/equations/be-21-kss-bound.d.ts +72 -0
  44. package/dist/bridges/equations/be-21-kss-bound.d.ts.map +1 -0
  45. package/dist/bridges/equations/be-21-kss-bound.js +103 -0
  46. package/dist/bridges/equations/be-21-kss-bound.js.map +1 -0
  47. package/dist/bridges/equations/be-22-topological-entanglement.d.ts +90 -0
  48. package/dist/bridges/equations/be-22-topological-entanglement.d.ts.map +1 -0
  49. package/dist/bridges/equations/be-22-topological-entanglement.js +123 -0
  50. package/dist/bridges/equations/be-22-topological-entanglement.js.map +1 -0
  51. package/dist/bridges/equations/be-23-syk-planckian.d.ts +89 -0
  52. package/dist/bridges/equations/be-23-syk-planckian.d.ts.map +1 -0
  53. package/dist/bridges/equations/be-23-syk-planckian.js +155 -0
  54. package/dist/bridges/equations/be-23-syk-planckian.js.map +1 -0
  55. package/dist/bridges/equations/be-24-foerster-fret.d.ts +81 -0
  56. package/dist/bridges/equations/be-24-foerster-fret.d.ts.map +1 -0
  57. package/dist/bridges/equations/be-24-foerster-fret.js +121 -0
  58. package/dist/bridges/equations/be-24-foerster-fret.js.map +1 -0
  59. package/dist/bridges/equations/be-25-iit-phi.d.ts +220 -0
  60. package/dist/bridges/equations/be-25-iit-phi.d.ts.map +1 -0
  61. package/dist/bridges/equations/be-25-iit-phi.js +259 -0
  62. package/dist/bridges/equations/be-25-iit-phi.js.map +1 -0
  63. package/dist/bridges/equations/be-25-orch-or.d.ts +78 -0
  64. package/dist/bridges/equations/be-25-orch-or.d.ts.map +1 -0
  65. package/dist/bridges/equations/be-25-orch-or.js +121 -0
  66. package/dist/bridges/equations/be-25-orch-or.js.map +1 -0
  67. package/dist/bridges/equations/be-26-dna-tunneling.d.ts +75 -0
  68. package/dist/bridges/equations/be-26-dna-tunneling.d.ts.map +1 -0
  69. package/dist/bridges/equations/be-26-dna-tunneling.js +138 -0
  70. package/dist/bridges/equations/be-26-dna-tunneling.js.map +1 -0
  71. package/dist/bridges/equations/be-27-effective-temperature.d.ts +81 -0
  72. package/dist/bridges/equations/be-27-effective-temperature.d.ts.map +1 -0
  73. package/dist/bridges/equations/be-27-effective-temperature.js +120 -0
  74. package/dist/bridges/equations/be-27-effective-temperature.js.map +1 -0
  75. package/dist/bridges/equations/be-28-onsager-entropy-production.d.ts +175 -0
  76. package/dist/bridges/equations/be-28-onsager-entropy-production.d.ts.map +1 -0
  77. package/dist/bridges/equations/be-28-onsager-entropy-production.js +203 -0
  78. package/dist/bridges/equations/be-28-onsager-entropy-production.js.map +1 -0
  79. package/dist/bridges/equations/be-29-jarzynski.d.ts +86 -0
  80. package/dist/bridges/equations/be-29-jarzynski.d.ts.map +1 -0
  81. package/dist/bridges/equations/be-29-jarzynski.js +132 -0
  82. package/dist/bridges/equations/be-29-jarzynski.js.map +1 -0
  83. package/dist/bridges/equations/be-30-flm-first-law.d.ts +93 -0
  84. package/dist/bridges/equations/be-30-flm-first-law.d.ts.map +1 -0
  85. package/dist/bridges/equations/be-30-flm-first-law.js +109 -0
  86. package/dist/bridges/equations/be-30-flm-first-law.js.map +1 -0
  87. package/dist/bridges/equations/be-31-causal-set-bd.d.ts +96 -0
  88. package/dist/bridges/equations/be-31-causal-set-bd.d.ts.map +1 -0
  89. package/dist/bridges/equations/be-31-causal-set-bd.js +133 -0
  90. package/dist/bridges/equations/be-31-causal-set-bd.js.map +1 -0
  91. package/dist/bridges/equations/be-32-quantum-reference-frame.d.ts +113 -0
  92. package/dist/bridges/equations/be-32-quantum-reference-frame.d.ts.map +1 -0
  93. package/dist/bridges/equations/be-32-quantum-reference-frame.js +155 -0
  94. package/dist/bridges/equations/be-32-quantum-reference-frame.js.map +1 -0
  95. package/dist/bridges/equations/be-33-hertz-millis.d.ts +77 -0
  96. package/dist/bridges/equations/be-33-hertz-millis.d.ts.map +1 -0
  97. package/dist/bridges/equations/be-33-hertz-millis.js +113 -0
  98. package/dist/bridges/equations/be-33-hertz-millis.js.map +1 -0
  99. package/dist/bridges/equations/be-34-kibble-zurek.d.ts +76 -0
  100. package/dist/bridges/equations/be-34-kibble-zurek.d.ts.map +1 -0
  101. package/dist/bridges/equations/be-34-kibble-zurek.js +139 -0
  102. package/dist/bridges/equations/be-34-kibble-zurek.js.map +1 -0
  103. package/dist/bridges/equations/be-35-conformal-bootstrap.d.ts +117 -0
  104. package/dist/bridges/equations/be-35-conformal-bootstrap.d.ts.map +1 -0
  105. package/dist/bridges/equations/be-35-conformal-bootstrap.js +167 -0
  106. package/dist/bridges/equations/be-35-conformal-bootstrap.js.map +1 -0
  107. package/dist/bridges/equations/be-36-gw-speed-bound.d.ts +77 -0
  108. package/dist/bridges/equations/be-36-gw-speed-bound.d.ts.map +1 -0
  109. package/dist/bridges/equations/be-36-gw-speed-bound.js +107 -0
  110. package/dist/bridges/equations/be-36-gw-speed-bound.js.map +1 -0
  111. package/dist/bridges/equations/be-37-shapiro-delay.d.ts +260 -0
  112. package/dist/bridges/equations/be-37-shapiro-delay.d.ts.map +1 -0
  113. package/dist/bridges/equations/be-37-shapiro-delay.js +429 -0
  114. package/dist/bridges/equations/be-37-shapiro-delay.js.map +1 -0
  115. package/dist/bridges/equations/be-38-mond.d.ts +86 -0
  116. package/dist/bridges/equations/be-38-mond.d.ts.map +1 -0
  117. package/dist/bridges/equations/be-38-mond.js +122 -0
  118. package/dist/bridges/equations/be-38-mond.js.map +1 -0
  119. package/dist/bridges/equations/be-39-asymptotic-safety.d.ts +106 -0
  120. package/dist/bridges/equations/be-39-asymptotic-safety.d.ts.map +1 -0
  121. package/dist/bridges/equations/be-39-asymptotic-safety.js +155 -0
  122. package/dist/bridges/equations/be-39-asymptotic-safety.js.map +1 -0
  123. package/dist/bridges/equations/be-40-composite-higgs.d.ts +81 -0
  124. package/dist/bridges/equations/be-40-composite-higgs.d.ts.map +1 -0
  125. package/dist/bridges/equations/be-40-composite-higgs.js +149 -0
  126. package/dist/bridges/equations/be-40-composite-higgs.js.map +1 -0
  127. package/dist/bridges/equations/be-41-swampland.d.ts +67 -0
  128. package/dist/bridges/equations/be-41-swampland.d.ts.map +1 -0
  129. package/dist/bridges/equations/be-41-swampland.js +109 -0
  130. package/dist/bridges/equations/be-41-swampland.js.map +1 -0
  131. package/dist/bridges/equations/be-42-hawking-temperature.d.ts +67 -0
  132. package/dist/bridges/equations/be-42-hawking-temperature.d.ts.map +1 -0
  133. package/dist/bridges/equations/be-42-hawking-temperature.js +109 -0
  134. package/dist/bridges/equations/be-42-hawking-temperature.js.map +1 -0
  135. package/dist/bridges/equations/be-43-er-epr.d.ts +73 -0
  136. package/dist/bridges/equations/be-43-er-epr.d.ts.map +1 -0
  137. package/dist/bridges/equations/be-43-er-epr.js +114 -0
  138. package/dist/bridges/equations/be-43-er-epr.js.map +1 -0
  139. package/dist/bridges/equations/be-44-soft-hair.d.ts +151 -0
  140. package/dist/bridges/equations/be-44-soft-hair.d.ts.map +1 -0
  141. package/dist/bridges/equations/be-44-soft-hair.js +185 -0
  142. package/dist/bridges/equations/be-44-soft-hair.js.map +1 -0
  143. package/dist/bridges/equations/be-45-tcc.d.ts +116 -0
  144. package/dist/bridges/equations/be-45-tcc.d.ts.map +1 -0
  145. package/dist/bridges/equations/be-45-tcc.js +157 -0
  146. package/dist/bridges/equations/be-45-tcc.js.map +1 -0
  147. package/dist/bridges/equations/be-46-multiverse-measure.d.ts +163 -0
  148. package/dist/bridges/equations/be-46-multiverse-measure.d.ts.map +1 -0
  149. package/dist/bridges/equations/be-46-multiverse-measure.js +198 -0
  150. package/dist/bridges/equations/be-46-multiverse-measure.js.map +1 -0
  151. package/dist/bridges/equations/be-47-bbn-dark-sector.d.ts +72 -0
  152. package/dist/bridges/equations/be-47-bbn-dark-sector.d.ts.map +1 -0
  153. package/dist/bridges/equations/be-47-bbn-dark-sector.js +121 -0
  154. package/dist/bridges/equations/be-47-bbn-dark-sector.js.map +1 -0
  155. package/dist/bridges/equations/be-48-grw-localization.d.ts +84 -0
  156. package/dist/bridges/equations/be-48-grw-localization.d.ts.map +1 -0
  157. package/dist/bridges/equations/be-48-grw-localization.js +107 -0
  158. package/dist/bridges/equations/be-48-grw-localization.js.map +1 -0
  159. package/dist/bridges/equations/be-49-quantum-darwinism.d.ts +97 -0
  160. package/dist/bridges/equations/be-49-quantum-darwinism.d.ts.map +1 -0
  161. package/dist/bridges/equations/be-49-quantum-darwinism.js +129 -0
  162. package/dist/bridges/equations/be-49-quantum-darwinism.js.map +1 -0
  163. package/dist/bridges/equations/be-50-wheeler-feynman.d.ts +120 -0
  164. package/dist/bridges/equations/be-50-wheeler-feynman.d.ts.map +1 -0
  165. package/dist/bridges/equations/be-50-wheeler-feynman.js +151 -0
  166. package/dist/bridges/equations/be-50-wheeler-feynman.js.map +1 -0
  167. package/dist/bridges/gravitational-lensing.d.ts +52 -0
  168. package/dist/bridges/gravitational-lensing.d.ts.map +1 -0
  169. package/dist/bridges/gravitational-lensing.js +48 -0
  170. package/dist/bridges/gravitational-lensing.js.map +1 -0
  171. package/dist/bridges/index.d.ts +104 -0
  172. package/dist/bridges/index.d.ts.map +1 -0
  173. package/dist/bridges/index.js +1663 -0
  174. package/dist/bridges/index.js.map +1 -0
  175. package/dist/bridges/perihelion-precession.d.ts +62 -0
  176. package/dist/bridges/perihelion-precession.d.ts.map +1 -0
  177. package/dist/bridges/perihelion-precession.js +68 -0
  178. package/dist/bridges/perihelion-precession.js.map +1 -0
  179. package/dist/core/tensor.d.ts +135 -0
  180. package/dist/core/tensor.d.ts.map +1 -0
  181. package/dist/core/tensor.js +376 -0
  182. package/dist/core/tensor.js.map +1 -0
  183. package/dist/core/types.d.ts +131 -0
  184. package/dist/core/types.d.ts.map +1 -0
  185. package/dist/core/types.js +25 -0
  186. package/dist/core/types.js.map +1 -0
  187. package/dist/dimensional/algebra.d.ts +34 -0
  188. package/dist/dimensional/algebra.d.ts.map +1 -0
  189. package/dist/dimensional/algebra.js +90 -0
  190. package/dist/dimensional/algebra.js.map +1 -0
  191. package/dist/dimensional/bridge-check.d.ts +48 -0
  192. package/dist/dimensional/bridge-check.d.ts.map +1 -0
  193. package/dist/dimensional/bridge-check.js +137 -0
  194. package/dist/dimensional/bridge-check.js.map +1 -0
  195. package/dist/dimensional/connection-validators.d.ts +53 -0
  196. package/dist/dimensional/connection-validators.d.ts.map +1 -0
  197. package/dist/dimensional/connection-validators.js +84 -0
  198. package/dist/dimensional/connection-validators.js.map +1 -0
  199. package/dist/dimensional/connection.d.ts +40 -0
  200. package/dist/dimensional/connection.d.ts.map +1 -0
  201. package/dist/dimensional/connection.js +81 -0
  202. package/dist/dimensional/connection.js.map +1 -0
  203. package/dist/dimensional/constants.d.ts +30 -0
  204. package/dist/dimensional/constants.d.ts.map +1 -0
  205. package/dist/dimensional/constants.js +31 -0
  206. package/dist/dimensional/constants.js.map +1 -0
  207. package/dist/dimensional/errors.d.ts +161 -0
  208. package/dist/dimensional/errors.d.ts.map +1 -0
  209. package/dist/dimensional/errors.js +254 -0
  210. package/dist/dimensional/errors.js.map +1 -0
  211. package/dist/dimensional/fresh-label.d.ts +26 -0
  212. package/dist/dimensional/fresh-label.d.ts.map +1 -0
  213. package/dist/dimensional/fresh-label.js +31 -0
  214. package/dist/dimensional/fresh-label.js.map +1 -0
  215. package/dist/dimensional/metric-validators.d.ts +124 -0
  216. package/dist/dimensional/metric-validators.d.ts.map +1 -0
  217. package/dist/dimensional/metric-validators.js +141 -0
  218. package/dist/dimensional/metric-validators.js.map +1 -0
  219. package/dist/dimensional/metric.d.ts +67 -0
  220. package/dist/dimensional/metric.d.ts.map +1 -0
  221. package/dist/dimensional/metric.js +177 -0
  222. package/dist/dimensional/metric.js.map +1 -0
  223. package/dist/dimensional/tensor.d.ts +153 -0
  224. package/dist/dimensional/tensor.d.ts.map +1 -0
  225. package/dist/dimensional/tensor.js +138 -0
  226. package/dist/dimensional/tensor.js.map +1 -0
  227. package/dist/dimensional/types.d.ts +50 -0
  228. package/dist/dimensional/types.d.ts.map +1 -0
  229. package/dist/dimensional/types.js +66 -0
  230. package/dist/dimensional/types.js.map +1 -0
  231. package/dist/dimensional/validator.d.ts +84 -0
  232. package/dist/dimensional/validator.d.ts.map +1 -0
  233. package/dist/dimensional/validator.js +505 -0
  234. package/dist/dimensional/validator.js.map +1 -0
  235. package/dist/index.d.ts +25 -0
  236. package/dist/index.d.ts.map +1 -0
  237. package/dist/index.js +37 -0
  238. package/dist/index.js.map +1 -0
  239. package/dist/numerical/be37-covariant-eikonal.d.ts +90 -0
  240. package/dist/numerical/be37-covariant-eikonal.d.ts.map +1 -0
  241. package/dist/numerical/be37-covariant-eikonal.js +79 -0
  242. package/dist/numerical/be37-covariant-eikonal.js.map +1 -0
  243. package/dist/numerical/connection-lowering-helpers.d.ts +107 -0
  244. package/dist/numerical/connection-lowering-helpers.d.ts.map +1 -0
  245. package/dist/numerical/connection-lowering-helpers.js +315 -0
  246. package/dist/numerical/connection-lowering-helpers.js.map +1 -0
  247. package/dist/numerical/engine-registry.d.ts +50 -0
  248. package/dist/numerical/engine-registry.d.ts.map +1 -0
  249. package/dist/numerical/engine-registry.js +82 -0
  250. package/dist/numerical/engine-registry.js.map +1 -0
  251. package/dist/numerical/errors.d.ts +28 -0
  252. package/dist/numerical/errors.d.ts.map +1 -0
  253. package/dist/numerical/errors.js +39 -0
  254. package/dist/numerical/errors.js.map +1 -0
  255. package/dist/numerical/float64-engine.d.ts +53 -0
  256. package/dist/numerical/float64-engine.d.ts.map +1 -0
  257. package/dist/numerical/float64-engine.js +638 -0
  258. package/dist/numerical/float64-engine.js.map +1 -0
  259. package/dist/numerical/geodesic-integrator.d.ts +62 -0
  260. package/dist/numerical/geodesic-integrator.d.ts.map +1 -0
  261. package/dist/numerical/geodesic-integrator.js +103 -0
  262. package/dist/numerical/geodesic-integrator.js.map +1 -0
  263. package/dist/numerical/grid-field.d.ts +24 -0
  264. package/dist/numerical/grid-field.d.ts.map +1 -0
  265. package/dist/numerical/grid-field.js +2 -0
  266. package/dist/numerical/grid-field.js.map +1 -0
  267. package/dist/numerical/index.d.ts +80 -0
  268. package/dist/numerical/index.d.ts.map +1 -0
  269. package/dist/numerical/index.js +75 -0
  270. package/dist/numerical/index.js.map +1 -0
  271. package/dist/numerical/lowering.d.ts +48 -0
  272. package/dist/numerical/lowering.d.ts.map +1 -0
  273. package/dist/numerical/lowering.js +443 -0
  274. package/dist/numerical/lowering.js.map +1 -0
  275. package/dist/numerical/mathts-engine.d.ts +55 -0
  276. package/dist/numerical/mathts-engine.d.ts.map +1 -0
  277. package/dist/numerical/mathts-engine.js +164 -0
  278. package/dist/numerical/mathts-engine.js.map +1 -0
  279. package/dist/numerical/metric-inverse.d.ts +31 -0
  280. package/dist/numerical/metric-inverse.d.ts.map +1 -0
  281. package/dist/numerical/metric-inverse.js +68 -0
  282. package/dist/numerical/metric-inverse.js.map +1 -0
  283. package/dist/numerical/null-ray-integrator.d.ts +13 -0
  284. package/dist/numerical/null-ray-integrator.d.ts.map +1 -0
  285. package/dist/numerical/null-ray-integrator.js +53 -0
  286. package/dist/numerical/null-ray-integrator.js.map +1 -0
  287. package/dist/numerical/pderiv.d.ts +43 -0
  288. package/dist/numerical/pderiv.d.ts.map +1 -0
  289. package/dist/numerical/pderiv.js +121 -0
  290. package/dist/numerical/pderiv.js.map +1 -0
  291. package/dist/numerical/tensor-engine.d.ts +114 -0
  292. package/dist/numerical/tensor-engine.d.ts.map +1 -0
  293. package/dist/numerical/tensor-engine.js +64 -0
  294. package/dist/numerical/tensor-engine.js.map +1 -0
  295. package/dist/numerical/types.d.ts +37 -0
  296. package/dist/numerical/types.d.ts.map +1 -0
  297. package/dist/numerical/types.js +8 -0
  298. package/dist/numerical/types.js.map +1 -0
  299. package/package.json +72 -0
@@ -0,0 +1,638 @@
1
+ /**
2
+ * Float64ReferenceEngine — the pure-TypeScript, Float64Array-backed
3
+ * TensorEngine implementation. v0.3.5's default engine. Zero runtime
4
+ * dependencies. Naive O(n) algorithms: a correctness baseline, not a
5
+ * performance target (v0.3.5-Design.md §13). MathTSEngine (Task 11) is
6
+ * the performance answer.
7
+ *
8
+ * @module numerical/float64-engine
9
+ */
10
+ import { NumericalBackendError } from './errors.js';
11
+ // ---------------------------------------------------------------------------
12
+ // Private: forward-mode AD via dual numbers
13
+ // ---------------------------------------------------------------------------
14
+ /**
15
+ * EngineDualTensor — a primal Float64Array + per-element tangent, both of
16
+ * the same length. Implements the dual-number rules:
17
+ * (a, a') + (b, b') = (a+b, a'+b')
18
+ * (a, a') - (b, b') = (a-b, a'-b')
19
+ * (a, a') * (b, b') = (a·b, a·b' + a'·b) [alias case: 2a·a']
20
+ * scale((a, a'), k) = (k·a, k·a')
21
+ *
22
+ * S5 fix: `.data` returns the primal so that engine ops that reach into
23
+ * `EngineTensor.data` still work structurally (AD-aware branches detect
24
+ * `'tangent' in arg` BEFORE falling through to primal paths).
25
+ *
26
+ * @internal
27
+ */
28
+ class EngineDualTensor {
29
+ shape;
30
+ primal;
31
+ tangent;
32
+ constructor(shape, primal, tangent) {
33
+ this.shape = shape;
34
+ this.primal = primal;
35
+ this.tangent = tangent;
36
+ }
37
+ /** S5 fix: primal acts as `.data` for structurally-compatible dispatch. */
38
+ get data() { return this.primal; }
39
+ add(other) {
40
+ const p = new Float64Array(this.primal.length);
41
+ const t = new Float64Array(this.tangent.length);
42
+ for (let i = 0; i < p.length; i++) {
43
+ p[i] = this.primal[i] + other.primal[i];
44
+ t[i] = this.tangent[i] + other.tangent[i];
45
+ }
46
+ return new EngineDualTensor(this.shape, p, t);
47
+ }
48
+ sub(other) {
49
+ const p = new Float64Array(this.primal.length);
50
+ const t = new Float64Array(this.tangent.length);
51
+ for (let i = 0; i < p.length; i++) {
52
+ p[i] = this.primal[i] - other.primal[i];
53
+ t[i] = this.tangent[i] - other.tangent[i];
54
+ }
55
+ return new EngineDualTensor(this.shape, p, t);
56
+ }
57
+ /**
58
+ * Elementwise mul with I3 alias check: `if (this === other)` applies
59
+ * (a·a)' = 2·a·a' directly instead of the split path.
60
+ */
61
+ mul(other) {
62
+ const p = new Float64Array(this.primal.length);
63
+ const t = new Float64Array(this.tangent.length);
64
+ if (this === other) {
65
+ for (let i = 0; i < p.length; i++) {
66
+ p[i] = this.primal[i] * this.primal[i];
67
+ t[i] = 2 * this.primal[i] * this.tangent[i];
68
+ }
69
+ }
70
+ else {
71
+ for (let i = 0; i < p.length; i++) {
72
+ p[i] = this.primal[i] * other.primal[i];
73
+ t[i] = this.tangent[i] * other.primal[i] + this.primal[i] * other.tangent[i];
74
+ }
75
+ }
76
+ return new EngineDualTensor(this.shape, p, t);
77
+ }
78
+ scale(k) {
79
+ const p = new Float64Array(this.primal.length);
80
+ const t = new Float64Array(this.tangent.length);
81
+ for (let i = 0; i < p.length; i++) {
82
+ p[i] = this.primal[i] * k;
83
+ t[i] = this.tangent[i] * k;
84
+ }
85
+ return new EngineDualTensor(this.shape, p, t);
86
+ }
87
+ }
88
+ /**
89
+ * EngineTape — records op backward closures during the forward pass.
90
+ *
91
+ * S3 fix: disjoint ID namespaces. `allocate()` returns `nextInputId--`
92
+ * (negatives ← inputs); `record()` returns `nextOpId++` (non-negatives ← ops).
93
+ * Eliminates the collision that arises when id = nodes.length + some constant
94
+ * eventually wraps around with op ids.
95
+ *
96
+ * E19 fix: `backward()` uses `slot.set(outputGrad)` to overwrite the seed slot
97
+ * (not +=), so re-invoking backward() is idempotent and avoids double-counting.
98
+ *
99
+ * @internal
100
+ */
101
+ class EngineTape {
102
+ nodes = [];
103
+ inputGradSlots = new Map();
104
+ nextOpId = 0;
105
+ nextInputId = -1; // negatives = inputs; non-negatives = ops
106
+ allocate(size) {
107
+ const id = this.nextInputId--;
108
+ const gradSlot = new Float64Array(size);
109
+ this.inputGradSlots.set(id, gradSlot);
110
+ return { id, gradSlot };
111
+ }
112
+ record(inputIds, outputSize, backward) {
113
+ const outputGradSlot = new Float64Array(outputSize);
114
+ const id = this.nextOpId++;
115
+ this.nodes.push({ inputIds, backward, outputGradSlot });
116
+ this.inputGradSlots.set(id, outputGradSlot);
117
+ return { id, gradSlot: outputGradSlot };
118
+ }
119
+ /** E19 fix: overwrite (not +=) the seed slot, then walk tape in reverse. */
120
+ backward(outputId, outputGrad) {
121
+ const slot = this.inputGradSlots.get(outputId);
122
+ if (!slot) {
123
+ throw new NumericalBackendError(`EngineTape.backward: unknown outputId ${outputId}`);
124
+ }
125
+ slot.set(outputGrad); // E19 fix: overwrite, not accumulate
126
+ for (let n = this.nodes.length - 1; n >= 0; n--) {
127
+ this.nodes[n].backward(this.nodes[n].outputGradSlot);
128
+ }
129
+ }
130
+ getInputGrad(id) {
131
+ return this.inputGradSlots.get(id);
132
+ }
133
+ }
134
+ /**
135
+ * EngineTapedTensor — a primal Float64Array + tape reference + node id.
136
+ * Arithmetic methods register backward closures on the shared tape.
137
+ *
138
+ * S5 fix: `.data` returns the primal for structural compatibility with ops
139
+ * that reach into `EngineTensor.data`.
140
+ *
141
+ * I3 fix: `mul()` checks `if (this === other)` and accumulates
142
+ * `2·outputGrad·primal` instead of the aliasing-accident path.
143
+ *
144
+ * @internal
145
+ */
146
+ class EngineTapedTensor {
147
+ shape;
148
+ primal;
149
+ tape;
150
+ id;
151
+ constructor(shape, primal, tape, id) {
152
+ this.shape = shape;
153
+ this.primal = primal;
154
+ this.tape = tape;
155
+ this.id = id;
156
+ }
157
+ /** S5 fix: primal acts as `.data` for structurally-compatible dispatch. */
158
+ get data() { return this.primal; }
159
+ static fromInput(t, tape) {
160
+ const { id } = tape.allocate(t.data.length);
161
+ return new EngineTapedTensor(t.shape, new Float64Array(t.data), tape, id);
162
+ }
163
+ add(other) {
164
+ const out = new Float64Array(this.primal.length);
165
+ for (let i = 0; i < out.length; i++)
166
+ out[i] = this.primal[i] + other.primal[i];
167
+ const thisGradSlot = this.tape.getInputGrad(this.id);
168
+ const otherGradSlot = this.tape.getInputGrad(other.id);
169
+ const { id } = this.tape.record([this.id, other.id], out.length, (outputGrad) => {
170
+ for (let i = 0; i < outputGrad.length; i++) {
171
+ thisGradSlot[i] += outputGrad[i];
172
+ otherGradSlot[i] += outputGrad[i];
173
+ }
174
+ });
175
+ return new EngineTapedTensor(this.shape, out, this.tape, id);
176
+ }
177
+ sub(other) {
178
+ const out = new Float64Array(this.primal.length);
179
+ for (let i = 0; i < out.length; i++)
180
+ out[i] = this.primal[i] - other.primal[i];
181
+ const thisGradSlot = this.tape.getInputGrad(this.id);
182
+ const otherGradSlot = this.tape.getInputGrad(other.id);
183
+ const { id } = this.tape.record([this.id, other.id], out.length, (outputGrad) => {
184
+ for (let i = 0; i < outputGrad.length; i++) {
185
+ thisGradSlot[i] += outputGrad[i];
186
+ otherGradSlot[i] -= outputGrad[i];
187
+ }
188
+ });
189
+ return new EngineTapedTensor(this.shape, out, this.tape, id);
190
+ }
191
+ /**
192
+ * I3 fix: explicit alias check. When `this === other`, accumulate
193
+ * `2·outputGrad·primal` (d(a²)/da = 2a via VJP) instead of the
194
+ * split-path that relies on aliasing accidents.
195
+ */
196
+ mul(other) {
197
+ const out = new Float64Array(this.primal.length);
198
+ for (let i = 0; i < out.length; i++)
199
+ out[i] = this.primal[i] * other.primal[i];
200
+ const thisPrimal = this.primal;
201
+ const otherPrimal = other.primal;
202
+ const thisGradSlot = this.tape.getInputGrad(this.id);
203
+ const otherGradSlot = this.tape.getInputGrad(other.id);
204
+ const isAliased = this === other;
205
+ const { id } = this.tape.record([this.id, other.id], out.length, (outputGrad) => {
206
+ for (let i = 0; i < outputGrad.length; i++) {
207
+ if (isAliased) {
208
+ thisGradSlot[i] += 2 * outputGrad[i] * thisPrimal[i];
209
+ }
210
+ else {
211
+ thisGradSlot[i] += outputGrad[i] * otherPrimal[i];
212
+ otherGradSlot[i] += outputGrad[i] * thisPrimal[i];
213
+ }
214
+ }
215
+ });
216
+ return new EngineTapedTensor(this.shape, out, this.tape, id);
217
+ }
218
+ scale(k) {
219
+ const out = new Float64Array(this.primal.length);
220
+ for (let i = 0; i < out.length; i++)
221
+ out[i] = this.primal[i] * k;
222
+ const thisGradSlot = this.tape.getInputGrad(this.id);
223
+ const { id } = this.tape.record([this.id], out.length, (outputGrad) => {
224
+ for (let i = 0; i < outputGrad.length; i++) {
225
+ thisGradSlot[i] += outputGrad[i] * k;
226
+ }
227
+ });
228
+ return new EngineTapedTensor(this.shape, out, this.tape, id);
229
+ }
230
+ }
231
+ /** Row-major Float64Array-backed tensor. `strides[k]` is the flat-index
232
+ * step for axis k. Rank-0 has shape [] and a length-1 data array.
233
+ * @internal — concrete EngineTensor of Float64ReferenceEngine; consumers
234
+ * operate on the opaque `EngineTensor` handle, not this class. */
235
+ class Float64Tensor {
236
+ shape;
237
+ data;
238
+ constructor(shape, data) {
239
+ this.shape = shape;
240
+ this.data = data;
241
+ }
242
+ static rowMajorStrides(shape) {
243
+ const strides = new Array(shape.length);
244
+ let acc = 1;
245
+ for (let k = shape.length - 1; k >= 0; k--) {
246
+ strides[k] = acc;
247
+ acc *= shape[k];
248
+ }
249
+ return strides;
250
+ }
251
+ static sizeOf(shape) {
252
+ return shape.reduce((a, b) => a * b, 1);
253
+ }
254
+ }
255
+ function flatten(data, shape) {
256
+ const size = Float64Tensor.sizeOf(shape);
257
+ const out = new Float64Array(size);
258
+ let cursor = 0;
259
+ const walk = (node, depth) => {
260
+ if (depth === shape.length) {
261
+ if (typeof node !== 'number') {
262
+ throw new NumericalBackendError(`fromNested: expected a number at depth ${depth}, got ${typeof node}`);
263
+ }
264
+ out[cursor++] = node;
265
+ return;
266
+ }
267
+ if (!Array.isArray(node) || node.length !== shape[depth]) {
268
+ throw new NumericalBackendError(`fromNested: shape mismatch at depth ${depth} — expected length ${shape[depth]}`);
269
+ }
270
+ for (const child of node)
271
+ walk(child, depth + 1);
272
+ };
273
+ walk(data, 0);
274
+ if (cursor !== size) {
275
+ throw new NumericalBackendError(`fromNested: filled ${cursor} of ${size} elements`);
276
+ }
277
+ return out;
278
+ }
279
+ function rebuild(t) {
280
+ if (t.shape.length === 0)
281
+ return t.data[0];
282
+ const strides = Float64Tensor.rowMajorStrides(t.shape);
283
+ const build = (depth, offset) => {
284
+ if (depth === t.shape.length - 1) {
285
+ const row = [];
286
+ for (let i = 0; i < t.shape[depth]; i++)
287
+ row.push(t.data[offset + i]);
288
+ return row;
289
+ }
290
+ const out = [];
291
+ for (let i = 0; i < t.shape[depth]; i++) {
292
+ out.push(build(depth + 1, offset + i * strides[depth]));
293
+ }
294
+ return out;
295
+ };
296
+ return build(0, 0);
297
+ }
298
+ function asF64(t, op) {
299
+ if (!(t instanceof Float64Tensor)) {
300
+ throw new NumericalBackendError(`Float64ReferenceEngine.${op}: operand is not a Float64Tensor`);
301
+ }
302
+ return t;
303
+ }
304
+ function sameShape(a, b) {
305
+ return a.length === b.length && a.every((v, i) => v === b[i]);
306
+ }
307
+ function elementwise(a, b, op, f) {
308
+ if (!sameShape(a.shape, b.shape)) {
309
+ throw new NumericalBackendError(`Float64ReferenceEngine.${op}: shape mismatch [${a.shape}] vs [${b.shape}]`);
310
+ }
311
+ const out = new Float64Array(a.data.length);
312
+ for (let i = 0; i < a.data.length; i++)
313
+ out[i] = f(a.data[i], b.data[i]);
314
+ return new Float64Tensor(a.shape, out);
315
+ }
316
+ /** Iterate every multi-index of `shape` in row-major order, calling `visit`
317
+ * with a fresh index array each step. */
318
+ function forEachIndex(shape, visit) {
319
+ if (shape.length === 0) {
320
+ visit([]);
321
+ return;
322
+ }
323
+ const idx = new Array(shape.length).fill(0);
324
+ const total = Float64Tensor.sizeOf(shape);
325
+ for (let n = 0; n < total; n++) {
326
+ visit(idx);
327
+ for (let k = shape.length - 1; k >= 0; k--) {
328
+ if (++idx[k] < shape[k])
329
+ break;
330
+ idx[k] = 0;
331
+ }
332
+ }
333
+ }
334
+ function flatIndex(idx, strides) {
335
+ let f = 0;
336
+ for (let k = 0; k < idx.length; k++)
337
+ f += idx[k] * strides[k];
338
+ return f;
339
+ }
340
+ /**
341
+ * The pure-TypeScript, zero-dependency `TensorEngine` — v0.3.5's default
342
+ * engine and the correctness baseline for the conformance suite.
343
+ * @public
344
+ */
345
+ export class Float64ReferenceEngine {
346
+ name = 'Float64ReferenceEngine';
347
+ fromNested(data, shape) {
348
+ return new Float64Tensor(shape, flatten(data, shape));
349
+ }
350
+ toNested(t) {
351
+ return rebuild(asF64(t, 'toNested'));
352
+ }
353
+ add(a, b) {
354
+ // AD dispatch: dual path (forward-mode)
355
+ if ('tangent' in a && 'tangent' in b) {
356
+ return a.add(b);
357
+ }
358
+ // AD dispatch: tape path (reverse-mode)
359
+ if ('tape' in a && 'tape' in b) {
360
+ return a.add(b);
361
+ }
362
+ return elementwise(asF64(a, 'add'), asF64(b, 'add'), 'add', (x, y) => x + y);
363
+ }
364
+ sub(a, b) {
365
+ // AD dispatch: dual path (forward-mode)
366
+ if ('tangent' in a && 'tangent' in b) {
367
+ return a.sub(b);
368
+ }
369
+ // AD dispatch: tape path (reverse-mode)
370
+ if ('tape' in a && 'tape' in b) {
371
+ return a.sub(b);
372
+ }
373
+ return elementwise(asF64(a, 'sub'), asF64(b, 'sub'), 'sub', (x, y) => x - y);
374
+ }
375
+ mul(a, b) {
376
+ // AD dispatch: dual path (forward-mode)
377
+ if ('tangent' in a && 'tangent' in b) {
378
+ return a.mul(b);
379
+ }
380
+ // AD dispatch: tape path (reverse-mode)
381
+ if ('tape' in a && 'tape' in b) {
382
+ return a.mul(b);
383
+ }
384
+ return elementwise(asF64(a, 'mul'), asF64(b, 'mul'), 'mul', (x, y) => x * y);
385
+ }
386
+ scale(t, k) {
387
+ // AD dispatch: dual path (forward-mode)
388
+ if ('tangent' in t) {
389
+ return t.scale(k);
390
+ }
391
+ // AD dispatch: tape path (reverse-mode)
392
+ if ('tape' in t) {
393
+ return t.scale(k);
394
+ }
395
+ const f = asF64(t, 'scale');
396
+ const out = new Float64Array(f.data.length);
397
+ for (let i = 0; i < f.data.length; i++)
398
+ out[i] = f.data[i] * k;
399
+ return new Float64Tensor(f.shape, out);
400
+ }
401
+ identity(n) {
402
+ const out = new Float64Array(n * n);
403
+ for (let i = 0; i < n; i++)
404
+ out[i * n + i] = 1;
405
+ return new Float64Tensor([n, n], out);
406
+ }
407
+ normInf(t) {
408
+ const f = asF64(t, 'normInf');
409
+ let max = 0;
410
+ for (let i = 0; i < f.data.length; i++) {
411
+ const a = Math.abs(f.data[i]);
412
+ if (a > max)
413
+ max = a;
414
+ }
415
+ return max;
416
+ }
417
+ reshape(t, shape) {
418
+ const f = asF64(t, 'reshape');
419
+ if (Float64Tensor.sizeOf(shape) !== f.data.length) {
420
+ throw new NumericalBackendError(`reshape: size mismatch — [${f.shape}] (${f.data.length}) -> [${shape}]`);
421
+ }
422
+ // Row-major storage is order-preserving: same data buffer, new shape.
423
+ return new Float64Tensor([...shape], f.data.slice());
424
+ }
425
+ transpose(t, perm) {
426
+ const f = asF64(t, 'transpose');
427
+ const rank = f.shape.length;
428
+ const p = perm ?? Array.from({ length: rank }, (_, i) => rank - 1 - i);
429
+ if (p.length !== rank) {
430
+ throw new NumericalBackendError(`transpose: perm length ${p.length} != rank ${rank}`);
431
+ }
432
+ const outShape = p.map((axis) => f.shape[axis]);
433
+ const inStrides = Float64Tensor.rowMajorStrides(f.shape);
434
+ const out = new Float64Array(f.data.length);
435
+ const outStrides = Float64Tensor.rowMajorStrides(outShape);
436
+ forEachIndex(outShape, (outIdx) => {
437
+ // outIdx[k] is the value of original axis p[k]; map back to input index.
438
+ const inIdx = new Array(rank);
439
+ for (let k = 0; k < rank; k++)
440
+ inIdx[p[k]] = outIdx[k];
441
+ out[flatIndex(outIdx, outStrides)] = f.data[flatIndex(inIdx, inStrides)];
442
+ });
443
+ return new Float64Tensor(outShape, out);
444
+ }
445
+ matMul(a, b) {
446
+ const fa = asF64(a, 'matMul');
447
+ const fb = asF64(b, 'matMul');
448
+ if (fa.shape.length !== 2 || fb.shape.length !== 2) {
449
+ throw new NumericalBackendError(`matMul: both operands must be rank-2 — got [${fa.shape}], [${fb.shape}]`);
450
+ }
451
+ const [m, k] = fa.shape;
452
+ const [k2, n] = fb.shape;
453
+ if (k !== k2) {
454
+ throw new NumericalBackendError(`matMul: inner dimension mismatch ${k} != ${k2}`);
455
+ }
456
+ const out = new Float64Array(m * n);
457
+ for (let i = 0; i < m; i++) {
458
+ for (let j = 0; j < n; j++) {
459
+ let sum = 0;
460
+ for (let p = 0; p < k; p++)
461
+ sum += fa.data[i * k + p] * fb.data[p * n + j];
462
+ out[i * n + j] = sum;
463
+ }
464
+ }
465
+ return new Float64Tensor([m, n], out);
466
+ }
467
+ einsum(spec, ...operands) {
468
+ const ops = operands.map((o, i) => asF64(o, `einsum (operand ${i})`));
469
+ const inStrides = ops.map((o) => Float64Tensor.rowMajorStrides(o.shape));
470
+ // Each free axis -> one output index variable; each contraction -> one
471
+ // summed index variable. Determine the size of every variable and the
472
+ // (operand, axis) sites that read it.
473
+ const freeSizes = spec.free.map((fa) => {
474
+ const op = ops[fa.operand];
475
+ if (!op)
476
+ throw new NumericalBackendError(`einsum: free axis references missing operand ${fa.operand}`);
477
+ return op.shape[fa.axis];
478
+ });
479
+ const contractSizes = spec.contractions.map((c) => {
480
+ const [[oa, axa], [ob, axb]] = c.pair;
481
+ const sa = ops[oa]?.shape[axa];
482
+ const sb = ops[ob]?.shape[axb];
483
+ if (sa === undefined || sb === undefined || sa !== sb) {
484
+ throw new NumericalBackendError(`einsum: contraction pair size mismatch — (${oa},${axa})=${sa} vs (${ob},${axb})=${sb}`);
485
+ }
486
+ return sa;
487
+ });
488
+ const outShape = freeSizes;
489
+ const outStrides = Float64Tensor.rowMajorStrides(outShape);
490
+ const out = new Float64Array(Float64Tensor.sizeOf(outShape));
491
+ // Guard (finding #2): every axis of a rank-≥1 operand MUST appear in the
492
+ // spec as either a free axis or a contracted axis. An unreferenced axis
493
+ // would silently read index 0 on every iteration — a wrong result. A
494
+ // rank-0 operand legitimately participates in nothing: it contributes its
495
+ // scalar value data[0] as a factor to every output element. That is the
496
+ // documented, intended behaviour — only rank-≥1 operands are guarded.
497
+ for (let o = 0; o < ops.length; o++) {
498
+ if (ops[o].shape.length === 0)
499
+ continue; // rank-0 scalar factor — allowed
500
+ const covered = new Set();
501
+ spec.free.forEach((fa) => { if (fa.operand === o)
502
+ covered.add(fa.axis); });
503
+ spec.contractions.forEach((c) => {
504
+ const [[oa, axa], [ob, axb]] = c.pair;
505
+ if (oa === o)
506
+ covered.add(axa);
507
+ if (ob === o)
508
+ covered.add(axb);
509
+ });
510
+ if (covered.size !== ops[o].shape.length) {
511
+ throw new NumericalBackendError(`einsum: operand ${o} (rank ${ops[o].shape.length}) has axes not referenced `
512
+ + `by the spec — every rank-≥1 operand axis must be a free or contracted axis`);
513
+ }
514
+ }
515
+ // For a given assignment of (free vars, contract vars), compute the flat
516
+ // index into each operand by summing the contributions of every axis that
517
+ // reads a variable.
518
+ const operandFlatIndex = (opIndex, freeVals, contractVals) => {
519
+ const idx = new Array(ops[opIndex].shape.length).fill(0);
520
+ spec.free.forEach((fa, v) => { if (fa.operand === opIndex)
521
+ idx[fa.axis] = freeVals[v]; });
522
+ spec.contractions.forEach((c, v) => {
523
+ const [[oa, axa], [ob, axb]] = c.pair;
524
+ if (oa === opIndex)
525
+ idx[axa] = contractVals[v];
526
+ if (ob === opIndex)
527
+ idx[axb] = contractVals[v];
528
+ });
529
+ return flatIndex(idx, inStrides[opIndex]);
530
+ };
531
+ forEachIndex(outShape, (freeVals) => {
532
+ let acc = 0;
533
+ forEachIndex(contractSizes, (contractVals) => {
534
+ let product = 1;
535
+ for (let o = 0; o < ops.length; o++) {
536
+ product *= ops[o].data[operandFlatIndex(o, freeVals, contractVals)];
537
+ }
538
+ acc += product;
539
+ });
540
+ out[flatIndex(freeVals, outStrides)] = acc;
541
+ });
542
+ return new Float64Tensor(outShape, out);
543
+ }
544
+ // -------------------------------------------------------------------------
545
+ // Forward-mode AD (Jacobian-vector product via dual numbers)
546
+ // -------------------------------------------------------------------------
547
+ /**
548
+ * Compute the value and full Jacobian of `fn` at point `x` using
549
+ * forward-mode automatic differentiation (dual numbers).
550
+ *
551
+ * Returns `{ value, jacobian }` where `jacobian.shape = [...value.shape, ...x.shape]`
552
+ * (row-major). `jacobian.data[kY * xSize + kX] = ∂y[kY] / ∂x[kX]`.
553
+ *
554
+ * Guard: if `fn` returns a non-EngineDualTensor, throws a clear error
555
+ * ("AD-traceable" guard, reconciliation fix S6/I2).
556
+ */
557
+ async forwardGrad(fn, x) {
558
+ const xData = x.data;
559
+ const xSize = xData.length;
560
+ // Probe with zero tangent to learn the output shape.
561
+ const xDualZero = new EngineDualTensor(x.shape, new Float64Array(xData), new Float64Array(xSize));
562
+ const yProbeRaw = fn(xDualZero);
563
+ if (!('tangent' in yProbeRaw)) {
564
+ throw new NumericalBackendError('Float64ReferenceEngine.forwardGrad: fn must be AD-traceable — its return must ' +
565
+ 'propagate through EngineDualTensor arithmetic (use engine.add/sub/mul/scale on ' +
566
+ 'the argument). A plain-tensor return loses the tangent and corrupts the Jacobian.');
567
+ }
568
+ const yProbe = yProbeRaw;
569
+ const ySize = yProbe.primal.length;
570
+ const jacobianShape = [...yProbe.shape, ...x.shape];
571
+ const jacobianData = new Float64Array(jacobianShape.reduce((a, b) => a * b, 1));
572
+ // Sweep each input flat-index kX. Build a unit-tangent dual, run fn,
573
+ // scatter the tangent into the Jacobian column.
574
+ for (let kX = 0; kX < xSize; kX++) {
575
+ const tan = new Float64Array(xSize);
576
+ tan[kX] = 1;
577
+ const xDualUnit = new EngineDualTensor(x.shape, new Float64Array(xData), tan);
578
+ const yDualRaw = fn(xDualUnit);
579
+ if (!('tangent' in yDualRaw)) {
580
+ throw new NumericalBackendError('Float64ReferenceEngine.forwardGrad: fn lost AD trace mid-sweep (returned non-dual tensor)');
581
+ }
582
+ const yDual = yDualRaw;
583
+ for (let kY = 0; kY < ySize; kY++) {
584
+ jacobianData[kY * xSize + kX] = yDual.tangent[kY];
585
+ }
586
+ }
587
+ return {
588
+ value: new Float64Tensor(yProbe.shape, new Float64Array(yProbe.primal)),
589
+ jacobian: new Float64Tensor(jacobianShape, jacobianData),
590
+ };
591
+ }
592
+ // -------------------------------------------------------------------------
593
+ // Reverse-mode AD (vector-Jacobian product via tape)
594
+ // -------------------------------------------------------------------------
595
+ /**
596
+ * Compute the value and gradient (VJP) of `fn` at point `x` using
597
+ * reverse-mode automatic differentiation (tape).
598
+ *
599
+ * `cotangent` defaults to ones-like(value). For non-scalar outputs,
600
+ * cotangent.shape must match value.shape.
601
+ *
602
+ * Guard: if `fn` returns a non-EngineTapedTensor, throws a clear error.
603
+ */
604
+ async reverseGrad(fn, x, cotangent) {
605
+ const tape = new EngineTape();
606
+ const xF64 = x;
607
+ const xTaped = EngineTapedTensor.fromInput(xF64, tape);
608
+ const yRaw = fn(xTaped);
609
+ if (!('tape' in yRaw)) {
610
+ throw new NumericalBackendError('Float64ReferenceEngine.reverseGrad: fn must be AD-traceable — its return must ' +
611
+ 'propagate through EngineTapedTensor arithmetic (use engine.add/sub/mul/scale on ' +
612
+ 'the argument). A plain-tensor return loses the tape and corrupts the gradient.');
613
+ }
614
+ const yTaped = yRaw;
615
+ const value = new Float64Tensor(yTaped.shape, new Float64Array(yTaped.primal));
616
+ // Resolve cotangent.
617
+ let ctData;
618
+ if (cotangent === undefined) {
619
+ ctData = new Float64Array(yTaped.primal.length).fill(1);
620
+ }
621
+ else {
622
+ if (cotangent.shape.length !== value.shape.length ||
623
+ !cotangent.shape.every((v, i) => v === value.shape[i])) {
624
+ throw new NumericalBackendError(`Float64ReferenceEngine.reverseGrad: cotangent shape [${cotangent.shape}] ` +
625
+ `!= value shape [${value.shape}]`);
626
+ }
627
+ ctData = new Float64Array(cotangent.data);
628
+ }
629
+ // E19 fix: backward() uses slot.set(outputGrad) — see EngineTape.backward.
630
+ tape.backward(yTaped.id, ctData);
631
+ const xGrad = tape.getInputGrad(xTaped.id);
632
+ return {
633
+ value,
634
+ gradient: new Float64Tensor(x.shape, new Float64Array(xGrad)),
635
+ };
636
+ }
637
+ }
638
+ //# sourceMappingURL=float64-engine.js.map