universal-physics-tensor 1.0.3 → 2.0.0

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 (306) hide show
  1. package/README.md +118 -73
  2. package/dist/atlas/benchmark/hybrid-retrieval.d.ts +1 -0
  3. package/dist/atlas/benchmark/hybrid-retrieval.d.ts.map +1 -1
  4. package/dist/atlas/benchmark/hybrid-retrieval.js.map +1 -1
  5. package/dist/atlas/chain-pipeline.d.ts +37 -0
  6. package/dist/atlas/chain-pipeline.d.ts.map +1 -1
  7. package/dist/atlas/chain-pipeline.js +95 -79
  8. package/dist/atlas/chain-pipeline.js.map +1 -1
  9. package/dist/atlas/composition-table.d.ts +5 -7
  10. package/dist/atlas/composition-table.d.ts.map +1 -1
  11. package/dist/atlas/composition-table.js +3 -5
  12. package/dist/atlas/composition-table.js.map +1 -1
  13. package/dist/atlas/proof-target.d.ts +28 -2
  14. package/dist/atlas/proof-target.d.ts.map +1 -1
  15. package/dist/atlas/proof-target.js +25 -16
  16. package/dist/atlas/proof-target.js.map +1 -1
  17. package/dist/atlas/regime.d.ts +2 -2
  18. package/dist/atlas/regime.d.ts.map +1 -1
  19. package/dist/atlas/regime.js +1 -1
  20. package/dist/atlas/regime.js.map +1 -1
  21. package/dist/atlas/types.d.ts +7 -7
  22. package/dist/atlas/types.d.ts.map +1 -1
  23. package/dist/bridges/be53-yang-mills-confrontation.d.ts +1 -1
  24. package/dist/bridges/be53-yang-mills-confrontation.d.ts.map +1 -1
  25. package/dist/canonical/normal-form.d.ts +2 -1
  26. package/dist/canonical/normal-form.d.ts.map +1 -1
  27. package/dist/canonical/normal-form.js +2 -1
  28. package/dist/canonical/normal-form.js.map +1 -1
  29. package/dist/cases/brownian-sphere.d.ts.map +1 -1
  30. package/dist/cases/brownian-sphere.js +1 -0
  31. package/dist/cases/brownian-sphere.js.map +1 -1
  32. package/dist/cli/args.d.ts +7 -0
  33. package/dist/cli/args.d.ts.map +1 -1
  34. package/dist/cli/args.js.map +1 -1
  35. package/dist/cli/command.d.ts +13 -0
  36. package/dist/cli/command.d.ts.map +1 -1
  37. package/dist/cli/command.js.map +1 -1
  38. package/dist/cli/commands/_atlas-route.d.ts +1 -1
  39. package/dist/cli/commands/atlas.d.ts.map +1 -1
  40. package/dist/cli/commands/atlas.js +28 -7
  41. package/dist/cli/commands/atlas.js.map +1 -1
  42. package/dist/cli/commands/audit.d.ts.map +1 -1
  43. package/dist/cli/commands/audit.js +7 -3
  44. package/dist/cli/commands/audit.js.map +1 -1
  45. package/dist/cli/commands/axes.d.ts.map +1 -1
  46. package/dist/cli/commands/axes.js +6 -2
  47. package/dist/cli/commands/axes.js.map +1 -1
  48. package/dist/cli/commands/candidates.d.ts.map +1 -1
  49. package/dist/cli/commands/candidates.js +7 -3
  50. package/dist/cli/commands/candidates.js.map +1 -1
  51. package/dist/cli/commands/canonical.d.ts.map +1 -1
  52. package/dist/cli/commands/canonical.js +11 -3
  53. package/dist/cli/commands/canonical.js.map +1 -1
  54. package/dist/cli/commands/chain.d.ts +1 -0
  55. package/dist/cli/commands/chain.d.ts.map +1 -1
  56. package/dist/cli/commands/chain.js +12 -1
  57. package/dist/cli/commands/chain.js.map +1 -1
  58. package/dist/cli/commands/confront.d.ts.map +1 -1
  59. package/dist/cli/commands/confront.js +20 -6
  60. package/dist/cli/commands/confront.js.map +1 -1
  61. package/dist/cli/commands/connectors.d.ts.map +1 -1
  62. package/dist/cli/commands/connectors.js +7 -3
  63. package/dist/cli/commands/connectors.js.map +1 -1
  64. package/dist/cli/commands/coverage.d.ts.map +1 -1
  65. package/dist/cli/commands/coverage.js +6 -2
  66. package/dist/cli/commands/coverage.js.map +1 -1
  67. package/dist/cli/commands/derive.d.ts.map +1 -1
  68. package/dist/cli/commands/derive.js +12 -4
  69. package/dist/cli/commands/derive.js.map +1 -1
  70. package/dist/cli/commands/discover.d.ts.map +1 -1
  71. package/dist/cli/commands/discover.js +35 -8
  72. package/dist/cli/commands/discover.js.map +1 -1
  73. package/dist/cli/commands/eval.d.ts.map +1 -1
  74. package/dist/cli/commands/eval.js +26 -19
  75. package/dist/cli/commands/eval.js.map +1 -1
  76. package/dist/cli/commands/evaluate.d.ts +7 -3
  77. package/dist/cli/commands/evaluate.d.ts.map +1 -1
  78. package/dist/cli/commands/evaluate.js +71 -36
  79. package/dist/cli/commands/evaluate.js.map +1 -1
  80. package/dist/cli/commands/explain.d.ts.map +1 -1
  81. package/dist/cli/commands/explain.js +7 -3
  82. package/dist/cli/commands/explain.js.map +1 -1
  83. package/dist/cli/commands/frontier.d.ts.map +1 -1
  84. package/dist/cli/commands/frontier.js +12 -2
  85. package/dist/cli/commands/frontier.js.map +1 -1
  86. package/dist/cli/commands/ground.d.ts.map +1 -1
  87. package/dist/cli/commands/ground.js +20 -5
  88. package/dist/cli/commands/ground.js.map +1 -1
  89. package/dist/cli/commands/map.d.ts +1 -1
  90. package/dist/cli/commands/map.d.ts.map +1 -1
  91. package/dist/cli/commands/map.js +29 -25
  92. package/dist/cli/commands/map.js.map +1 -1
  93. package/dist/cli/commands/metric.d.ts.map +1 -1
  94. package/dist/cli/commands/metric.js +21 -14
  95. package/dist/cli/commands/metric.js.map +1 -1
  96. package/dist/cli/commands/path.d.ts +3 -2
  97. package/dist/cli/commands/path.d.ts.map +1 -1
  98. package/dist/cli/commands/path.js +47 -20
  99. package/dist/cli/commands/path.js.map +1 -1
  100. package/dist/cli/commands/predict.d.ts.map +1 -1
  101. package/dist/cli/commands/predict.js +7 -3
  102. package/dist/cli/commands/predict.js.map +1 -1
  103. package/dist/cli/commands/priority.d.ts.map +1 -1
  104. package/dist/cli/commands/priority.js +7 -3
  105. package/dist/cli/commands/priority.js.map +1 -1
  106. package/dist/cli/commands/probe.d.ts.map +1 -1
  107. package/dist/cli/commands/probe.js +32 -16
  108. package/dist/cli/commands/probe.js.map +1 -1
  109. package/dist/cli/commands/recover.d.ts.map +1 -1
  110. package/dist/cli/commands/recover.js +7 -4
  111. package/dist/cli/commands/recover.js.map +1 -1
  112. package/dist/cli/commands/regime.d.ts +2 -2
  113. package/dist/cli/commands/regime.d.ts.map +1 -1
  114. package/dist/cli/commands/regime.js +36 -12
  115. package/dist/cli/commands/regime.js.map +1 -1
  116. package/dist/cli/commands/retrieve.d.ts.map +1 -1
  117. package/dist/cli/commands/retrieve.js +23 -4
  118. package/dist/cli/commands/retrieve.js.map +1 -1
  119. package/dist/cli/commands/search.d.ts.map +1 -1
  120. package/dist/cli/commands/search.js +12 -2
  121. package/dist/cli/commands/search.js.map +1 -1
  122. package/dist/cli/commands/symbolic.d.ts.map +1 -1
  123. package/dist/cli/commands/symbolic.js +11 -3
  124. package/dist/cli/commands/symbolic.js.map +1 -1
  125. package/dist/cli/commands/testplan.d.ts.map +1 -1
  126. package/dist/cli/commands/testplan.js +12 -2
  127. package/dist/cli/commands/testplan.js.map +1 -1
  128. package/dist/cli/eval-numbers.d.ts +2 -2
  129. package/dist/cli/eval-numbers.js +2 -2
  130. package/dist/cli/flag-help.d.ts +34 -0
  131. package/dist/cli/flag-help.d.ts.map +1 -0
  132. package/dist/cli/flag-help.js +113 -0
  133. package/dist/cli/flag-help.js.map +1 -0
  134. package/dist/cli/main.d.ts +0 -8
  135. package/dist/cli/main.d.ts.map +1 -1
  136. package/dist/cli/main.js +42 -29
  137. package/dist/cli/main.js.map +1 -1
  138. package/dist/cli/version.d.ts +6 -2
  139. package/dist/cli/version.d.ts.map +1 -1
  140. package/dist/cli/version.js +11 -3
  141. package/dist/cli/version.js.map +1 -1
  142. package/dist/cli-api.d.ts +50 -0
  143. package/dist/cli-api.d.ts.map +1 -1
  144. package/dist/cli-api.js +42 -0
  145. package/dist/cli-api.js.map +1 -1
  146. package/dist/composition/adjudication.d.ts +9 -0
  147. package/dist/composition/adjudication.d.ts.map +1 -1
  148. package/dist/composition/adjudication.js +13 -0
  149. package/dist/composition/adjudication.js.map +1 -1
  150. package/dist/composition/canonical-compare.d.ts +1 -0
  151. package/dist/composition/canonical-compare.d.ts.map +1 -1
  152. package/dist/composition/canonical-compare.js +1 -0
  153. package/dist/composition/canonical-compare.js.map +1 -1
  154. package/dist/composition/chain-result.d.ts +55 -0
  155. package/dist/composition/chain-result.d.ts.map +1 -0
  156. package/dist/composition/chain-result.js +57 -0
  157. package/dist/composition/chain-result.js.map +1 -0
  158. package/dist/composition/compose-symbolic.d.ts.map +1 -1
  159. package/dist/composition/compose-symbolic.js +4 -39
  160. package/dist/composition/compose-symbolic.js.map +1 -1
  161. package/dist/composition/compose.d.ts.map +1 -1
  162. package/dist/composition/compose.js +11 -4
  163. package/dist/composition/compose.js.map +1 -1
  164. package/dist/composition/edges/calibration.d.ts +9 -0
  165. package/dist/composition/edges/calibration.d.ts.map +1 -1
  166. package/dist/composition/edges/calibration.js +9 -9
  167. package/dist/composition/edges/calibration.js.map +1 -1
  168. package/dist/composition/edges/catalog-condensed-matter.d.ts +10 -0
  169. package/dist/composition/edges/catalog-condensed-matter.d.ts.map +1 -1
  170. package/dist/composition/edges/catalog-condensed-matter.js +10 -10
  171. package/dist/composition/edges/catalog-condensed-matter.js.map +1 -1
  172. package/dist/composition/edges/catalog-gravitation-cosmology.d.ts +11 -0
  173. package/dist/composition/edges/catalog-gravitation-cosmology.d.ts.map +1 -1
  174. package/dist/composition/edges/catalog-gravitation-cosmology.js +11 -11
  175. package/dist/composition/edges/catalog-gravitation-cosmology.js.map +1 -1
  176. package/dist/composition/edges/catalog-quantum.d.ts +10 -0
  177. package/dist/composition/edges/catalog-quantum.d.ts.map +1 -1
  178. package/dist/composition/edges/catalog-quantum.js +10 -10
  179. package/dist/composition/edges/catalog-quantum.js.map +1 -1
  180. package/dist/composition/edges/catalog-tranche.d.ts +11 -0
  181. package/dist/composition/edges/catalog-tranche.d.ts.map +1 -1
  182. package/dist/composition/edges/catalog-tranche.js +11 -11
  183. package/dist/composition/edges/catalog-tranche.js.map +1 -1
  184. package/dist/composition/enumerate.d.ts +46 -5
  185. package/dist/composition/enumerate.d.ts.map +1 -1
  186. package/dist/composition/enumerate.js +37 -8
  187. package/dist/composition/enumerate.js.map +1 -1
  188. package/dist/composition/expr-simplify.d.ts.map +1 -1
  189. package/dist/composition/expr-simplify.js +3 -11
  190. package/dist/composition/expr-simplify.js.map +1 -1
  191. package/dist/composition/frontier-account.d.ts +6 -6
  192. package/dist/composition/frontier-account.d.ts.map +1 -1
  193. package/dist/composition/mathts-scalar-symbols.d.ts +48 -0
  194. package/dist/composition/mathts-scalar-symbols.d.ts.map +1 -0
  195. package/dist/composition/mathts-scalar-symbols.js +150 -0
  196. package/dist/composition/mathts-scalar-symbols.js.map +1 -0
  197. package/dist/composition/probe/frontier.d.ts.map +1 -1
  198. package/dist/composition/probe/frontier.js +17 -4
  199. package/dist/composition/probe/frontier.js.map +1 -1
  200. package/dist/composition/probe/study.js +1 -1
  201. package/dist/composition/probe/study.js.map +1 -1
  202. package/dist/composition/uncertainty.d.ts +2 -2
  203. package/dist/composition/uncertainty.d.ts.map +1 -1
  204. package/dist/composition/uncertainty.js +14 -13
  205. package/dist/composition/uncertainty.js.map +1 -1
  206. package/dist/diff/bridge-gradient.d.ts +5 -8
  207. package/dist/diff/bridge-gradient.d.ts.map +1 -1
  208. package/dist/diff/bridge-gradient.js +5 -8
  209. package/dist/diff/bridge-gradient.js.map +1 -1
  210. package/dist/dimensional/buckingham.d.ts.map +1 -1
  211. package/dist/dimensional/buckingham.js +19 -91
  212. package/dist/dimensional/buckingham.js.map +1 -1
  213. package/dist/dimensional/units.d.ts +13 -0
  214. package/dist/dimensional/units.d.ts.map +1 -1
  215. package/dist/dimensional/units.js +78 -1
  216. package/dist/dimensional/units.js.map +1 -1
  217. package/dist/index.d.ts +1 -1
  218. package/dist/index.d.ts.map +1 -1
  219. package/dist/index.js +4 -4
  220. package/dist/index.js.map +1 -1
  221. package/dist/numerical/binding-value.d.ts +3 -3
  222. package/dist/numerical/binding-value.d.ts.map +1 -1
  223. package/dist/numerical/binding-value.js +60 -31
  224. package/dist/numerical/binding-value.js.map +1 -1
  225. package/dist/numerical/einstein-equation.js +3 -3
  226. package/dist/numerical/einstein-equation.js.map +1 -1
  227. package/dist/numerical/engine-registry.d.ts +12 -27
  228. package/dist/numerical/engine-registry.d.ts.map +1 -1
  229. package/dist/numerical/engine-registry.js +10 -65
  230. package/dist/numerical/engine-registry.js.map +1 -1
  231. package/dist/numerical/errors.d.ts +1 -2
  232. package/dist/numerical/errors.d.ts.map +1 -1
  233. package/dist/numerical/errors.js +1 -2
  234. package/dist/numerical/errors.js.map +1 -1
  235. package/dist/numerical/formula-contract.d.ts +60 -0
  236. package/dist/numerical/formula-contract.d.ts.map +1 -0
  237. package/dist/numerical/formula-contract.js +107 -0
  238. package/dist/numerical/formula-contract.js.map +1 -0
  239. package/dist/numerical/formula-dimension.d.ts +33 -10
  240. package/dist/numerical/formula-dimension.d.ts.map +1 -1
  241. package/dist/numerical/formula-dimension.js +48 -35
  242. package/dist/numerical/formula-dimension.js.map +1 -1
  243. package/dist/numerical/formula-mathts.d.ts +15 -17
  244. package/dist/numerical/formula-mathts.d.ts.map +1 -1
  245. package/dist/numerical/formula-mathts.js +34 -36
  246. package/dist/numerical/formula-mathts.js.map +1 -1
  247. package/dist/numerical/formula-registry.d.ts +11 -19
  248. package/dist/numerical/formula-registry.d.ts.map +1 -1
  249. package/dist/numerical/formula-registry.js +16 -79
  250. package/dist/numerical/formula-registry.js.map +1 -1
  251. package/dist/numerical/geodesic-integrator.d.ts.map +1 -1
  252. package/dist/numerical/geodesic-integrator.js +19 -31
  253. package/dist/numerical/geodesic-integrator.js.map +1 -1
  254. package/dist/numerical/gl4-integrator.d.ts +12 -51
  255. package/dist/numerical/gl4-integrator.d.ts.map +1 -1
  256. package/dist/numerical/gl4-integrator.js +103 -565
  257. package/dist/numerical/gl4-integrator.js.map +1 -1
  258. package/dist/numerical/index.d.ts +0 -2
  259. package/dist/numerical/index.d.ts.map +1 -1
  260. package/dist/numerical/index.js +0 -2
  261. package/dist/numerical/index.js.map +1 -1
  262. package/dist/numerical/kretschmann.d.ts +2 -2
  263. package/dist/numerical/kretschmann.js +2 -2
  264. package/dist/numerical/mathts-engine.d.ts +7 -8
  265. package/dist/numerical/mathts-engine.d.ts.map +1 -1
  266. package/dist/numerical/mathts-engine.js +19 -27
  267. package/dist/numerical/mathts-engine.js.map +1 -1
  268. package/dist/numerical/null-ray-integrator.d.ts.map +1 -1
  269. package/dist/numerical/null-ray-integrator.js +11 -28
  270. package/dist/numerical/null-ray-integrator.js.map +1 -1
  271. package/dist/numerical/quadrature.d.ts +2 -17
  272. package/dist/numerical/quadrature.d.ts.map +1 -1
  273. package/dist/numerical/quadrature.js +9 -20
  274. package/dist/numerical/quadrature.js.map +1 -1
  275. package/dist/numerical/spacetime-metrics.d.ts +1 -0
  276. package/dist/numerical/spacetime-metrics.d.ts.map +1 -1
  277. package/dist/numerical/spacetime-metrics.js.map +1 -1
  278. package/dist/numerical/strides.d.ts +1 -2
  279. package/dist/numerical/strides.d.ts.map +1 -1
  280. package/dist/numerical/strides.js +1 -2
  281. package/dist/numerical/strides.js.map +1 -1
  282. package/dist/numerical/tensor-engine.d.ts +2 -3
  283. package/dist/numerical/tensor-engine.d.ts.map +1 -1
  284. package/dist/numerical/tensor-engine.js +1 -2
  285. package/dist/numerical/tensor-engine.js.map +1 -1
  286. package/dist/relations/category.d.ts +8 -2
  287. package/dist/relations/category.d.ts.map +1 -1
  288. package/dist/relations/category.js +10 -2
  289. package/dist/relations/category.js.map +1 -1
  290. package/dist/relations/composition-table.d.ts +1 -1
  291. package/dist/relations/composition-table.d.ts.map +1 -1
  292. package/dist/relations/composition-table.js +1 -1
  293. package/dist/relations/composition-table.js.map +1 -1
  294. package/dist/relations/regime-vocabularies.d.ts +40 -0
  295. package/dist/relations/regime-vocabularies.d.ts.map +1 -0
  296. package/dist/relations/regime-vocabularies.js +19 -0
  297. package/dist/relations/regime-vocabularies.js.map +1 -0
  298. package/package.json +13 -46
  299. package/dist/numerical/float64-engine.d.ts +0 -55
  300. package/dist/numerical/float64-engine.d.ts.map +0 -1
  301. package/dist/numerical/float64-engine.js +0 -675
  302. package/dist/numerical/float64-engine.js.map +0 -1
  303. package/dist/numerical/formula.d.ts +0 -106
  304. package/dist/numerical/formula.d.ts.map +0 -1
  305. package/dist/numerical/formula.js +0 -348
  306. package/dist/numerical/formula.js.map +0 -1
@@ -23,556 +23,107 @@
23
23
  *
24
24
  * @module numerical/gl4-integrator
25
25
  */
26
+ import { gaussLegendre4 } from '@danielsimonjr/mathts-functions';
26
27
  import { GL4ConvergenceError, NumericalBackendError } from './errors.js';
27
- const SQRT3_OVER_6 = Math.sqrt(3) / 6;
28
28
  /**
29
- * Gauss-Legendre 4th-order quadrature nodes c₁, c₂ — the two roots of the
30
- * shifted Legendre polynomial P₂(x) on [0,1].
29
+ * Gauss–Legendre order-4 Butcher tableau from MathTS. `GL4_A` is the 2×2
30
+ * stage matrix, `GL4_B` the weights (both ½), and `GL4_C` the two nodes.
31
+ * `integrateGeodesicGL4` does not read these names; the tableau tests do.
31
32
  *
32
- * c₁ = ½ − √3/6, c₂ = ½ + √3/6.
33
- *
34
- * @internal — exported for unit tests pinning the Butcher tableau invariants.
35
- * Public callers should use {@link integrateGeodesicGL4}, not the raw constants.
33
+ * @internal
36
34
  */
37
- export const GL4_C = [0.5 - SQRT3_OVER_6, 0.5 + SQRT3_OVER_6];
35
+ export { GL4_A, GL4_B, GL4_C } from '@danielsimonjr/mathts-functions';
38
36
  /**
39
- * GL4 Butcher matrix a_{ij} — the 2×2 collocation table for the implicit
40
- * stages:
41
- *
42
- * [ 1/4 1/4 − √3/6 ]
43
- * [ 1/4 + √3/6 1/4 ]
37
+ * Geodesic Hamiltonian derivative, packed as `y = [x..., p...]`.
44
38
  *
45
- * (Hairer/Lubich/Wanner §II.1, Table 1.1.)
46
- *
47
- * @internal — exported for unit tests pinning the Butcher tableau invariants.
48
- * Public callers should use {@link integrateGeodesicGL4}, not the raw constants.
49
- */
50
- export const GL4_A = [
51
- [0.25, 0.25 - SQRT3_OVER_6],
52
- [0.25 + SQRT3_OVER_6, 0.25],
53
- ];
54
- /**
55
- * GL4 stage weights — the 2-point Gauss-Legendre quadrature weights on
56
- * [0,1]: b₁ = b₂ = ½.
39
+ * dx^μ = g^{μν} p_ν
40
+ * dp_μ = −½ (∂_μ g^{νρ}) p_ν p_ρ
57
41
  *
58
- * @internal — exported for unit tests pinning the Butcher tableau invariants.
59
- * Public callers should use {@link integrateGeodesicGL4}, not the raw constants.
42
+ * `dg[μ*dim² + ν*dim + ρ] = ∂_μ g^{νρ}`. A coefficient that is exactly 0 is
43
+ * skipped, so `0 * NaN` does not poison a component the term does not enter.
60
44
  */
61
- export const GL4_B = [0.5, 0.5];
45
+ function geodesicDeriv(y, gInverseFn, dgInverseFn, xBuf) {
46
+ const dim = xBuf.length;
47
+ for (let i = 0; i < dim; i++)
48
+ xBuf[i] = y[i];
49
+ const coords = xBuf;
50
+ const gInv = gInverseFn(coords);
51
+ const dgInv = dgInverseFn(coords);
52
+ const out = new Array(dim * 2);
53
+ for (let mu = 0; mu < dim; mu++) {
54
+ let dx = 0;
55
+ let dp = 0;
56
+ const gRow = mu * dim;
57
+ const dgBase = gRow * dim;
58
+ for (let nu = 0; nu < dim; nu++) {
59
+ const pNu = y[dim + nu];
60
+ const g = gInv[gRow + nu];
61
+ // Skip an exact zero so `0 * NaN` does not poison a component this term does not enter.
62
+ if (g !== 0)
63
+ dx += g * pNu;
64
+ let pDot = 0;
65
+ const offset = dgBase + nu * dim;
66
+ for (let rho = 0; rho < dim; rho++) {
67
+ const dg = dgInv[offset + rho];
68
+ if (dg !== 0)
69
+ pDot += dg * y[dim + rho];
70
+ }
71
+ if (pDot !== 0)
72
+ dp += pDot * pNu;
73
+ }
74
+ out[mu] = dx;
75
+ out[dim + mu] = dp === 0 ? 0 : -0.5 * dp;
76
+ }
77
+ return out;
78
+ }
79
+ /** One GL4 step of the geodesic Hamiltonian, via MathTS `gaussLegendre4`. */
80
+ function advanceGl4(state, h, gInverseFn, dgInverseFn, opts) {
81
+ const dim = state.x.length;
82
+ const y0 = new Array(dim * 2);
83
+ for (let i = 0; i < dim; i++) {
84
+ y0[i] = state.x[i];
85
+ y0[dim + i] = state.p[i];
86
+ }
87
+ const xBuf = new Float64Array(dim);
88
+ let solved;
89
+ try {
90
+ solved = gaussLegendre4((_t, y) => geodesicDeriv(y, gInverseFn, dgInverseFn, xBuf), y0, [0, h], { steps: 1, picardTol: opts.picardTol, picardMaxIter: opts.picardMaxIter });
91
+ }
92
+ catch (err) {
93
+ if (err instanceof Error && /Picard iteration did not converge/i.test(err.message)) {
94
+ throw new GL4ConvergenceError(err.message);
95
+ }
96
+ throw err;
97
+ }
98
+ const y1 = solved.y[solved.y.length - 1];
99
+ const iterations = solved.iterations[0];
100
+ if (y1 === undefined || iterations === undefined) {
101
+ throw new GL4ConvergenceError(`Picard iteration did not converge in ${opts.picardMaxIter} iterations (maxDelta above picardTol=${opts.picardTol})`);
102
+ }
103
+ return { x: y1.slice(0, dim), p: y1.slice(dim), iterations };
104
+ }
62
105
  /**
63
- * Picard fixed-point solver for the GL4 implicit stage system.
64
- *
65
- * Per Design §3 Task 1a, the implicit system is:
66
- * X_i = x_n + h · Σ_j a_{ij} · g^{·ν}(X_j) P_{j,ν}
67
- * P_{i,μ} = p_n − h · Σ_j a_{ij} · ½ (∂_μ g^νρ)(X_j) P_{j,ν} P_{j,ρ}
68
- *
69
- * Data flow (F15 / M1): stage values (X_j, P_j) at iterate k feed forward
70
- * to update (X_i, P_i) at iterate k+1. This is Picard iteration (NOT
71
- * Newton) — no Jacobian assembly or LU decomposition. Convergence is
72
- * linear with contraction rate ≈ h·|∂f/∂x|. For Mercury (h ≈ 150 s),
73
- * expect 30–40 iterations at tol=1e-12. GL4's symplecticity is guaranteed
74
- * by the Butcher tableau, not by the inner solver's convergence speed
75
- * (Sanz-Serna 1988; Hairer/Lubich/Wanner §II.1).
76
- *
77
- * The `dgInverseFn` index order is `dg[λ][μ][ν] = ∂_λ g^{μν}` (Task 0 I2
78
- * pin, also recorded on `GL4Options.dgInverseFn`). When we evaluate
79
- * `dp_μ = −½ (∂_μ g^{νρ}) P_ν P_ρ` we therefore read
80
- * `dgInvAtXj[mu*dim²+nu*dim+rho]` — `mu` is the differentiation axis (λ in the
81
- * pinned order) and `(nu, rho)` are the upper metric indices.
106
+ * One GL4 step, reported in the stage-result shape the stage tests read.
82
107
  *
83
- * Throws `GL4ConvergenceError` with message matching
84
- * `/Picard iteration did not converge/` if `picardMaxIter` is exhausted.
108
+ * The step itself is MathTS `gaussLegendre4`. In flat space `p` is constant,
109
+ * so both stage momenta equal that advanced `p`. Throws `GL4ConvergenceError`
110
+ * with a message matching `/Picard iteration did not converge/` when the
111
+ * Picard cap is exhausted.
85
112
  *
86
113
  * @internal
87
114
  */
88
115
  export function solveGL4Stage(state, h, gInverseFn, dgInverseFn, opts) {
89
- const dim = state.x.length;
90
- // Pre-allocate ping-pong buffers (O-2): both X / P stage pairs as
91
- // reusable Float64Arrays. The original implementation allocated 4
92
- // arrays per Picard iteration (up to picardMaxIter = 50 iters per
93
- // RK4 step); now allocation is once per call and references are
94
- // swapped per iteration.
95
- const bufXA = [new Float64Array(dim), new Float64Array(dim)];
96
- const bufXB = [new Float64Array(dim), new Float64Array(dim)];
97
- const bufPA = [new Float64Array(dim), new Float64Array(dim)];
98
- const bufPB = [new Float64Array(dim), new Float64Array(dim)];
99
- // Initial guess: stage values = state values (k=0 of fixed-point iteration).
100
- bufXA[0].set(state.x);
101
- bufXA[1].set(state.x);
102
- bufPA[0].set(state.p);
103
- bufPA[1].set(state.p);
104
- let X = bufXA;
105
- let P = bufPA;
106
- let Xnew = bufXB;
107
- let Pnew = bufPB;
108
- // Pre-allocate arrays to hoist dxStage and dpStage out of the `i` loop
109
- const dxStageArr = [new Float64Array(dim), new Float64Array(dim)];
110
- const dpStageArr = [new Float64Array(dim), new Float64Array(dim)];
111
- const dxStageArr0 = dxStageArr[0];
112
- const dxStageArr1 = dxStageArr[1];
113
- const dpStageArr0 = dpStageArr[0];
114
- const dpStageArr1 = dpStageArr[1];
115
- const hA00 = h * GL4_A[0][0];
116
- const hA01 = h * GL4_A[0][1];
117
- const hA10 = h * GL4_A[1][0];
118
- const hA11 = h * GL4_A[1][1];
119
- const halfhA00 = 0.5 * hA00;
120
- const halfhA01 = 0.5 * hA01;
121
- const halfhA10 = 0.5 * hA10;
122
- const halfhA11 = 0.5 * hA11;
123
- for (let k = 0; k < opts.picardMaxIter; k++) {
124
- // 1. Evaluate metric closures only twice per Picard iteration
125
- const gInvAtX0 = gInverseFn(X[0]);
126
- const dgInvAtX0 = dgInverseFn(X[0]);
127
- const gInvAtX1 = gInverseFn(X[1]);
128
- const dgInvAtX1 = dgInverseFn(X[1]);
129
- // 2. Precompute dxStage and dpStage for all j and mu (only 8 combinations)
130
- const p0_st = P[0];
131
- const p1_st = P[1];
132
- if (dim === 4) {
133
- // Bolt: Pre-cache momentum elements into local scalars to eliminate array lookups.
134
- // This combined with manual loop unrolling completely eliminates loop overhead
135
- // for 4D spacetime integrations without losing `g !== 0` bailout sparseness benefits.
136
- const p00 = p0_st[0], p01 = p0_st[1], p02 = p0_st[2], p03 = p0_st[3];
137
- const p10 = p1_st[0], p11 = p1_st[1], p12 = p1_st[2], p13 = p1_st[3];
138
- for (let mu = 0; mu < 4; mu++) {
139
- let dx0 = 0, dp0 = 0, dx1 = 0, dp1 = 0;
140
- const mu4 = mu * 4;
141
- let g0, g1, dg0, dg1;
142
- let pDotTerm0, pDotTerm1;
143
- let offset = mu * 16;
144
- // nu = 0
145
- g0 = gInvAtX0[mu4];
146
- if (g0 !== 0)
147
- dx0 += g0 * p00;
148
- g1 = gInvAtX1[mu4];
149
- if (g1 !== 0)
150
- dx1 += g1 * p10;
151
- dg0 = dgInvAtX0[offset];
152
- dg1 = dgInvAtX1[offset];
153
- // Guarded like every other rho term, and like master. Assigning
154
- // unguarded here would compute 0 * p when dg is exactly 0, which is
155
- // NaN for a non-finite momentum -- manufacturing a divergence signal
156
- // out of a term that contributes nothing.
157
- pDotTerm0 = 0;
158
- if (dg0 !== 0) {
159
- pDotTerm0 = dg0 * p00;
160
- }
161
- pDotTerm1 = 0;
162
- if (dg1 !== 0) {
163
- pDotTerm1 = dg1 * p10;
164
- }
165
- dg0 = dgInvAtX0[offset + 1];
166
- dg1 = dgInvAtX1[offset + 1];
167
- if (dg0 !== 0) {
168
- pDotTerm0 += dg0 * p01;
169
- }
170
- if (dg1 !== 0) {
171
- pDotTerm1 += dg1 * p11;
172
- }
173
- dg0 = dgInvAtX0[offset + 2];
174
- dg1 = dgInvAtX1[offset + 2];
175
- if (dg0 !== 0) {
176
- pDotTerm0 += dg0 * p02;
177
- }
178
- if (dg1 !== 0) {
179
- pDotTerm1 += dg1 * p12;
180
- }
181
- dg0 = dgInvAtX0[offset + 3];
182
- dg1 = dgInvAtX1[offset + 3];
183
- if (dg0 !== 0) {
184
- pDotTerm0 += dg0 * p03;
185
- }
186
- if (dg1 !== 0) {
187
- pDotTerm1 += dg1 * p13;
188
- }
189
- if (pDotTerm0 !== 0) {
190
- dp0 += pDotTerm0 * p00;
191
- }
192
- if (pDotTerm1 !== 0) {
193
- dp1 += pDotTerm1 * p10;
194
- }
195
- // nu = 1
196
- offset += 4;
197
- g0 = gInvAtX0[mu4 + 1];
198
- if (g0 !== 0)
199
- dx0 += g0 * p01;
200
- g1 = gInvAtX1[mu4 + 1];
201
- if (g1 !== 0)
202
- dx1 += g1 * p11;
203
- dg0 = dgInvAtX0[offset];
204
- dg1 = dgInvAtX1[offset];
205
- // Guarded like every other rho term, and like master. Assigning
206
- // unguarded here would compute 0 * p when dg is exactly 0, which is
207
- // NaN for a non-finite momentum -- manufacturing a divergence signal
208
- // out of a term that contributes nothing.
209
- pDotTerm0 = 0;
210
- if (dg0 !== 0) {
211
- pDotTerm0 = dg0 * p00;
212
- }
213
- pDotTerm1 = 0;
214
- if (dg1 !== 0) {
215
- pDotTerm1 = dg1 * p10;
216
- }
217
- dg0 = dgInvAtX0[offset + 1];
218
- dg1 = dgInvAtX1[offset + 1];
219
- if (dg0 !== 0) {
220
- pDotTerm0 += dg0 * p01;
221
- }
222
- if (dg1 !== 0) {
223
- pDotTerm1 += dg1 * p11;
224
- }
225
- dg0 = dgInvAtX0[offset + 2];
226
- dg1 = dgInvAtX1[offset + 2];
227
- if (dg0 !== 0) {
228
- pDotTerm0 += dg0 * p02;
229
- }
230
- if (dg1 !== 0) {
231
- pDotTerm1 += dg1 * p12;
232
- }
233
- dg0 = dgInvAtX0[offset + 3];
234
- dg1 = dgInvAtX1[offset + 3];
235
- if (dg0 !== 0) {
236
- pDotTerm0 += dg0 * p03;
237
- }
238
- if (dg1 !== 0) {
239
- pDotTerm1 += dg1 * p13;
240
- }
241
- if (pDotTerm0 !== 0) {
242
- dp0 += pDotTerm0 * p01;
243
- }
244
- if (pDotTerm1 !== 0) {
245
- dp1 += pDotTerm1 * p11;
246
- }
247
- // nu = 2
248
- offset += 4;
249
- g0 = gInvAtX0[mu4 + 2];
250
- if (g0 !== 0)
251
- dx0 += g0 * p02;
252
- g1 = gInvAtX1[mu4 + 2];
253
- if (g1 !== 0)
254
- dx1 += g1 * p12;
255
- dg0 = dgInvAtX0[offset];
256
- dg1 = dgInvAtX1[offset];
257
- // Guarded like every other rho term, and like master. Assigning
258
- // unguarded here would compute 0 * p when dg is exactly 0, which is
259
- // NaN for a non-finite momentum -- manufacturing a divergence signal
260
- // out of a term that contributes nothing.
261
- pDotTerm0 = 0;
262
- if (dg0 !== 0) {
263
- pDotTerm0 = dg0 * p00;
264
- }
265
- pDotTerm1 = 0;
266
- if (dg1 !== 0) {
267
- pDotTerm1 = dg1 * p10;
268
- }
269
- dg0 = dgInvAtX0[offset + 1];
270
- dg1 = dgInvAtX1[offset + 1];
271
- if (dg0 !== 0) {
272
- pDotTerm0 += dg0 * p01;
273
- }
274
- if (dg1 !== 0) {
275
- pDotTerm1 += dg1 * p11;
276
- }
277
- dg0 = dgInvAtX0[offset + 2];
278
- dg1 = dgInvAtX1[offset + 2];
279
- if (dg0 !== 0) {
280
- pDotTerm0 += dg0 * p02;
281
- }
282
- if (dg1 !== 0) {
283
- pDotTerm1 += dg1 * p12;
284
- }
285
- dg0 = dgInvAtX0[offset + 3];
286
- dg1 = dgInvAtX1[offset + 3];
287
- if (dg0 !== 0) {
288
- pDotTerm0 += dg0 * p03;
289
- }
290
- if (dg1 !== 0) {
291
- pDotTerm1 += dg1 * p13;
292
- }
293
- if (pDotTerm0 !== 0) {
294
- dp0 += pDotTerm0 * p02;
295
- }
296
- if (pDotTerm1 !== 0) {
297
- dp1 += pDotTerm1 * p12;
298
- }
299
- // nu = 3
300
- offset += 4;
301
- g0 = gInvAtX0[mu4 + 3];
302
- if (g0 !== 0)
303
- dx0 += g0 * p03;
304
- g1 = gInvAtX1[mu4 + 3];
305
- if (g1 !== 0)
306
- dx1 += g1 * p13;
307
- dg0 = dgInvAtX0[offset];
308
- dg1 = dgInvAtX1[offset];
309
- // Guarded like every other rho term, and like master. Assigning
310
- // unguarded here would compute 0 * p when dg is exactly 0, which is
311
- // NaN for a non-finite momentum -- manufacturing a divergence signal
312
- // out of a term that contributes nothing.
313
- pDotTerm0 = 0;
314
- if (dg0 !== 0) {
315
- pDotTerm0 = dg0 * p00;
316
- }
317
- pDotTerm1 = 0;
318
- if (dg1 !== 0) {
319
- pDotTerm1 = dg1 * p10;
320
- }
321
- dg0 = dgInvAtX0[offset + 1];
322
- dg1 = dgInvAtX1[offset + 1];
323
- if (dg0 !== 0) {
324
- pDotTerm0 += dg0 * p01;
325
- }
326
- if (dg1 !== 0) {
327
- pDotTerm1 += dg1 * p11;
328
- }
329
- dg0 = dgInvAtX0[offset + 2];
330
- dg1 = dgInvAtX1[offset + 2];
331
- if (dg0 !== 0) {
332
- pDotTerm0 += dg0 * p02;
333
- }
334
- if (dg1 !== 0) {
335
- pDotTerm1 += dg1 * p12;
336
- }
337
- dg0 = dgInvAtX0[offset + 3];
338
- dg1 = dgInvAtX1[offset + 3];
339
- if (dg0 !== 0) {
340
- pDotTerm0 += dg0 * p03;
341
- }
342
- if (dg1 !== 0) {
343
- pDotTerm1 += dg1 * p13;
344
- }
345
- if (pDotTerm0 !== 0) {
346
- dp0 += pDotTerm0 * p03;
347
- }
348
- if (pDotTerm1 !== 0) {
349
- dp1 += pDotTerm1 * p13;
350
- }
351
- dxStageArr0[mu] = dx0;
352
- dpStageArr0[mu] = dp0;
353
- dxStageArr1[mu] = dx1;
354
- dpStageArr1[mu] = dp1;
355
- }
356
- }
357
- else {
358
- for (let mu = 0; mu < dim; mu++) {
359
- let dx0 = 0, dp0 = 0;
360
- let dx1 = 0, dp1 = 0;
361
- const mu_dim = mu * dim;
362
- const mu_dim_dim = mu_dim * dim;
363
- for (let nu = 0; nu < dim; nu++) {
364
- const idx_g = mu_dim + nu;
365
- // Stage 0
366
- const g0 = gInvAtX0[idx_g];
367
- if (g0 !== 0) {
368
- dx0 += g0 * p0_st[nu];
369
- }
370
- // Stage 1
371
- const g1 = gInvAtX1[idx_g];
372
- if (g1 !== 0) {
373
- dx1 += g1 * p1_st[nu];
374
- }
375
- let pDotTerm0 = 0;
376
- let pDotTerm1 = 0;
377
- const offset = mu_dim_dim + nu * dim;
378
- for (let rho = 0; rho < dim; rho++) {
379
- const dgIdx = offset + rho;
380
- const dg0 = dgInvAtX0[dgIdx];
381
- if (dg0 !== 0) {
382
- pDotTerm0 += dg0 * p0_st[rho];
383
- }
384
- const dg1 = dgInvAtX1[dgIdx];
385
- if (dg1 !== 0) {
386
- pDotTerm1 += dg1 * p1_st[rho];
387
- }
388
- }
389
- if (pDotTerm0 !== 0) {
390
- dp0 += pDotTerm0 * p0_st[nu];
391
- }
392
- if (pDotTerm1 !== 0) {
393
- dp1 += pDotTerm1 * p1_st[nu];
394
- }
395
- }
396
- dxStageArr0[mu] = dx0;
397
- dpStageArr0[mu] = dp0;
398
- dxStageArr1[mu] = dx1;
399
- dpStageArr1[mu] = dp1;
400
- }
401
- }
402
- // 3. Accumulate for i and mu
403
- if (dim === 4) {
404
- // Bolt: Manual loop unrolling for dim=4 to avoid loop iteration overhead and Math method calls
405
- const x0_st = state.x[0], x1_st = state.x[1], x2_st = state.x[2], x3_st = state.x[3];
406
- const p0_st = state.p[0], p1_st = state.p[1], p2_st = state.p[2], p3_st = state.p[3];
407
- const dx0_0 = dxStageArr0[0], dx0_1 = dxStageArr0[1], dx0_2 = dxStageArr0[2], dx0_3 = dxStageArr0[3];
408
- const dx1_0 = dxStageArr1[0], dx1_1 = dxStageArr1[1], dx1_2 = dxStageArr1[2], dx1_3 = dxStageArr1[3];
409
- const dp0_0 = dpStageArr0[0], dp0_1 = dpStageArr0[1], dp0_2 = dpStageArr0[2], dp0_3 = dpStageArr0[3];
410
- const dp1_0 = dpStageArr1[0], dp1_1 = dpStageArr1[1], dp1_2 = dpStageArr1[2], dp1_3 = dpStageArr1[3];
411
- Xnew[0][0] = x0_st + hA00 * dx0_0 + hA01 * dx1_0;
412
- Pnew[0][0] = p0_st - halfhA00 * dp0_0 - halfhA01 * dp1_0;
413
- Xnew[0][1] = x1_st + hA00 * dx0_1 + hA01 * dx1_1;
414
- Pnew[0][1] = p1_st - halfhA00 * dp0_1 - halfhA01 * dp1_1;
415
- Xnew[0][2] = x2_st + hA00 * dx0_2 + hA01 * dx1_2;
416
- Pnew[0][2] = p2_st - halfhA00 * dp0_2 - halfhA01 * dp1_2;
417
- Xnew[0][3] = x3_st + hA00 * dx0_3 + hA01 * dx1_3;
418
- Pnew[0][3] = p3_st - halfhA00 * dp0_3 - halfhA01 * dp1_3;
419
- Xnew[1][0] = x0_st + hA10 * dx0_0 + hA11 * dx1_0;
420
- Pnew[1][0] = p0_st - halfhA10 * dp0_0 - halfhA11 * dp1_0;
421
- Xnew[1][1] = x1_st + hA10 * dx0_1 + hA11 * dx1_1;
422
- Pnew[1][1] = p1_st - halfhA10 * dp0_1 - halfhA11 * dp1_1;
423
- Xnew[1][2] = x2_st + hA10 * dx0_2 + hA11 * dx1_2;
424
- Pnew[1][2] = p2_st - halfhA10 * dp0_2 - halfhA11 * dp1_2;
425
- Xnew[1][3] = x3_st + hA10 * dx0_3 + hA11 * dx1_3;
426
- Pnew[1][3] = p3_st - halfhA10 * dp0_3 - halfhA11 * dp1_3;
427
- }
428
- else {
429
- for (let mu = 0; mu < dim; mu++) {
430
- const x0 = state.x[mu];
431
- const p0 = state.p[mu];
432
- const dx0 = dxStageArr0[mu];
433
- const dx1 = dxStageArr1[mu];
434
- const dp0 = dpStageArr0[mu];
435
- const dp1 = dpStageArr1[mu];
436
- Xnew[0][mu] = x0 + hA00 * dx0 + hA01 * dx1;
437
- Pnew[0][mu] = p0 - halfhA00 * dp0 - halfhA01 * dp1;
438
- Xnew[1][mu] = x0 + hA10 * dx0 + hA11 * dx1;
439
- Pnew[1][mu] = p0 - halfhA10 * dp0 - halfhA11 * dp1;
440
- }
441
- }
442
- // Convergence check: max |δX, δP|
443
- let maxDelta = 0;
444
- if (dim === 4) {
445
- let d = Xnew[0][0] - X[0][0];
446
- let absD = d < 0 ? -d : d;
447
- if (absD > maxDelta)
448
- maxDelta = absD;
449
- d = Xnew[0][1] - X[0][1];
450
- absD = d < 0 ? -d : d;
451
- if (absD > maxDelta)
452
- maxDelta = absD;
453
- d = Xnew[0][2] - X[0][2];
454
- absD = d < 0 ? -d : d;
455
- if (absD > maxDelta)
456
- maxDelta = absD;
457
- d = Xnew[0][3] - X[0][3];
458
- absD = d < 0 ? -d : d;
459
- if (absD > maxDelta)
460
- maxDelta = absD;
461
- d = Pnew[0][0] - P[0][0];
462
- absD = d < 0 ? -d : d;
463
- if (absD > maxDelta)
464
- maxDelta = absD;
465
- d = Pnew[0][1] - P[0][1];
466
- absD = d < 0 ? -d : d;
467
- if (absD > maxDelta)
468
- maxDelta = absD;
469
- d = Pnew[0][2] - P[0][2];
470
- absD = d < 0 ? -d : d;
471
- if (absD > maxDelta)
472
- maxDelta = absD;
473
- d = Pnew[0][3] - P[0][3];
474
- absD = d < 0 ? -d : d;
475
- if (absD > maxDelta)
476
- maxDelta = absD;
477
- d = Xnew[1][0] - X[1][0];
478
- absD = d < 0 ? -d : d;
479
- if (absD > maxDelta)
480
- maxDelta = absD;
481
- d = Xnew[1][1] - X[1][1];
482
- absD = d < 0 ? -d : d;
483
- if (absD > maxDelta)
484
- maxDelta = absD;
485
- d = Xnew[1][2] - X[1][2];
486
- absD = d < 0 ? -d : d;
487
- if (absD > maxDelta)
488
- maxDelta = absD;
489
- d = Xnew[1][3] - X[1][3];
490
- absD = d < 0 ? -d : d;
491
- if (absD > maxDelta)
492
- maxDelta = absD;
493
- d = Pnew[1][0] - P[1][0];
494
- absD = d < 0 ? -d : d;
495
- if (absD > maxDelta)
496
- maxDelta = absD;
497
- d = Pnew[1][1] - P[1][1];
498
- absD = d < 0 ? -d : d;
499
- if (absD > maxDelta)
500
- maxDelta = absD;
501
- d = Pnew[1][2] - P[1][2];
502
- absD = d < 0 ? -d : d;
503
- if (absD > maxDelta)
504
- maxDelta = absD;
505
- d = Pnew[1][3] - P[1][3];
506
- absD = d < 0 ? -d : d;
507
- if (absD > maxDelta)
508
- maxDelta = absD;
509
- }
510
- else {
511
- for (let i = 0; i < 2; i++) {
512
- for (let mu = 0; mu < dim; mu++) {
513
- let d = Xnew[i][mu] - X[i][mu];
514
- let absD = d < 0 ? -d : d;
515
- if (absD > maxDelta)
516
- maxDelta = absD;
517
- d = Pnew[i][mu] - P[i][mu];
518
- absD = d < 0 ? -d : d;
519
- if (absD > maxDelta)
520
- maxDelta = absD;
521
- }
522
- }
523
- }
524
- // Ping-pong swap: read-from + write-to buffers exchange roles for next iter.
525
- // Bolt: Use explicit temporary variables instead of array destructuring to prevent GC overhead
526
- const tmpX = X;
527
- X = Xnew;
528
- Xnew = tmpX;
529
- const tmpP = P;
530
- P = Pnew;
531
- Pnew = tmpP;
532
- if (maxDelta < opts.picardTol) {
533
- // Clone on return — caller may retain references and the next
534
- // solveGL4Stage call will overwrite our internal buffers.
535
- if (dim === 4) {
536
- // Bolt: Array literals are significantly faster than allocating arrays and manually populating them in tight loops.
537
- return {
538
- stageX: [[X[0][0], X[0][1], X[0][2], X[0][3]], [X[1][0], X[1][1], X[1][2], X[1][3]]],
539
- stageP: [[P[0][0], P[0][1], P[0][2], P[0][3]], [P[1][0], P[1][1], P[1][2], P[1][3]]],
540
- stageDx: [[dxStageArr0[0], dxStageArr0[1], dxStageArr0[2], dxStageArr0[3]], [dxStageArr1[0], dxStageArr1[1], dxStageArr1[2], dxStageArr1[3]]],
541
- stageDp: [[dpStageArr0[0], dpStageArr0[1], dpStageArr0[2], dpStageArr0[3]], [dpStageArr1[0], dpStageArr1[1], dpStageArr1[2], dpStageArr1[3]]],
542
- iterations: k + 1,
543
- };
544
- }
545
- else {
546
- // Bolt: Manual loop is significantly faster than Array.from for TypedArrays in tight loops.
547
- const sX0 = new Array(dim);
548
- const sX1 = new Array(dim);
549
- const sP0 = new Array(dim);
550
- const sP1 = new Array(dim);
551
- const sDx0 = new Array(dim);
552
- const sDx1 = new Array(dim);
553
- const sDp0 = new Array(dim);
554
- const sDp1 = new Array(dim);
555
- for (let m = 0; m < dim; m++) {
556
- sX0[m] = X[0][m];
557
- sX1[m] = X[1][m];
558
- sP0[m] = P[0][m];
559
- sP1[m] = P[1][m];
560
- sDx0[m] = dxStageArr0[m];
561
- sDx1[m] = dxStageArr1[m];
562
- sDp0[m] = dpStageArr0[m];
563
- sDp1[m] = dpStageArr1[m];
564
- }
565
- return {
566
- stageX: [sX0, sX1],
567
- stageP: [sP0, sP1],
568
- stageDx: [sDx0, sDx1],
569
- stageDp: [sDp0, sDp1],
570
- iterations: k + 1,
571
- };
572
- }
573
- }
574
- }
575
- throw new GL4ConvergenceError(`Picard iteration did not converge in ${opts.picardMaxIter} iterations (maxDelta above picardTol=${opts.picardTol})`);
116
+ const step = advanceGl4(state, h, gInverseFn, dgInverseFn, opts);
117
+ const dim = step.p.length;
118
+ const zeros = new Array(dim).fill(0);
119
+ // In flat space p is constant, so both stage momenta equal the advanced p.
120
+ return {
121
+ stageX: [step.x.slice(), step.x.slice()],
122
+ stageP: [step.p.slice(), step.p.slice()],
123
+ stageDx: [zeros, zeros.slice()],
124
+ stageDp: [zeros.slice(), zeros.slice()],
125
+ iterations: step.iterations,
126
+ };
576
127
  }
577
128
  // ---------------------------------------------------------------------------
578
129
  // Integrator entry-point (Task 3, Phase 1a-iii)
@@ -582,7 +133,8 @@ export function solveGL4Stage(state, h, gInverseFn, dgInverseFn, opts) {
582
133
  *
583
134
  * H(x, p) = ½ g^{μν}(x) p_μ p_ν
584
135
  *
585
- * Drives the implicit Picard stage solver (`solveGL4Stage`) for each step,
136
+ * Each accepted step is MathTS `gaussLegendre4` on the geodesic Hamiltonian.
137
+ * Adaptive step-halving stays here. `onStep` reads that step's `iterations`.
586
138
  * with **adaptive step-halving on Picard non-convergence** (Adam+Eve I4,
587
139
  * replaces the single-retry R8): if Picard fails at step size h, retry at
588
140
  * h/2, h/4, … down to `hMin` (default `h · 1e-9`); throw
@@ -665,12 +217,12 @@ export function integrateGeodesicGL4(initialState, options) {
665
217
  const remainEps = h * 1e-12;
666
218
  while (remaining > remainEps) {
667
219
  const trialH = Math.min(stepH, remaining);
668
- let stages;
220
+ let advanced;
669
221
  let stepSucceeded = false;
670
222
  let subH = trialH;
671
223
  while (subH >= hFloor) {
672
224
  try {
673
- stages = solveGL4Stage({ x, p }, subH, gInverseFn, dgInverseFn, {
225
+ advanced = advanceGl4({ x, p }, subH, gInverseFn, dgInverseFn, {
674
226
  picardTol,
675
227
  picardMaxIter,
676
228
  });
@@ -682,37 +234,23 @@ export function integrateGeodesicGL4(initialState, options) {
682
234
  halvings++;
683
235
  }
684
236
  }
685
- if (!stepSucceeded || stages === undefined) {
237
+ if (!stepSucceeded || advanced === undefined) {
686
238
  throw new GL4ConvergenceError(`GL4 integrator: Picard iteration did not converge even at h_min=${hFloor} (step ${n}). Diagnose step-size or metric singularity.`);
687
239
  }
688
- // Bolt: Update state using double-buffering (pointer swapping) instead of slicing `newX` and `newP`
689
- // on every step-halving attempt. The converged stages are mapped onto our persistent buffer,
690
- // and we swap pointers once successful. This eliminates allocating arrays in the hot loop completely,
691
- // avoiding massive GC pauses while still preventing accumulative corruption.
692
- const b0 = GL4_B[0];
693
- const b1 = GL4_B[1];
694
- const dx0 = stages.stageDx[0];
695
- const dx1 = stages.stageDx[1];
696
- const dp0 = stages.stageDp[0];
697
- const dp1 = stages.stageDp[1];
698
- const hb0 = subH * b0;
699
- const hb1 = subH * b1;
700
- const mhb0 = hb0 * -0.5;
701
- const mhb1 = hb1 * -0.5;
702
240
  if (stateDim === 4) {
703
- newX[0] = x[0] + hb0 * dx0[0] + hb1 * dx1[0];
704
- newP[0] = p[0] + mhb0 * dp0[0] + mhb1 * dp1[0];
705
- newX[1] = x[1] + hb0 * dx0[1] + hb1 * dx1[1];
706
- newP[1] = p[1] + mhb0 * dp0[1] + mhb1 * dp1[1];
707
- newX[2] = x[2] + hb0 * dx0[2] + hb1 * dx1[2];
708
- newP[2] = p[2] + mhb0 * dp0[2] + mhb1 * dp1[2];
709
- newX[3] = x[3] + hb0 * dx0[3] + hb1 * dx1[3];
710
- newP[3] = p[3] + mhb0 * dp0[3] + mhb1 * dp1[3];
241
+ newX[0] = advanced.x[0];
242
+ newP[0] = advanced.p[0];
243
+ newX[1] = advanced.x[1];
244
+ newP[1] = advanced.p[1];
245
+ newX[2] = advanced.x[2];
246
+ newP[2] = advanced.p[2];
247
+ newX[3] = advanced.x[3];
248
+ newP[3] = advanced.p[3];
711
249
  }
712
250
  else {
713
251
  for (let mu = 0; mu < stateDim; mu++) {
714
- newX[mu] = x[mu] + hb0 * dx0[mu] + hb1 * dx1[mu];
715
- newP[mu] = p[mu] + mhb0 * dp0[mu] + mhb1 * dp1[mu];
252
+ newX[mu] = advanced.x[mu];
253
+ newP[mu] = advanced.p[mu];
716
254
  }
717
255
  }
718
256
  const tmpX = x;
@@ -723,7 +261,7 @@ export function integrateGeodesicGL4(initialState, options) {
723
261
  newP = tmpP;
724
262
  remaining -= subH;
725
263
  stepH = subH; // keep the converging size for the rest of this macro-step
726
- lastIterations = stages.iterations;
264
+ lastIterations = advanced.iterations;
727
265
  }
728
266
  snapshots.push({ tau: (n + 1) * h, x: x.slice(), p: p.slice() });
729
267
  if (onStep !== undefined) {